korganizer

koagendaview.cpp
1/*
2 This file is part of KOrganizer.
3 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
4 Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
20 As a special exception, permission is given to link this program
21 with any edition of TQt, and distribute the resulting executable,
22 without including the source code for TQt in the source distribution.
23*/
24
25#include <tqhbox.h>
26#include <tqvbox.h>
27#include <tqlabel.h>
28#include <tqframe.h>
29#include <tqlayout.h>
30#ifndef KORG_NOSPLITTER
31#include <tqsplitter.h>
32#endif
33#include <tqfont.h>
34#include <tqfontmetrics.h>
35#include <tqpopupmenu.h>
36#include <tqtooltip.h>
37#include <tqpainter.h>
38#include <tqpushbutton.h>
39#include <tqcursor.h>
40#include <tqbitarray.h>
41
42#include <tdeapplication.h>
43#include <kdebug.h>
44#include <tdestandarddirs.h>
45#include <kiconloader.h>
46#include <tdelocale.h>
47#include <tdeconfig.h>
48#include <tdeglobal.h>
49#include <tdeglobalsettings.h>
50#include <kholidays.h>
51
52#include <libkcal/calendar.h>
53#include <libkcal/icaldrag.h>
54#include <libkcal/dndfactory.h>
55#include <libkcal/calfilter.h>
56
57#include <kcalendarsystem.h>
58
59#include "koglobals.h"
60#ifndef KORG_NOPLUGINS
61#include "kocore.h"
62#endif
63#include "koprefs.h"
64#include "koagenda.h"
65#include "koagendaitem.h"
66#include "timelabels.h"
67
68#include "koincidencetooltip.h"
69#include "kogroupware.h"
70#include "kodialogmanager.h"
71#include "koeventpopupmenu.h"
72
73#include "koagendaview.h"
74#include "koagendaview.moc"
75
76using namespace KOrg;
77
78
79EventIndicator::EventIndicator(Location loc,TQWidget *parent,const char *name)
80 : TQFrame(parent,name)
81{
82 mColumns = 1;
83 mEnabled.resize( mColumns );
84 mLocation = loc;
85
86 if (mLocation == Top) mPixmap = KOGlobals::self()->smallIcon("upindicator");
87 else mPixmap = KOGlobals::self()->smallIcon("downindicator");
88
89 setMinimumHeight(mPixmap.height());
90}
91
92EventIndicator::~EventIndicator()
93{
94}
95
96void EventIndicator::drawContents(TQPainter *p)
97{
98// kdDebug(5850) << "======== top: " << contentsRect().top() << " bottom "
99// << contentsRect().bottom() << " left " << contentsRect().left()
100// << " right " << contentsRect().right() << endl;
101
102 int i;
103 for(i=0;i<mColumns;++i) {
104 if (mEnabled[i]) {
105 int cellWidth = contentsRect().right()/mColumns;
106 int xOffset = KOGlobals::self()->reverseLayout() ?
107 (mColumns - 1 - i)*cellWidth + cellWidth/2 -mPixmap.width()/2 :
108 i*cellWidth + cellWidth/2 -mPixmap.width()/2;
109 p->drawPixmap(TQPoint(xOffset,0),mPixmap);
110 }
111 }
112}
113
114void EventIndicator::changeColumns(int columns)
115{
116 mColumns = columns;
117 mEnabled.resize(mColumns);
118
119 update();
120}
121
122void EventIndicator::enableColumn(int column, bool enable)
123{
124 mEnabled[column] = enable;
125}
126
127
128#include <libkcal/incidence.h>
129
133
134
135KOAlternateLabel::KOAlternateLabel(const TQString &shortlabel, const TQString &longlabel,
136 const TQString &extensivelabel, TQWidget *parent, const char *name )
137 : TQLabel(parent, name), mTextTypeFixed(false), mShortText(shortlabel),
138 mLongText(longlabel), mExtensiveText(extensivelabel)
139{
140 setSizePolicy(TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed ));
141 if (mExtensiveText.isEmpty()) mExtensiveText = mLongText;
142 squeezeTextToLabel();
143}
144
145KOAlternateLabel::~KOAlternateLabel()
146{
147}
148
149void KOAlternateLabel::useShortText()
150{
151 mTextTypeFixed = true;
152 TQLabel::setText( mShortText );
153 TQToolTip::remove( this );
154 TQToolTip::add( this, mExtensiveText );
155 update(); // for kolab/issue4350
156}
157
158void KOAlternateLabel::useLongText()
159{
160 mTextTypeFixed = true;
161 TQLabel::setText( mLongText );
162 TQToolTip::remove( this );
163 TQToolTip::add( this, mExtensiveText );
164 update(); // for kolab/issue4350
165}
166
167void KOAlternateLabel::useExtensiveText()
168{
169 mTextTypeFixed = true;
170 TQLabel::setText( mExtensiveText );
171 TQToolTip::remove( this );
172 TQToolTip::add( this, "" );
173 update(); // for kolab/issue4350
174}
175
176void KOAlternateLabel::useDefaultText()
177{
178 mTextTypeFixed = false;
179 squeezeTextToLabel();
180}
181
182KOAlternateLabel::TextType KOAlternateLabel::largestFittingTextType() const
183{
184 TQFontMetrics fm( fontMetrics() );
185 const int labelWidth = size().width();
186 const int longTextWidth = fm.width( mLongText );
187 const int extensiveTextWidth = fm.width( mExtensiveText );
188 if ( extensiveTextWidth <= labelWidth )
189 return Extensive;
190 else if ( longTextWidth <= labelWidth )
191 return Long;
192 else
193 return Short;
194}
195
196void KOAlternateLabel::setFixedType( TextType type )
197{
198 switch ( type )
199 {
200 case Extensive: useExtensiveText(); break;
201 case Long: useLongText(); break;
202 case Short: useShortText(); break;
203 }
204}
205
206void KOAlternateLabel::squeezeTextToLabel()
207{
208 if ( mTextTypeFixed )
209 return;
210
211 const TextType type = largestFittingTextType();
212 switch ( type )
213 {
214 case Extensive:
215 TQLabel::setText( mExtensiveText );
216 TQToolTip::remove( this );
217 TQToolTip::add( this, "" );
218 break;
219 case Long:
220 TQLabel::setText( mLongText );
221 TQToolTip::remove( this );
222 TQToolTip::add( this, mExtensiveText );
223 break;
224 case Short:
225 TQLabel::setText( mShortText );
226 TQToolTip::remove( this );
227 TQToolTip::add( this, mExtensiveText );
228 break;
229 }
230 update(); // for kolab/issue4350
231}
232
233void KOAlternateLabel::resizeEvent( TQResizeEvent * )
234{
235 squeezeTextToLabel();
236}
237
238TQSize KOAlternateLabel::minimumSizeHint() const
239{
240 TQSize sh = TQLabel::minimumSizeHint();
241 sh.setWidth(-1);
242 return sh;
243}
244
248
249KOAgendaView::KOAgendaView( Calendar *cal,
250 CalendarView *calendarView,
251 TQWidget *parent,
252 const char *name,
253 bool isSideBySide ) :
254 KOrg::AgendaView (cal, parent,name), mExpandButton( 0 ),
255 mAllowAgendaUpdate( true ),
256 mUpdateItem( 0 ),
257 mIsSideBySide( isSideBySide ),
258 mPendingChanges( true ),
259 mAreDatesInitialized( false )
260{
261 mSelectedDates.append(TQDate::currentDate());
262
263 mLayoutDayLabels = 0;
264 mDayLabelsFrame = 0;
265 mDayLabels = 0;
266
267 bool isRTL = KOGlobals::self()->reverseLayout();
268
269 if ( KOPrefs::instance()->compactDialogs() ) {
270 if ( KOPrefs::instance()->mVerticalScreen ) {
271 mExpandedPixmap = KOGlobals::self()->smallIcon( "1downarrow" );
272 mNotExpandedPixmap = KOGlobals::self()->smallIcon( "1uparrow" );
273 } else {
274 mExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1leftarrow" : "1rightarrow" );
275 mNotExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1rightarrow" : "1leftarrow" );
276 }
277 }
278
279 TQBoxLayout *topLayout = new TQVBoxLayout(this);
280
281 // Create day name labels for agenda columns
282 mDayLabelsFrame = new TQHBox(this);
283 topLayout->addWidget(mDayLabelsFrame);
284
285 // Create agenda splitter
286#ifndef KORG_NOSPLITTER
287 mSplitterAgenda = new TQSplitter(TQt::Vertical,this);
288 topLayout->addWidget(mSplitterAgenda);
289
290 mSplitterAgenda->setOpaqueResize( TDEGlobalSettings::opaqueResize() );
291
292 mAllDayFrame = new TQHBox(mSplitterAgenda);
293
294 TQWidget *agendaFrame = new TQWidget(mSplitterAgenda);
295#else
296 TQVBox *mainBox = new TQVBox( this );
297 topLayout->addWidget( mainBox );
298
299 mAllDayFrame = new TQHBox(mainBox);
300
301 TQWidget *agendaFrame = new TQWidget(mainBox);
302#endif
303
304 // Create all-day agenda widget
305 mDummyAllDayLeft = new TQVBox( mAllDayFrame );
306 if ( isSideBySide )
307 mDummyAllDayLeft->hide();
308
309 if ( KOPrefs::instance()->compactDialogs() ) {
310 mExpandButton = new TQPushButton(mDummyAllDayLeft);
311 mExpandButton->setPixmap( mNotExpandedPixmap );
312 mExpandButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed,
313 TQSizePolicy::Fixed ) );
314 connect( mExpandButton, TQ_SIGNAL( clicked() ), TQ_SIGNAL( toggleExpand() ) );
315 } else {
316 TQLabel *label = new TQLabel( i18n("All Day"), mDummyAllDayLeft );
317 label->setAlignment( TQt::AlignRight | TQt::AlignVCenter | TQt::WordBreak );
318 }
319
320 mAllDayAgenda = new KOAgenda( 1, calendarView, mAllDayFrame );
321 mAllDayAgenda->setCalendar( calendar() );
322 TQWidget *dummyAllDayRight = new TQWidget(mAllDayFrame);
323
324 // Create agenda frame
325 TQGridLayout *agendaLayout = new TQGridLayout(agendaFrame,3,3);
326// TQHBox *agendaFrame = new TQHBox(splitterAgenda);
327
328 // create event indicator bars
329 mEventIndicatorTop = new EventIndicator(EventIndicator::Top,agendaFrame);
330 agendaLayout->addWidget(mEventIndicatorTop,0,1);
331 mEventIndicatorBottom = new EventIndicator(EventIndicator::Bottom,
332 agendaFrame);
333 agendaLayout->addWidget(mEventIndicatorBottom,2,1);
334 TQWidget *dummyAgendaRight = new TQWidget(agendaFrame);
335 agendaLayout->addWidget(dummyAgendaRight,0,2);
336
337 // Create time labels
338 mTimeLabels = new TimeLabels(24,agendaFrame);
339 agendaLayout->addWidget(mTimeLabels,1,0);
340
341 // Create agenda
342 mAgenda = new KOAgenda( 1, 96, KOPrefs::instance()->mHourSize, calendarView, agendaFrame );
343 mAgenda->setCalendar( calendar() );
344 agendaLayout->addMultiCellWidget(mAgenda,1,1,1,2);
345 agendaLayout->setColStretch(1,1);
346
347 // Create event context menu for agenda
348 mAgendaPopup = eventPopup();
349
350 // Create event context menu for all day agenda
351 mAllDayAgendaPopup = eventPopup();
352
353 // make connections between dependent widgets
354 mTimeLabels->setAgenda(mAgenda);
355 if ( isSideBySide )
356 mTimeLabels->hide();
357
358 // Update widgets to reflect user preferences
359// updateConfig();
360
361 createDayLabels( true );
362
363 if ( !isSideBySide ) {
364 // these blank widgets make the All Day Event box line up with the agenda
365 dummyAllDayRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
366 dummyAgendaRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
367 }
368
369 updateTimeBarWidth();
370
371 // Scrolling
372 connect(mAgenda->verticalScrollBar(),TQ_SIGNAL(valueChanged(int)),
373 mTimeLabels, TQ_SLOT(positionChanged()));
374
375 connect( mAgenda,
376 TQ_SIGNAL( zoomView( const int, const TQPoint & ,const TQt::Orientation ) ),
377 TQ_SLOT( zoomView( const int, const TQPoint &, const TQt::Orientation ) ) );
378
379 connect(mTimeLabels->verticalScrollBar(),TQ_SIGNAL(valueChanged(int)),
380 TQ_SLOT(setContentsPos(int)));
381
382 // Create Events, depends on type of agenda
383 connect( mAgenda, TQ_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)),
384 TQ_SLOT(newTimeSpanSelected(const TQPoint &, const TQPoint &)));
385 connect( mAllDayAgenda, TQ_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)),
386 TQ_SLOT(newTimeSpanSelectedAllDay(const TQPoint &, const TQPoint &)));
387
388 // event indicator update
389 connect( mAgenda, TQ_SIGNAL(lowerYChanged(int)),
390 TQ_SLOT(updateEventIndicatorTop(int)));
391 connect( mAgenda, TQ_SIGNAL(upperYChanged(int)),
392 TQ_SLOT(updateEventIndicatorBottom(int)));
393
394 if ( !readOnly() ) {
395 connectAgenda( mAgenda, mAgendaPopup, mAllDayAgenda );
396 connectAgenda( mAllDayAgenda, mAllDayAgendaPopup, mAgenda);
397 }
398
399 if ( cal ) {
400 cal->registerObserver( this );
401 }
402}
403
404
405KOAgendaView::~KOAgendaView()
406{
407 if ( calendar() )
408 calendar()->unregisterObserver( this );
409 delete mAgendaPopup;
410 delete mAllDayAgendaPopup;
411}
412
413void KOAgendaView::connectAgenda( KOAgenda *agenda, TQPopupMenu *popup,
414 KOAgenda *otherAgenda )
415{
416 connect( agenda, TQ_SIGNAL(showIncidencePopupSignal(Calendar *,Incidence *,const TQDate &)),
417 popup, TQ_SLOT(showIncidencePopup(Calendar *,Incidence *,const TQDate &)) );
418
419 connect( agenda, TQ_SIGNAL(showNewEventPopupSignal()),
420 TQ_SLOT(showNewEventPopup()) );
421
422
423 // Create/Show/Edit/Delete Event
424 connect( agenda, TQ_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)),
425 TQ_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)) );
426
427 connect( agenda, TQ_SIGNAL(newStartSelectSignal()),
428 otherAgenda, TQ_SLOT(clearSelection()) );
429 connect( agenda, TQ_SIGNAL(newStartSelectSignal()),
430 TQ_SIGNAL(timeSpanSelectionChanged()) );
431
432 connect( agenda, TQ_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)),
433 TQ_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)) );
434 connect( agenda, TQ_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)),
435 TQ_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)) );
436 connect( agenda, TQ_SIGNAL(deleteIncidenceSignal(Incidence *)),
437 TQ_SIGNAL(deleteIncidenceSignal(Incidence *)) );
438
439 connect( agenda, TQ_SIGNAL(startMultiModify(const TQString &)),
440 TQ_SIGNAL(startMultiModify(const TQString &)) );
441 connect( agenda, TQ_SIGNAL(endMultiModify()),
442 TQ_SIGNAL(endMultiModify()) );
443
444 connect( agenda, TQ_SIGNAL(itemModified(KOAgendaItem *)),
445 TQ_SLOT(updateEventDates(KOAgendaItem *)) );
446
447 connect( agenda, TQ_SIGNAL(enableAgendaUpdate(bool)),
448 TQ_SLOT(enableAgendaUpdate(bool)) );
449
450 // drag signals
451 connect( agenda, TQ_SIGNAL(startDragSignal(Incidence *)),
452 TQ_SLOT(startDrag(Incidence *)) );
453
454 // synchronize selections
455 connect( agenda, TQ_SIGNAL(incidenceSelected(Incidence *,const TQDate &)),
456 otherAgenda, TQ_SLOT(deselectItem()) );
457 connect( agenda, TQ_SIGNAL(incidenceSelected(Incidence *,const TQDate &)),
458 TQ_SIGNAL(incidenceSelected(Incidence *,const TQDate &)) );
459
460 // rescheduling of todos by d'n'd
461 connect( agenda, TQ_SIGNAL(droppedToDo(Todo *,const TQPoint &,bool)),
462 TQ_SLOT(slotTodoDropped(Todo *,const TQPoint &,bool)) );
463
464}
465
466void KOAgendaView::zoomInVertically( )
467{
468 if ( !mIsSideBySide )
469 KOPrefs::instance()->mHourSize++;
470 mAgenda->updateConfig();
471 mAgenda->checkScrollBoundaries();
472
473 mTimeLabels->updateConfig();
474 mTimeLabels->positionChanged();
475 mTimeLabels->repaint();
476
477 updateView();
478}
479
480void KOAgendaView::zoomOutVertically( )
481{
482
483 if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) {
484
485 if ( !mIsSideBySide )
486 KOPrefs::instance()->mHourSize--;
487 mAgenda->updateConfig();
488 mAgenda->checkScrollBoundaries();
489
490 mTimeLabels->updateConfig();
491 mTimeLabels->positionChanged();
492 mTimeLabels->repaint();
493
494 updateView();
495 }
496}
497
498void KOAgendaView::zoomInHorizontally( const TQDate &date)
499{
500 TQDate begin;
501 TQDate newBegin;
502 TQDate dateToZoom = date;
503 int ndays,count;
504
505 begin = mSelectedDates.first();
506 ndays = begin.daysTo( mSelectedDates.last() );
507
508 // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it.
509 if ( ! dateToZoom.isValid () )
510 dateToZoom=mAgenda->selectedIncidenceDate();
511
512 if( !dateToZoom.isValid() ) {
513 if ( ndays > 1 ) {
514 newBegin=begin.addDays(1);
515 count = ndays-1;
516 emit zoomViewHorizontally ( newBegin , count );
517 }
518 } else {
519 if ( ndays <= 2 ) {
520 newBegin = dateToZoom;
521 count = 1;
522 } else {
523 newBegin = dateToZoom.addDays( -ndays/2 +1 );
524 count = ndays -1 ;
525 }
526 emit zoomViewHorizontally ( newBegin , count );
527 }
528}
529
530void KOAgendaView::zoomOutHorizontally( const TQDate &date )
531{
532 TQDate begin;
533 TQDate newBegin;
534 TQDate dateToZoom = date;
535 int ndays,count;
536
537 begin = mSelectedDates.first();
538 ndays = begin.daysTo( mSelectedDates.last() );
539
540 // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it.
541 if ( ! dateToZoom.isValid () )
542 dateToZoom=mAgenda->selectedIncidenceDate();
543
544 if ( !dateToZoom.isValid() ) {
545 newBegin = begin.addDays(-1);
546 count = ndays+3 ;
547 } else {
548 newBegin = dateToZoom.addDays( -ndays/2-1 );
549 count = ndays+3;
550 }
551
552 if ( abs( count ) >= 31 )
553 kdDebug(5850) << "change to the mounth view?"<<endl;
554 else
555 //We want to center the date
556 emit zoomViewHorizontally( newBegin, count );
557}
558
559void KOAgendaView::zoomView( const int delta, const TQPoint &pos,
560 const TQt::Orientation orient )
561{
562 static TQDate zoomDate;
563 static TQTimer *t = new TQTimer( this );
564
565
566 //Zoom to the selected incidence, on the other way
567 // zoom to the date on screen after the first mousewheel move.
568 if ( orient == TQt::Horizontal ) {
569 TQDate date=mAgenda->selectedIncidenceDate();
570 if ( date.isValid() )
571 zoomDate=date;
572 else{
573 if ( !t->isActive() ) {
574 zoomDate= mSelectedDates[pos.x()];
575 }
576 t->start ( 1000,true );
577 }
578 if ( delta > 0 )
579 zoomOutHorizontally( zoomDate );
580 else
581 zoomInHorizontally( zoomDate );
582 } else {
583 // Vertical zoom
584 TQPoint posConstentsOld = mAgenda->gridToContents(pos);
585 if ( delta > 0 ) {
586 zoomOutVertically();
587 } else {
588 zoomInVertically();
589 }
590 TQPoint posConstentsNew = mAgenda->gridToContents(pos);
591 mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() );
592 }
593}
594
596{
597// kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl;
598
599 // Check if mSelectedDates has changed, if not just return
600 // Removes some flickering and gains speed (since this is called by each updateView())
601 if ( !force && mSaveSelectedDates == mSelectedDates ) {
602 return;
603 }
604 mSaveSelectedDates = mSelectedDates;
605
606 delete mDayLabels;
607 mDateDayLabels.clear();
608
609 mDayLabels = new TQFrame (mDayLabelsFrame);
610 mLayoutDayLabels = new TQHBoxLayout(mDayLabels);
611 if ( !mIsSideBySide )
612 mLayoutDayLabels->addSpacing(mTimeLabels->width());
613
614 const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
615
616 DateList::ConstIterator dit;
617 for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
618 TQDate date = *dit;
619 TQBoxLayout *dayLayout = new TQVBoxLayout(mLayoutDayLabels);
620 mLayoutDayLabels->setStretchFactor(dayLayout, 1);
621// dayLayout->setMinimumWidth(1);
622
623 int dW = calsys->dayOfWeek(date);
624 TQString veryLongStr = TDEGlobal::locale()->formatDate( date );
625 TQString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" )
626 .arg( calsys->weekDayName( dW, true ) )
627 .arg( calsys->day(date) );
628 TQString shortstr = TQString::number(calsys->day(date));
629
630 KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr,
631 longstr, veryLongStr, mDayLabels);
632 dayLabel->useShortText(); // will be recalculated in updateDayLabelSizes() anyway
633 dayLabel->setMinimumWidth(1);
634 dayLabel->setAlignment(TQLabel::AlignHCenter);
635 if (date == TQDate::currentDate()) {
636 TQFont font = dayLabel->font();
637 font.setBold(true);
638 dayLabel->setFont(font);
639 }
640 dayLayout->addWidget(dayLabel);
641 mDateDayLabels.append( dayLabel );
642
643 // if a holiday region is selected, show the holiday name
644 TQStringList texts = KOGlobals::self()->holiday( date );
645 TQStringList::ConstIterator textit = texts.begin();
646 for ( ; textit != texts.end(); ++textit ) {
647 // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
648 KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), TQString(), mDayLabels );
649 label->setMinimumWidth(1);
650 label->setAlignment(AlignCenter);
651 dayLayout->addWidget(label);
652 }
653
654#ifndef KORG_NOPLUGINS
655 CalendarDecoration::List cds = KOCore::self()->calendarDecorations();
657 for(it = cds.first(); it; it = cds.next()) {
658 TQString text = it->shortText( date );
659 if ( !text.isEmpty() ) {
660 // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
661 KOAlternateLabel*label = new KOAlternateLabel( text, text, TQString(), mDayLabels );
662 label->setMinimumWidth(1);
663 label->setAlignment(AlignCenter);
664 dayLayout->addWidget(label);
665 }
666 }
667
668 for(it = cds.first(); it; it = cds.next()) {
669 TQWidget *wid = it->smallWidget(mDayLabels,date);
670 if ( wid ) {
671// wid->setHeight(20);
672 dayLayout->addWidget(wid);
673 }
674 }
675#endif
676 }
677
678 if ( !mIsSideBySide )
679 mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width());
680 mDayLabels->show();
681 TQTimer::singleShot( 0, this, TQ_SLOT( updateDayLabelSizes() ) );
682}
683
684void KOAgendaView::enableAgendaUpdate( bool enable )
685{
686 mAllowAgendaUpdate = enable;
687}
688
690{
691 // Not sure about the max number of events, so return 0 for now.
692 return 0;
693}
694
696{
697 return mSelectedDates.count();
698}
699
701{
702 Incidence::List selected;
703 Incidence *incidence;
704
705 incidence = mAgenda->selectedIncidence();
706 if (incidence) selected.append(incidence);
707
708 incidence = mAllDayAgenda->selectedIncidence();
709 if (incidence) selected.append(incidence);
710
711 return selected;
712}
713
715{
716 DateList selected;
717 TQDate qd;
718
719 qd = mAgenda->selectedIncidenceDate();
720 if (qd.isValid()) selected.append(qd);
721
722 qd = mAllDayAgenda->selectedIncidenceDate();
723 if (qd.isValid()) selected.append(qd);
724
725 return selected;
726}
727
728bool KOAgendaView::eventDurationHint( TQDateTime &startDt, TQDateTime &endDt,
729 bool &allDay )
730{
731 if ( selectionStart().isValid() ) {
732 TQDateTime start = selectionStart();
733 TQDateTime end = selectionEnd();
734
735 if ( start.secsTo( end ) == 15*60 ) {
736 // One cell in the agenda view selected, e.g.
737 // because of a double-click, => Use the default duration
738 TQTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() );
739 int addSecs = ( defaultDuration.hour()*3600 ) +
740 ( defaultDuration.minute()*60 );
741 end = start.addSecs( addSecs );
742 }
743
744 startDt = start;
745 endDt = end;
746 allDay = selectedIsAllDay();
747 return true;
748 }
749 return false;
750}
751
754{
755 if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false;
756
757 if (selectedIsAllDay()) {
758 int days = selectionStart().daysTo(selectionEnd());
759 return ( days < 1 );
760 } else {
761 int secs = selectionStart().secsTo(selectionEnd());
762 return ( secs <= 24*60*60/mAgenda->rows() );
763 }
764}
765
766
767void KOAgendaView::updateView()
768{
769// kdDebug(5850) << "KOAgendaView::updateView()" << endl;
770 fillAgenda();
771}
772
773
774/*
775 Update configuration settings for the agenda view. This method is not
776 complete.
777*/
778void KOAgendaView::updateConfig()
779{
780// kdDebug(5850) << "KOAgendaView::updateConfig()" << endl;
781
782 // update config for children
783 mTimeLabels->updateConfig();
784 mAgenda->updateConfig();
785 mAllDayAgenda->updateConfig();
786
787 // widget synchronization
788 // FIXME: find a better way, maybe signal/slot
789 mTimeLabels->positionChanged();
790
791 // for some reason, this needs to be called explicitly
792 mTimeLabels->repaint();
793
794 updateTimeBarWidth();
795
796 // ToolTips displaying summary of events
797 KOAgendaItem::toolTipGroup()->setEnabled(KOPrefs::instance()
798 ->mEnableToolTips);
799
801
802 createDayLabels( true );
803
804 updateView();
805}
806
807void KOAgendaView::updateTimeBarWidth()
808{
809 int width;
810
811 width = mDummyAllDayLeft->fontMetrics().width( i18n("All Day") );
812 width = TQMAX( width, mTimeLabels->width() );
813
814 mDummyAllDayLeft->setFixedWidth( width );
815 mTimeLabels->setFixedWidth( width );
816}
817
818void KOAgendaView::updateDayLabelSizes()
819{
820 // First, calculate the maximum text type that fits for all labels
821 KOAlternateLabel::TextType overallType = KOAlternateLabel::Extensive;
822 TQPtrList<KOAlternateLabel>::const_iterator it = mDateDayLabels.constBegin();
823 for( ; it != mDateDayLabels.constEnd(); it++ ) {
824 KOAlternateLabel::TextType type = (*it)->largestFittingTextType();
825 if ( type < overallType )
826 overallType = type;
827 }
828
829 // Then, set that maximum text type to all the labels
830 it = mDateDayLabels.constBegin();
831 for( ; it != mDateDayLabels.constEnd(); it++ ) {
832 (*it)->setFixedType( overallType );
833 }
834}
835
836void KOAgendaView::resizeEvent( TQResizeEvent *resizeEvent )
837{
838 updateDayLabelSizes();
839 KOrg::AgendaView::resizeEvent( resizeEvent );
840}
841
842void KOAgendaView::updateEventDates( KOAgendaItem *item )
843{
844 kdDebug(5850) << "KOAgendaView::updateEventDates(): " << item->text()
845 << "; item->cellXLeft(): " << item->cellXLeft()
846 << "; item->cellYTop(): " << item->cellYTop()
847 << "; item->lastMultiItem(): " << item->lastMultiItem()
848 << "; item->itemPos(): " << item->itemPos()
849 << "; item->itemCount(): " << item->itemCount()
850 << endl;
851
852 TQDateTime startDt, endDt;
853
854 // Start date of this incidence, calculate the offset from it (so recurring and
855 // non-recurring items can be treated exactly the same, we never need to check
856 // for doesRecur(), because we only move the start day by the number of days the
857 // agenda item was really moved. Smart, isn't it?)
858 TQDate thisDate;
859 if ( item->cellXLeft() < 0 ) {
860 thisDate = ( mSelectedDates.first() ).addDays( item->cellXLeft() );
861 } else {
862 thisDate = mSelectedDates[ item->cellXLeft() ];
863 }
864 TQDate oldThisDate( item->itemDate() );
865 const int daysOffset = oldThisDate.daysTo( thisDate );
866 int daysLength = 0;
867
868 // startDt.setDate( startDate );
869
870 Incidence *incidence = item->incidence();
871 if ( !incidence ) {
872 return;
873 }
874 if ( !mChanger ||
875 !mChanger->beginChange( incidence, resourceCalendar(), subResourceCalendar() ) ) {
876 return;
877 }
878 Incidence *oldIncidence = incidence->clone();
879
880 TQTime startTime( 0, 0, 0 ), endTime( 0, 0, 0 );
881 if ( incidence->doesFloat() ) {
882 daysLength = item->cellWidth() - 1;
883 } else {
884 startTime = mAgenda->gyToTime( item->cellYTop() );
885 if ( item->lastMultiItem() ) {
886 endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 );
887 daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft();
888 kdDebug(5850) << "item->lastMultiItem()->cellXLeft(): " << item->lastMultiItem()->cellXLeft()
889 << endl;
890 } else if ( item->itemPos() == item->itemCount() && item->itemCount() > 1 ) {
891 /* multiitem handling in agenda assumes two things:
892 - The start (first KOAgendaItem) is always visible.
893 - The first KOAgendaItem of the incidence has a non-null item->lastMultiItem()
894 pointing to the last KOagendaItem.
895
896 But those aren't always met, for example when in day-view.
897 kolab/issue4417
898 */
899
900 // Cornercase 1: - Resizing the end of the event but the start isn't visible
901 endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
902 daysLength = item->itemCount() - 1;
903 startTime = incidence->dtStart().time();
904 } else if ( item->itemPos() == 1 && item->itemCount() > 1 ) {
905 // Cornercase 2: - Resizing the start of the event but the end isn't visible
906 endTime = incidence->dtEnd().time();
907 daysLength = item->itemCount() - 1;
908 } else {
909 endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
910 }
911 }
912
913 kdDebug(5850) << "daysLength: " << daysLength << "; startTime: " << startTime
914 << "; endTime: " << endTime << "; thisDate: " << thisDate
915 << "; incidence->dtStart(): " << incidence->dtStart() << endl;
916
917 // FIXME: use a visitor here
918 if ( incidence->type() == "Event" ) {
919 startDt = incidence->dtStart();
920 startDt = startDt.addDays( daysOffset );
921 startDt.setTime( startTime );
922 endDt = startDt.addDays( daysLength );
923 endDt.setTime( endTime );
924 Event* ev = static_cast<Event*>( incidence );
925 if ( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) {
926 // No change
927 delete oldIncidence;
928 return;
929 }
930 incidence->setDtStart( startDt );
931 ev->setDtEnd( endDt );
932 } else if ( incidence->type() == "Todo" ) {
933 Todo *td = static_cast<Todo*>( incidence );
934 startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
935 startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) );
936 startDt.setTime( startTime );
937 endDt.setDate( thisDate );
938 endDt.setTime( endTime );
939
940 if( td->dtDue() == endDt ) {
941 // No change
942 delete oldIncidence;
943 return;
944 }
945 }
946 // FIXME: Adjusting the recurrence should really go to CalendarView so this
947 // functionality will also be available in other views!
948 // TODO_Recurrence: This does not belong here, and I'm not really sure
949 // how it's supposed to work anyway.
950/*
951 Recurrence *recur = incidence->recurrence();
952 if ( recur->doesRecur() && daysOffset != 0 ) {
953 switch ( recur->recurrenceType() ) {
954 case Recurrence::rYearlyPos: {
955 int freq = recur->frequency();
956 int duration = recur->duration();
957 TQDate endDt( recur->endDate() );
958 bool negative = false;
959
960 TQPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() );
961 if ( monthPos.first() ) {
962 negative = monthPos.first()->negative;
963 }
964 TQBitArray days( 7 );
965 int pos = 0;
966 days.fill( false );
967 days.setBit( thisDate.dayOfWeek() - 1 );
968 if ( negative ) {
969 pos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
970 } else {
971 pos = ( thisDate.day()-1 ) / 7 + 1;
972 }
973 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
974 recur->unsetRecurs();
975 if ( duration != 0 ) {
976 recur->setYearly( Recurrence::rYearlyPos, freq, duration );
977 } else {
978 recur->setYearly( Recurrence::rYearlyPos, freq, endDt );
979 }
980 recur->addYearlyMonthPos( pos, days );
981 recur->addYearlyNum( thisDate.month() );
982
983 break; }
984 case Recurrence::rYearlyDay: {
985 int freq = recur->frequency();
986 int duration = recur->duration();
987 TQDate endDt( recur->endDate() );
988 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
989 recur->unsetRecurs();
990 if ( duration == 0 ) { // end by date
991 recur->setYearly( Recurrence::rYearlyDay, freq, endDt );
992 } else {
993 recur->setYearly( Recurrence::rYearlyDay, freq, duration );
994 }
995 recur->addYearlyNum( thisDate.dayOfYear() );
996 break; }
997 case Recurrence::rYearlyMonth: {
998 int freq = recur->frequency();
999 int duration = recur->duration();
1000 TQDate endDt( recur->endDate() );
1001 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
1002 recur->unsetRecurs();
1003 if ( duration != 0 ) {
1004 recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration );
1005 } else {
1006 recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt );
1007 }
1008 recur->addYearlyNum( thisDate.month() );
1009 break; }
1010 case Recurrence::rMonthlyPos: {
1011 int freq = recur->frequency();
1012 int duration = recur->duration();
1013 TQDate endDt( recur->endDate() );
1014 TQPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() );
1015 if ( !monthPos.isEmpty() ) {
1016 // FIXME: How shall I adapt the day x of week Y if we move the date across month borders???
1017 // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
1018 // That's fine for korganizer, but might mess up other organizers.
1019 TQBitArray rDays( 7 );
1020 rDays = monthPos.first()->rDays;
1021 bool negative = monthPos.first()->negative;
1022 int newPos;
1023 rDays.fill( false );
1024 rDays.setBit( thisDate.dayOfWeek() - 1 );
1025 if ( negative ) {
1026 newPos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
1027 } else {
1028 newPos = ( thisDate.day()-1 ) / 7 + 1;
1029 }
1030
1031 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
1032 recur->unsetRecurs();
1033 if ( duration == 0 ) { // end by date
1034 recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt );
1035 } else {
1036 recur->setMonthly( Recurrence::rMonthlyPos, freq, duration );
1037 }
1038 recur->addMonthlyPos( newPos, rDays );
1039 }
1040 break;}
1041 case Recurrence::rMonthlyDay: {
1042 int freq = recur->frequency();
1043 int duration = recur->duration();
1044 TQDate endDt( recur->endDate() );
1045 TQPtrList<int> monthDays( recur->monthDays() );
1046 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
1047 recur->unsetRecurs();
1048 if ( duration == 0 ) { // end by date
1049 recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt );
1050 } else {
1051 recur->setMonthly( Recurrence::rMonthlyDay, freq, duration );
1052 }
1053 // FIXME: How shall I adapt the n-th day if we move the date across month borders???
1054 // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
1055 // That's fine for korganizer, but might mess up other organizers.
1056 recur->addMonthlyDay( thisDate.day() );
1057
1058 break;}
1059 case Recurrence::rWeekly: {
1060 TQBitArray days(7), oldDays( recur->days() );
1061 int offset = daysOffset % 7;
1062 if ( offset<0 ) offset = (offset+7) % 7;
1063 // rotate the days
1064 for (int d=0; d<7; d++ ) {
1065 days.setBit( (d+offset) % 7, oldDays.at(d) );
1066 }
1067 if ( recur->duration() == 0 ) { // end by date
1068 recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() );
1069 } else { // duration or no end
1070 recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() );
1071 }
1072 break;}
1073 // nothing to be done for the following:
1074 case Recurrence::rDaily:
1075 case Recurrence::rHourly:
1076 case Recurrence::rMinutely:
1077 case Recurrence::rNone:
1078 default:
1079 break;
1080 }
1081 if ( recur->duration()==0 ) { // end by date
1082 recur->setEndDate( recur->endDate().addDays( daysOffset ) );
1083 }
1084 KMessageBox::information( this, i18n("A recurring calendar item was moved "
1085 "to a different day. The recurrence settings "
1086 "have been updated with that move. Please check "
1087 "them in the editor."),
1088 i18n("Recurrence Moved"),
1089 "RecurrenceMoveInAgendaWarning" );
1090 }*/
1091
1092 // FIXME: use a visitor here
1093 if ( incidence->type() == "Event" ) {
1094 incidence->setDtStart( startDt );
1095 static_cast<Event*>( incidence )->setDtEnd( endDt );
1096 } else if ( incidence->type() == "Todo" ) {
1097 Todo *td = static_cast<Todo*>( incidence );
1098 if ( td->hasStartDate() ) {
1099 td->setDtStart( startDt );
1100 }
1101 td->setDtDue( endDt );
1102 }
1103
1104 item->setItemDate( startDt.date() );
1105
1106 KOIncidenceToolTip::remove( item );
1107 KOIncidenceToolTip::add( item, calendar(), incidence, thisDate, KOAgendaItem::toolTipGroup() );
1108
1109 const bool result = mChanger->changeIncidence( oldIncidence, incidence,
1110 KOGlobals::DATE_MODIFIED, this );
1111 mChanger->endChange( incidence, resourceCalendar(), subResourceCalendar() );
1112 delete oldIncidence;
1113
1114 if ( !result ) {
1115 mPendingChanges = true;
1116 TQTimer::singleShot( 0, this, TQ_SLOT(updateView()) );
1117 return;
1118 }
1119
1120 // don't update the agenda as the item already has the correct coordinates.
1121 // an update would delete the current item and recreate it, but we are still
1122 // using a pointer to that item! => CRASH
1123 enableAgendaUpdate( false );
1124 // We need to do this in a timer to make sure we are not deleting the item
1125 // we are currently working on, which would lead to crashes
1126 // Only the actually moved agenda item is already at the correct position and mustn't be
1127 // recreated. All others have to!!!
1128 if ( incidence->doesRecur() ) {
1129 mUpdateItem = incidence;
1130 TQTimer::singleShot( 0, this, TQ_SLOT( doUpdateItem() ) );
1131 }
1132
1133 enableAgendaUpdate( true );
1134
1135// kdDebug(5850) << "KOAgendaView::updateEventDates() done " << endl;
1136}
1137
1139{
1140 if ( mUpdateItem ) {
1141 changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED );
1142 mUpdateItem = 0;
1143 }
1144}
1145
1146
1147
1148void KOAgendaView::showDates( const TQDate &start, const TQDate &end )
1149{
1150// kdDebug(5850) << "KOAgendaView::selectDates" << endl;
1151 if ( !mSelectedDates.isEmpty() && mSelectedDates.first() == start
1152 && mSelectedDates.last() == end && !mPendingChanges )
1153 return;
1154
1155 mSelectedDates.clear();
1156
1157 TQDate d = start;
1158 while ( d <= end ) {
1159 mSelectedDates.append( d );
1160 d = d.addDays( 1 );
1161 }
1162
1163 mAreDatesInitialized = true;
1164
1165 // and update the view
1166 fillAgenda();
1167}
1168
1169
1170void KOAgendaView::showIncidences( const Incidence::List &, const TQDate & )
1171{
1172 kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl;
1173}
1174
1175void KOAgendaView::insertIncidence( Incidence *incidence, const TQDate &curDate )
1176{
1177 if ( !filterByResource( incidence ) ) {
1178 return;
1179 }
1180
1181 // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's
1182 Event *event = dynamic_cast<Event *>( incidence );
1183 Todo *todo = dynamic_cast<Todo *>( incidence );
1184
1185 int curCol = mSelectedDates.first().daysTo( curDate );
1186
1187 // In case incidence->dtStart() isn't visible (crosses bounderies)
1188 if ( curCol < 0 ) {
1189 curCol = 0;
1190 }
1191
1192 // The date for the event is not displayed, just ignore it
1193 if ( curCol >= int( mSelectedDates.count() ) ) {
1194 return;
1195 }
1196
1197 // Default values, which can never be reached
1198 mMinY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) ) + 1;
1199 mMaxY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) ) - 1;
1200
1201 int beginX;
1202 int endX;
1203 TQDate columnDate;
1204 if ( event ) {
1205 TQDate firstVisibleDate = mSelectedDates.first();
1206 // its crossing bounderies, lets calculate beginX and endX
1207 if ( curDate < firstVisibleDate ) {
1208 beginX = curCol + firstVisibleDate.daysTo( curDate );
1209 endX = beginX + event->dtStart().daysTo( event->dtEnd() );
1210 columnDate = firstVisibleDate;
1211 } else {
1212 beginX = curCol;
1213 endX = beginX + event->dtStart().daysTo( event->dtEnd() );
1214 columnDate = curDate;
1215 }
1216 } else if ( todo ) {
1217 if ( !todo->hasDueDate() ) {
1218 return; // todo shall not be displayed if it has no date
1219 }
1220 columnDate = curDate;
1221 beginX = endX = curCol;
1222
1223 } else {
1224 return;
1225 }
1226 if ( todo && todo->isOverdue() ) {
1227 mAllDayAgenda->insertAllDayItem( incidence, columnDate, curCol, curCol );
1228 } else if ( incidence->doesFloat() ||
1229 ( todo &&
1230 !todo->dtDue().isValid() ) ) {
1231 mAllDayAgenda->insertAllDayItem( incidence, columnDate, beginX, endX );
1232 } else if ( event && event->isMultiDay() ) {
1233 int startY = mAgenda->timeToY( event->dtStart().time() );
1234 TQTime endtime = event->dtEnd().time();
1235 if ( endtime == TQTime( 0, 0, 0 ) ) {
1236 endtime = TQTime( 23, 59, 59 );
1237 }
1238 int endY = mAgenda->timeToY( endtime ) - 1;
1239 if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) {
1240 mAgenda->insertMultiItem( event, columnDate, beginX, endX, startY, endY );
1241
1242 }
1243 if ( beginX == curCol ) {
1244 mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) );
1245 if ( startY < mMinY[curCol] ) {
1246 mMinY[curCol] = startY;
1247 }
1248 } else if ( endX == curCol ) {
1249 mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) );
1250 if ( endY > mMaxY[curCol] ) {
1251 mMaxY[curCol] = endY;
1252 }
1253 } else {
1254 mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) );
1255 mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) );
1256 }
1257 } else {
1258 int startY = 0, endY = 0;
1259 if ( event ) {
1260 startY = mAgenda->timeToY( incidence->dtStart().time() );
1261 TQTime endtime = event->dtEnd().time();
1262 if ( endtime == TQTime( 0, 0, 0 ) ) {
1263 endtime = TQTime( 23, 59, 59 );
1264 }
1265 endY = mAgenda->timeToY( endtime ) - 1;
1266 }
1267 if ( todo ) {
1268 TQTime t = todo->dtDue().time();
1269
1270 if ( t == TQTime( 0, 0 ) ) {
1271 t = TQTime( 23, 59 );
1272 }
1273
1274 int halfHour = 1800;
1275 if ( t.addSecs( -halfHour ) < t ) {
1276 startY = mAgenda->timeToY( t.addSecs( -halfHour ) );
1277 endY = mAgenda->timeToY( t ) - 1;
1278 } else {
1279 startY = 0;
1280 endY = mAgenda->timeToY( t.addSecs( halfHour ) ) - 1;
1281 }
1282 }
1283 if ( endY < startY ) {
1284 endY = startY;
1285 }
1286 mAgenda->insertItem( incidence, columnDate, curCol, startY, endY, 1, 1 );
1287 if ( startY < mMinY[curCol] ) {
1288 mMinY[curCol] = startY;
1289 }
1290 if ( endY > mMaxY[curCol] ) {
1291 mMaxY[curCol] = endY;
1292 }
1293 }
1294}
1295
1296void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence )
1297{
1298 Todo *todo = dynamic_cast<Todo *>(incidence);
1299 CalFilter *filter = calendar()->filter();
1300 if ( ( filter && !filter->filterIncidence( incidence ) ) ||
1301 ( ( todo && !KOPrefs::instance()->showAllDayTodo() ) ) ) {
1302 return;
1303 }
1304
1305 displayIncidence( incidence );
1306}
1307
1308void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode )
1309{
1310 switch ( mode ) {
1311 case KOGlobals::INCIDENCEADDED:
1312 {
1313 // Add an event. No need to recreate the whole view!
1314 // recreating everything even causes troubles: dropping to the
1315 // day matrix recreates the agenda items, but the evaluation is
1316 // still in an agendaItems' code, which was deleted in the mean time.
1317 // Thus KOrg crashes...
1318 changeIncidenceDisplayAdded( incidence );
1320 break;
1321 }
1322 case KOGlobals::INCIDENCEEDITED:
1323 {
1324 if ( mAllowAgendaUpdate ) {
1325 removeIncidence( incidence );
1326 changeIncidenceDisplayAdded( incidence );
1327 }
1329 break;
1330 }
1331 case KOGlobals::INCIDENCEDELETED:
1332 {
1333 removeIncidence( incidence );
1335 break;
1336 }
1337 default:
1338 return;
1339 }
1340
1341 // HACK: Update the view if the all-day agenda has been modified.
1342 // Do this because there are some layout problems in the
1343 // all-day agenda that are not easily solved, but clearing
1344 // and redrawing works ok.
1345 if ( incidence->doesFloat() ) {
1346 updateView();
1347 }
1348}
1349
1350void KOAgendaView::fillAgenda( const TQDate & )
1351{
1352 fillAgenda();
1353}
1354
1356{
1357 if ( !mAreDatesInitialized ) {
1358 return;
1359 }
1360
1361 mPendingChanges = false;
1362
1363 /* Remember the uids of the selected items. In case one of the
1364 * items was deleted and re-added, we want to reselect it. */
1365 const TQString &selectedAgendaUid = mAgenda->lastSelectedUid();
1366 const TQString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
1367
1368 enableAgendaUpdate( true );
1369 clearView();
1370
1371 mAllDayAgenda->changeColumns( mSelectedDates.count() );
1372 mAgenda->changeColumns( mSelectedDates.count() );
1373 mEventIndicatorTop->changeColumns( mSelectedDates.count() );
1374 mEventIndicatorBottom->changeColumns( mSelectedDates.count() );
1375
1376 createDayLabels( false );
1378
1379 mMinY.resize( mSelectedDates.count() );
1380 mMaxY.resize( mSelectedDates.count() );
1381
1382 mAgenda->setDateList( mSelectedDates );
1383
1384 bool somethingReselected = false;
1385 Incidence::List incidences = calendar()->incidences();
1386
1387 for ( Incidence::List::ConstIterator it = incidences.begin(); it!=incidences.constEnd(); ++it ) {
1388 Incidence *incidence = (*it);
1389 displayIncidence( incidence );
1390
1391 if( incidence->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
1392 mAgenda->selectItemByUID( incidence->uid() );
1393 somethingReselected = true;
1394 }
1395
1396 if( incidence->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
1397 mAllDayAgenda->selectItemByUID( incidence->uid() );
1398 somethingReselected = true;
1399 }
1400
1401 }
1402
1403 mAgenda->checkScrollBoundaries();
1405
1406 // mAgenda->viewport()->update();
1407 // mAllDayAgenda->viewport()->update();
1408
1409 // make invalid
1411
1412 if( !somethingReselected ) {
1413 emit incidenceSelected( 0, TQDate() );
1414 }
1415}
1416
1417void KOAgendaView::displayIncidence( Incidence *incidence )
1418{
1419 TQDate today = TQDate::currentDate();
1420 DateTimeList::iterator t;
1421
1422 // FIXME: use a visitor here
1423 Todo *todo = dynamic_cast<Todo *>( incidence );
1424 Event *event = dynamic_cast<Event *>( incidence );
1425
1426 TQDateTime firstVisibleDateTime = mSelectedDates.first();
1427 TQDateTime lastVisibleDateTime = mSelectedDates.last();
1428
1429 lastVisibleDateTime.setTime( TQTime( 23, 59, 59, 59 ) );
1430 firstVisibleDateTime.setTime( TQTime( 0, 0 ) );
1431 DateTimeList dateTimeList;
1432
1433 TQDateTime incDtStart = incidence->dtStart();
1434 TQDateTime incDtEnd = incidence->dtEnd();
1435
1436 if ( todo &&
1437 ( !KOPrefs::instance()->showAllDayTodo() || !todo->hasDueDate() ) ) {
1438 return;
1439 }
1440
1441 if ( incidence->doesRecur() ) {
1442 int eventDuration = event ? incDtStart.daysTo( incDtEnd ) : 0;
1443
1444 // if there's a multiday event that starts before firstVisibleDateTime but ends after
1445 // lets include it. timesInInterval() ignores incidences that aren't totaly inside
1446 // the range
1447 TQDateTime startDateTimeWithOffset = firstVisibleDateTime.addDays( -eventDuration );
1448 dateTimeList =
1449 incidence->recurrence()->timesInInterval( startDateTimeWithOffset,
1450 lastVisibleDateTime );
1451 } else {
1452 TQDateTime dateToAdd; // date to add to our date list
1453 TQDateTime incidenceStart;
1454 TQDateTime incidenceEnd;
1455
1456 if ( todo && todo->hasDueDate() && !todo->isOverdue() ) {
1457 // If it's not overdue it will be shown at the original date (not today)
1458 dateToAdd = todo->dtDue();
1459
1460 // To-dos are drawn with the bottom of the rectangle at dtDue
1461 // if dtDue is at 00:00, then it should be displayed in the previous day, at 23:59
1462 if ( !todo->doesFloat() && dateToAdd.time() == TQTime( 0, 0 ) ) {
1463 dateToAdd = dateToAdd.addSecs( -1 );
1464 }
1465
1466 incidenceEnd = dateToAdd;
1467 } else if ( event ) {
1468 dateToAdd = incDtStart;
1469 incidenceEnd = incDtEnd;
1470 }
1471
1472 if ( incidence->doesFloat() ) {
1473 // so comparisons with < > actually work
1474 dateToAdd.setTime( TQTime( 0, 0 ) );
1475 incidenceEnd.setTime( TQTime( 23, 59, 59, 59 ) );
1476 }
1477
1478 if ( dateToAdd <= lastVisibleDateTime && incidenceEnd > firstVisibleDateTime ) {
1479 dateTimeList += dateToAdd;
1480 }
1481 }
1482
1483 // ToDo items shall be displayed today if they are already overdude
1484 TQDateTime dateTimeToday = today;
1485 if ( todo &&
1486 todo->isOverdue() &&
1487 dateTimeToday >= firstVisibleDateTime &&
1488 dateTimeToday <= lastVisibleDateTime ) {
1489
1490 bool doAdd = true;
1491
1492 if ( todo->doesRecur() ) {
1493 /* If there's a recurring instance showing up today don't add "today" again
1494 * we don't want the event to appear duplicated */
1495 for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
1496 if ( (*t).date() == today ) {
1497 doAdd = false;
1498 break;
1499 }
1500 }
1501 }
1502
1503 if ( doAdd ) {
1504 dateTimeList += dateTimeToday;
1505 }
1506 }
1507
1508 for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
1509 insertIncidence( incidence, (*t).date() );
1510 }
1511}
1512
1514{
1515// kdDebug(5850) << "ClearView" << endl;
1516 mAllDayAgenda->clear();
1517 mAgenda->clear();
1518}
1519
1520CalPrinterBase::PrintType KOAgendaView::printType()
1521{
1522 if ( currentDateCount() == 1 ) return CalPrinterBase::Day;
1523 else return CalPrinterBase::Week;
1524}
1525
1526void KOAgendaView::updateEventIndicatorTop( int newY )
1527{
1528 uint i;
1529 for( i = 0; i < mMinY.size(); ++i ) {
1530 mEventIndicatorTop->enableColumn( i, newY > mMinY[i] );
1531 }
1532 mEventIndicatorTop->update();
1533}
1534
1535void KOAgendaView::updateEventIndicatorBottom( int newY )
1536{
1537 uint i;
1538 for( i = 0; i < mMaxY.size(); ++i ) {
1539 mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
1540 }
1541 mEventIndicatorBottom->update();
1542}
1543
1544void KOAgendaView::slotTodoDropped( Todo *todo, const TQPoint &gpos, bool allDay )
1545{
1546 if ( gpos.x()<0 || gpos.y()<0 ) return;
1547 TQDate day = mSelectedDates[gpos.x()];
1548 TQTime time = mAgenda->gyToTime( gpos.y() );
1549 TQDateTime newTime( day, time );
1550
1551 if ( todo ) {
1552 Todo *existingTodo = calendar()->todo( todo->uid() );
1553 if ( existingTodo ) {
1554 kdDebug(5850) << "Drop existing Todo" << endl;
1555 Todo *oldTodo = existingTodo->clone();
1556 if ( mChanger &&
1557 mChanger->beginChange( existingTodo, resourceCalendar(), subResourceCalendar() ) ) {
1558 existingTodo->setDtDue( newTime );
1559 existingTodo->setFloats( allDay );
1560 existingTodo->setHasDueDate( true );
1561 mChanger->changeIncidence( oldTodo, existingTodo,
1562 KOGlobals::DATE_MODIFIED, this );
1563 mChanger->endChange( existingTodo, resourceCalendar(), subResourceCalendar() );
1564 } else {
1565 KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
1566 "because it cannot be locked.") );
1567 }
1568 delete oldTodo;
1569 } else {
1570 kdDebug(5850) << "Drop new Todo" << endl;
1571 todo->setDtDue( newTime );
1572 todo->setFloats( allDay );
1573 todo->setHasDueDate( true );
1574 if ( !mChanger->addIncidence( todo, 0, TQString(), this ) ) {
1575 KODialogManager::errorSaveIncidence( this, todo );
1576 }
1577 }
1578 }
1579}
1580
1581void KOAgendaView::startDrag( Incidence *incidence )
1582{
1583#ifndef KORG_NODND
1584 DndFactory factory( calendar() );
1585 ICalDrag *vd = factory.createDrag( incidence, this );
1586 if ( vd->drag() ) {
1587 kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl;
1588 }
1589#endif
1590}
1591
1592void KOAgendaView::readSettings()
1593{
1594 readSettings(KOGlobals::self()->config());
1595}
1596
1597void KOAgendaView::readSettings(TDEConfig *config)
1598{
1599// kdDebug(5850) << "KOAgendaView::readSettings()" << endl;
1600
1601 config->setGroup("Views");
1602
1603#ifndef KORG_NOSPLITTER
1604 TQValueList<int> sizes = config->readIntListEntry("Separator AgendaView");
1605 if (sizes.count() == 2) {
1606 mSplitterAgenda->setSizes(sizes);
1607 }
1608#endif
1609
1610 updateConfig();
1611}
1612
1613void KOAgendaView::writeSettings(TDEConfig *config)
1614{
1615// kdDebug(5850) << "KOAgendaView::writeSettings()" << endl;
1616
1617 config->setGroup("Views");
1618
1619#ifndef KORG_NOSPLITTER
1620 TQValueList<int> list = mSplitterAgenda->sizes();
1621 config->writeEntry("Separator AgendaView",list);
1622#endif
1623}
1624
1626{
1627 if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) {
1628 return;
1629 }
1630
1631 mHolidayMask.resize( mSelectedDates.count() + 1 );
1632
1633 for( uint i = 0; i < mSelectedDates.count(); ++i ) {
1634 mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
1635 }
1636
1637 // Store the information about the day before the visible area (needed for
1638 // overnight working hours) in the last bit of the mask:
1639 bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
1640 mHolidayMask[ mSelectedDates.count() ] = showDay;
1641
1642 mAgenda->setHolidayMask( &mHolidayMask );
1643 mAllDayAgenda->setHolidayMask( &mHolidayMask );
1644}
1645
1646void KOAgendaView::setContentsPos( int y )
1647{
1648 mAgenda->setContentsPos( 0, y );
1649}
1650
1651void KOAgendaView::setExpandedButton( bool expanded )
1652{
1653 if ( !mExpandButton ) return;
1654
1655 if ( expanded ) {
1656 mExpandButton->setPixmap( mExpandedPixmap );
1657 } else {
1658 mExpandButton->setPixmap( mNotExpandedPixmap );
1659 }
1660}
1661
1662void KOAgendaView::clearSelection()
1663{
1664 mAgenda->deselectItem();
1665 mAllDayAgenda->deselectItem();
1666}
1667
1668void KOAgendaView::newTimeSpanSelectedAllDay( const TQPoint &start, const TQPoint &end )
1669{
1670 newTimeSpanSelected( start, end );
1671 mTimeSpanInAllDay = true;
1672}
1673
1674void KOAgendaView::newTimeSpanSelected( const TQPoint &start, const TQPoint &end )
1675{
1676 if (!mSelectedDates.count()) return;
1677
1678 mTimeSpanInAllDay = false;
1679
1680 TQDate dayStart = mSelectedDates[ kClamp( start.x(), 0, (int)mSelectedDates.size() - 1 ) ];
1681 TQDate dayEnd = mSelectedDates[ kClamp( end.x(), 0, (int)mSelectedDates.size() - 1 ) ];
1682
1683 TQTime timeStart = mAgenda->gyToTime(start.y());
1684 TQTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
1685
1686 TQDateTime dtStart(dayStart,timeStart);
1687 TQDateTime dtEnd(dayEnd,timeEnd);
1688
1689 mTimeSpanBegin = dtStart;
1690 mTimeSpanEnd = dtEnd;
1691}
1692
1694{
1695 mTimeSpanBegin.setDate(TQDate());
1696 mTimeSpanEnd.setDate(TQDate());
1697 mTimeSpanInAllDay = false;
1698}
1699
1700void KOAgendaView::setTypeAheadReceiver( TQObject *o )
1701{
1702 mAgenda->setTypeAheadReceiver( o );
1703 mAllDayAgenda->setTypeAheadReceiver( o );
1704}
1705
1706void KOAgendaView::finishTypeAhead()
1707{
1708 mAgenda->finishTypeAhead();
1709 mAllDayAgenda->finishTypeAhead();
1710}
1711
1712void KOAgendaView::removeIncidence( Incidence *incidence )
1713{
1714 mAgenda->removeIncidence( incidence );
1715 mAllDayAgenda->removeIncidence( incidence );
1716}
1717
1719{
1720 mMinY = mAgenda->minContentsY();
1721 mMaxY = mAgenda->maxContentsY();
1722
1723 mAgenda->checkScrollBoundaries();
1724 updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
1725 updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
1726}
1727
1728void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
1729{
1730 mChanger = changer;
1731 mAgenda->setIncidenceChanger( changer );
1732 mAllDayAgenda->setIncidenceChanger( changer );
1733}
1734
1735void KOAgendaView::clearTimeSpanSelection()
1736{
1737 mAgenda->clearSelection();
1738 mAllDayAgenda->clearSelection();
1740}
1741
1742bool KOAgendaView::filterByResource( Incidence *incidence )
1743{
1744 // Special handling for groupware to-dos that are in Task folders.
1745 // Put them in the top-level "Calendar" folder for lack of a better
1746 // place since we never show Task type folders even in the
1747 // multiagenda view.
1748 if ( resourceCalendar() && incidence->type() == "Todo" ) {
1749 TQString subRes = resourceCalendar()->subresourceIdentifier( incidence );
1750 if ( resourceCalendar()->subresourceType( subRes ) == "todo" ) {
1751 TQString calmatch = "/.INBOX.directory/Calendar";
1752 TQString i18nmatch = "/.INBOX.directory/" + i18n( "Calendar" );
1753 if ( subResourceCalendar().contains( calmatch ) ||
1754 subResourceCalendar().contains( i18nmatch ) ) {
1755 return true;
1756 }
1757 }
1758 }
1759
1760 // Normal handling
1761 if ( !resourceCalendar() )
1762 return true;
1763 CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() );
1764 if ( !calRes )
1765 return true;
1766 if ( calRes->resource( incidence ) != resourceCalendar() )
1767 return false;
1768 if ( !subResourceCalendar().isEmpty() ) {
1770 return false;
1771 }
1772 return true;
1773}
1774
1775void KOAgendaView::resourcesChanged()
1776{
1777 mPendingChanges = true;
1778}
1779
1780void KOAgendaView::calendarIncidenceAdded(Incidence * incidence)
1781{
1782 Q_UNUSED( incidence );
1783 mPendingChanges = true;
1784}
1785
1786void KOAgendaView::calendarIncidenceChanged(Incidence * incidence)
1787{
1788 Q_UNUSED( incidence );
1789 mPendingChanges = true;
1790}
1791
1792void KOAgendaView::calendarIncidenceDeleted(Incidence * incidence)
1793{
1794 Q_UNUSED( incidence );
1795 mPendingChanges = true;
1796}
This is the main calendar widget.
Definition: calendarview.h:82
bool filterIncidence(Incidence *) const
ResourceCalendar * resource(Incidence *incidence)
virtual Incidence::List incidences()
void registerObserver(Observer *observer)
virtual Todo * todo(const TQString &uid)=0
void unregisterObserver(Observer *observer)
CalFilter * filter()
virtual TQDateTime dtEnd() const
void setDtEnd(const TQDateTime &dtEnd)
bool doesFloat() const
TQString uid() const
virtual TQDateTime dtStart() const
virtual TQDateTime dtEnd() const
void setFloats(bool f)
bool doesRecur() const
virtual Incidence * clone()=0
virtual void setDtStart(const TQDateTime &dtStart)
Recurrence * recurrence() const
DateTimeList timesInInterval(const TQDateTime &start, const TQDateTime &end) const
virtual TQString subresourceIdentifier(Incidence *incidence)
bool hasDueDate() const
bool hasStartDate() const
Todo * clone()
void setDtDue(const TQDateTime &dtDue, bool first=false)
bool isOverdue() const
TQDateTime dtStart(bool first=false) const
void setDtStart(const TQDateTime &dtStart)
TQDateTime dtDue(bool first=false) const
void setHasDueDate(bool hasDueDate)
bool selectedIsSingleCell()
returns if only a single cell is selected, or a range of cells
void doUpdateItem()
update just the display of the given incidence, called by a single-shot timer
void newTimeSpanSelectedAllDay(const TQPoint &start, const TQPoint &end)
Updates data for selected timespan for all day event.
void fillAgenda()
Fill agenda using the current set value for the start date.
TQDateTime selectionEnd()
end-datetime of selection
Definition: koagendaview.h:143
virtual int maxDatesHint()
Returns maximum number of days supported by the koagendaview.
void clearView()
Remove all events from view.
void createDayLabels(bool force)
Create labels for the selected dates.
void updateEventIndicators()
Updates the event indicators after a certain incidence was modified or removed.
void setHolidayMasks()
Set the masks on the agenda widgets indicating, which days are holidays.
TQDateTime selectionStart()
start-datetime of selection
Definition: koagendaview.h:141
void updateEventDates(KOAgendaItem *item)
Update event belonging to agenda item.
virtual DateList selectedIncidenceDates()
returns the currently selected events
void newTimeSpanSelected(const TQPoint &start, const TQPoint &end)
Updates data for selected timespan.
virtual int currentDateCount()
Returns number of currently shown dates.
void slotTodoDropped(Todo *, const TQPoint &, bool)
reschedule the todo to the given x- and y- coordinates.
virtual bool eventDurationHint(TQDateTime &startDt, TQDateTime &endDt, bool &allDay)
return the default start/end date/time for new events
void deleteSelectedDateTime()
make selected start/end invalid
virtual Incidence::List selectedIncidences()
returns the currently selected events
bool selectedIsAllDay()
returns true if selection is for whole day
Definition: koagendaview.h:145
static void add(TQWidget *widget, Calendar *calendar, Incidence *incidence, const TQDate &date=TQDate(), TQToolTipGroup *group=0, const TQString &longText="")
Base class for single/multi agenda views.
Definition: agendaview.h:28
virtual Calendar * calendar()
Return calendar object of this view.
Definition: baseview.h:89
void editIncidenceSignal(Incidence *, const TQDate &)
instructs the receiver to begin editing the incidence specified in some manner.
void deleteIncidenceSignal(Incidence *)
instructs the receiver to delete the Incidence in some manner; some possibilities include automatical...
void newEventSignal(ResourceCalendar *res, const TQString &subResource)
instructs the receiver to create a new event.
TQString subResourceCalendar() const
Return subResourceCalendar of this view.
Definition: baseview.h:105
void showIncidenceSignal(Incidence *, const TQDate &)
instructs the receiver to show the incidence in read-only mode.
ResourceCalendar * resourceCalendar()
Return resourceCalendar of this view.
Definition: baseview.h:100
This class provides the interface for a date dependent decoration.
virtual TQWidget * smallWidget(TQWidget *, const TQDate &)
Return a small widget.
virtual TQString shortText(const TQDate &)
Return a short text for a given date, ususally only a few words.