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