Add context, this fordward is different from the fordward when we are in mail context
[kdepim.git] / korganizer / koagendaview.cpp
blob74d07c74229eac623b206e8281b04cc7d373db7e
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>
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.
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.
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 As a special exception, permission is given to link this program
21 with any edition of Qt, and distribute the resulting executable,
22 without including the source code for Qt in the source distribution.
25 #include "koagendaview.h"
26 #include "koglobals.h"
27 #ifndef KORG_NOPLUGINS
28 #include "kocore.h"
29 #include "kodecorationlabel.h"
30 #endif
31 #include "koprefs.h"
32 #include "koagenda.h"
33 #include "koagendaitem.h"
34 #include "kogroupware.h"
35 #include "kodialogmanager.h"
36 #include "koeventpopupmenu.h"
37 #include "koalternatelabel.h"
38 #include "timelabelszone.h"
40 #include <kcal/calendar.h>
41 #include <kcal/icaldrag.h>
42 #include <kcal/dndfactory.h>
43 #include <kcal/calfilter.h>
44 #include <kcal/incidence.h>
45 #include <kcal/incidenceformatter.h>
47 #include <kapplication.h>
48 #include <kcalendarsystem.h>
49 #include <kdebug.h>
50 #include <kstandarddirs.h>
51 #include <kiconloader.h>
52 #include <klocale.h>
53 #include <kconfig.h>
54 #include <kglobal.h>
55 #include <kglobalsettings.h>
56 #include <kholidays.h>
57 #include <kvbox.h>
58 #include <ksystemtimezone.h>
59 #include <kpushbutton.h>
60 #include <kcombobox.h>
62 #include <QLabel>
63 #include <QFrame>
64 #include <QLayout>
65 #include <QSplitter>
66 #include <QFont>
67 #include <QFontMetrics>
68 #include <QMenu>
69 #include <QPainter>
70 #include <QPushButton>
71 #include <QToolButton>
72 #include <QCursor>
73 #include <QBitArray>
74 #include <QPaintEvent>
75 #include <QGridLayout>
76 #include <QBoxLayout>
77 #include <QHBoxLayout>
78 #include <QResizeEvent>
79 #include <QVBoxLayout>
80 #include <QListWidget>
82 using namespace KOrg;
84 EventIndicator::EventIndicator( Location loc, QWidget *parent ) : QFrame( parent )
86 mColumns = 1;
87 mEnabled.resize( mColumns );
88 mLocation = loc;
90 if (mLocation == Top) {
91 mPixmap = KOGlobals::self()->smallIcon( "arrow-up-double" );
92 } else {
93 mPixmap = KOGlobals::self()->smallIcon( "arrow-down-double" );
96 setMinimumHeight(mPixmap.height());
99 EventIndicator::~EventIndicator()
103 void EventIndicator::paintEvent( QPaintEvent *event )
105 QFrame::paintEvent( event );
107 QPainter painter( this );
109 int i;
110 for(i=0;i<mColumns;++i) {
111 if (mEnabled[i]) {
112 int cellWidth = contentsRect().right()/mColumns;
113 int xOffset = KOGlobals::self()->reverseLayout() ?
114 (mColumns - 1 - i)*cellWidth + cellWidth/2 -mPixmap.width()/2 :
115 i*cellWidth + cellWidth/2 -mPixmap.width()/2;
116 painter.drawPixmap(QPoint(xOffset,0),mPixmap);
121 void EventIndicator::changeColumns(int columns)
123 mColumns = columns;
124 mEnabled.resize(mColumns);
126 update();
129 void EventIndicator::enableColumn(int column, bool enable)
131 mEnabled[column] = enable;
134 ////////////////////////////////////////////////////////////////////////////
135 ////////////////////////////////////////////////////////////////////////////
136 ////////////////////////////////////////////////////////////////////////////
138 KOAgendaView::KOAgendaView( Calendar *cal, QWidget *parent, bool isSideBySide ) :
139 KOrg::AgendaView( cal, parent ),
140 mTimeLabelsZone( 0 ),
141 mAllowAgendaUpdate( true ),
142 mUpdateItem( 0 ),
143 mResource( 0 ),
144 mIsSideBySide( isSideBySide )
146 mSelectedDates.append( QDate::currentDate() );
148 mLayoutDayLabels = 0;
149 mDayLabelsFrame = 0;
150 mDayLabels = 0;
151 mLayoutBottomDayLabels = 0;
152 mBottomDayLabelsFrame = 0;
153 mBottomDayLabels = 0;
155 mTopLayout = new QGridLayout( this );
156 mTopLayout->setMargin( 0 );
158 /* Create agenda splitter */
159 mSplitterAgenda = new QSplitter( Qt::Vertical, this );
160 mTopLayout->addWidget( mSplitterAgenda, 1, 0 );
161 mSplitterAgenda->setOpaqueResize( KGlobalSettings::opaqueResize() );
164 /* Create day name labels for agenda columns */
165 mDayLabelsFrame = new KHBox( mSplitterAgenda );
166 mDayLabelsFrame->setSpacing( 2 );
169 /* Create all-day agenda widget */
170 mAllDayFrame = new KHBox( mSplitterAgenda );
171 mAllDayFrame->setSpacing( 2 );
173 // Alignment and description widgets
174 mTimeBarHeaderFrame = new KHBox( mAllDayFrame );
176 // The widget itself
177 mAllDayAgenda = new KOAgenda( 1, mAllDayFrame );
178 QWidget *dummyAllDayRight = new QWidget( mAllDayFrame );
180 // Create the event context menu for the all-day agenda
181 mAllDayAgendaPopup = eventPopup();
184 /* Create the main agenda widget and the related widgets */
185 QWidget *agendaFrame = new QWidget( mSplitterAgenda );
186 mAgendaLayout = new QGridLayout( agendaFrame );
187 mAgendaLayout->setMargin( 0 );
188 mAgendaLayout->setHorizontalSpacing( 2 );
189 mAgendaLayout->setVerticalSpacing( 0 );
190 if ( isSideBySide )
191 mTimeBarHeaderFrame->hide();
193 // Create event indicator bars
194 mEventIndicatorTop = new EventIndicator( EventIndicator::Top, agendaFrame );
195 mAgendaLayout->addWidget( mEventIndicatorTop, 0, 1 );
196 mEventIndicatorBottom = new EventIndicator( EventIndicator::Bottom,
197 agendaFrame );
198 mAgendaLayout->addWidget( mEventIndicatorBottom, 2, 1 );
200 // Alignment and description widgets
201 QWidget *dummyAgendaRight = new QWidget( agendaFrame );
202 mAgendaLayout->addWidget( dummyAgendaRight, 0, 2 );
204 // Create agenda
205 mAgenda = new KOAgenda( 1, 96, KOPrefs::instance()->mHourSize, agendaFrame );
206 mAgendaLayout->addWidget( mAgenda, 1, 1, 1, 2 );
207 mAgendaLayout->setColumnStretch( 1, 1 );
209 // Create time labels
210 mTimeLabelsZone = new TimeLabelsZone( this, mAgenda );
211 mAgendaLayout->addWidget( mTimeLabelsZone, 1, 0 );
213 // Create event context menu for agenda
214 mAgendaPopup = eventPopup();
216 // Scrolling
217 connect( mAgenda,
218 SIGNAL( zoomView(const int, const QPoint &, const Qt::Orientation) ),
219 SLOT( zoomView(const int, const QPoint &, const Qt::Orientation) ) );
221 // Event indicator updates
222 connect( mAgenda, SIGNAL( lowerYChanged(int) ),
223 SLOT( updateEventIndicatorTop(int) ) );
224 connect( mAgenda, SIGNAL( upperYChanged(int) ),
225 SLOT( updateEventIndicatorBottom(int) ) );
226 if ( isSideBySide )
227 mTimeLabelsZone->hide();
230 /* Create a frame at the bottom which may be used by decorations */
231 mBottomDayLabelsFrame = new KHBox( mSplitterAgenda );
232 mBottomDayLabelsFrame->setSpacing( 2 );
234 if ( !isSideBySide ) {
235 /* Make the all-day and normal agendas line up with each other */
236 dummyAllDayRight->setFixedWidth( mAgenda->verticalScrollBar()->width()
237 - mAgendaLayout->horizontalSpacing() );
238 dummyAgendaRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
241 updateTimeBarWidth();
244 /* Update widgets to reflect user preferences */
245 // updateConfig();
246 createDayLabels();
249 /* Connect the agendas */
250 connectAgenda( mAgenda, mAgendaPopup, mAllDayAgenda );
251 connectAgenda( mAllDayAgenda, mAllDayAgendaPopup, mAgenda);
253 connect( mAgenda,
254 SIGNAL( newTimeSpanSignal( const QPoint &, const QPoint & ) ),
255 SLOT( newTimeSpanSelected( const QPoint &, const QPoint & ) ) );
256 connect( mAllDayAgenda,
257 SIGNAL( newTimeSpanSignal( const QPoint &, const QPoint & ) ),
258 SLOT( newTimeSpanSelectedAllDay( const QPoint &, const QPoint & ) ) );
262 KOAgendaView::~KOAgendaView()
264 delete mAgendaPopup;
265 delete mAllDayAgendaPopup;
268 void KOAgendaView::connectAgenda( KOAgenda *agenda, QMenu *popup,
269 KOAgenda *otherAgenda )
271 connect( agenda, SIGNAL( showIncidencePopupSignal( Incidence *, const QDate & ) ),
272 popup, SLOT( showIncidencePopup( Incidence *, const QDate & ) ) );
274 connect( agenda, SIGNAL( showNewEventPopupSignal() ),
275 SLOT( showNewEventPopup() ) );
277 agenda->setCalendar( calendar() );
279 // Create/Show/Edit/Delete Event
280 connect( agenda, SIGNAL( newEventSignal() ), SIGNAL( newEventSignal() ) );
282 connect( agenda, SIGNAL( newStartSelectSignal() ),
283 otherAgenda, SLOT( clearSelection() ) );
284 connect( agenda, SIGNAL( newStartSelectSignal() ),
285 SIGNAL( timeSpanSelectionChanged()) );
287 connect( agenda, SIGNAL( editIncidenceSignal( Incidence * ) ),
288 SIGNAL( editIncidenceSignal( Incidence * ) ) );
289 connect( agenda, SIGNAL( showIncidenceSignal( Incidence * ) ),
290 SIGNAL( showIncidenceSignal( Incidence * ) ) );
291 connect( agenda, SIGNAL( deleteIncidenceSignal( Incidence * ) ),
292 SIGNAL( deleteIncidenceSignal( Incidence * ) ) );
294 connect( agenda, SIGNAL( startMultiModify( const QString & ) ),
295 SIGNAL( startMultiModify( const QString & ) ) );
296 connect( agenda, SIGNAL( endMultiModify() ),
297 SIGNAL( endMultiModify() ) );
299 connect( agenda, SIGNAL( itemModified( KOAgendaItem * ) ),
300 SLOT( updateEventDates( KOAgendaItem * ) ) );
301 connect( agenda, SIGNAL( enableAgendaUpdate( bool ) ),
302 SLOT( enableAgendaUpdate( bool ) ) );
304 // drag signals
305 connect( agenda, SIGNAL( startDragSignal( Incidence * ) ),
306 SLOT( startDrag( Incidence * ) ) );
308 // synchronize selections
309 connect( agenda, SIGNAL( incidenceSelected( Incidence * ) ),
310 otherAgenda, SLOT( deselectItem() ) );
311 connect( agenda, SIGNAL( incidenceSelected( Incidence * ) ),
312 SIGNAL( incidenceSelected( Incidence * ) ) );
314 // rescheduling of todos by d'n'd
315 connect( agenda, SIGNAL( droppedToDo( Todo *, const QPoint &, bool ) ),
316 SLOT( slotTodoDropped( Todo *, const QPoint &, bool ) ) );
320 void KOAgendaView::zoomInVertically( )
322 if ( !mIsSideBySide )
323 KOPrefs::instance()->mHourSize++;
324 mAgenda->updateConfig();
325 mAgenda->checkScrollBoundaries();
327 mTimeLabelsZone->updateAll();
329 updateView();
333 void KOAgendaView::zoomOutVertically( )
336 if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) {
338 if ( !mIsSideBySide )
339 KOPrefs::instance()->mHourSize--;
340 mAgenda->updateConfig();
341 mAgenda->checkScrollBoundaries();
343 mTimeLabelsZone->updateAll();
344 updateView();
348 void KOAgendaView::zoomInHorizontally( const QDate &date)
350 QDate begin;
351 QDate newBegin;
352 QDate dateToZoom = date;
353 int ndays,count;
355 begin = mSelectedDates.first();
356 ndays = begin.daysTo( mSelectedDates.last() );
358 // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it.
359 if ( ! dateToZoom.isValid () )
360 dateToZoom=mAgenda->selectedIncidenceDate();
362 if( !dateToZoom.isValid() ) {
363 if ( ndays > 1 ) {
364 newBegin=begin.addDays(1);
365 count = ndays-1;
366 emit zoomViewHorizontally ( newBegin , count );
368 } else {
369 if ( ndays <= 2 ) {
370 newBegin = dateToZoom;
371 count = 1;
372 } else {
373 newBegin = dateToZoom.addDays( -ndays/2 +1 );
374 count = ndays -1 ;
376 emit zoomViewHorizontally ( newBegin , count );
380 void KOAgendaView::zoomOutHorizontally( const QDate &date )
382 QDate begin;
383 QDate newBegin;
384 QDate dateToZoom = date;
385 int ndays,count;
387 begin = mSelectedDates.first();
388 ndays = begin.daysTo( mSelectedDates.last() );
390 // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it.
391 if ( ! dateToZoom.isValid () )
392 dateToZoom=mAgenda->selectedIncidenceDate();
394 if ( !dateToZoom.isValid() ) {
395 newBegin = begin.addDays(-1);
396 count = ndays+3 ;
397 } else {
398 newBegin = dateToZoom.addDays( -ndays/2-1 );
399 count = ndays+3;
402 if ( abs( count ) >= 31 ) {
403 kDebug() << "change to the month view?";
404 } else {
405 //We want to center the date
406 emit zoomViewHorizontally( newBegin, count );
410 void KOAgendaView::zoomView( const int delta, const QPoint &pos,
411 const Qt::Orientation orient )
413 static QDate zoomDate;
414 static QTimer *t = new QTimer( this );
417 //Zoom to the selected incidence, on the other way
418 // zoom to the date on screen after the first mousewheel move.
419 if ( orient == Qt::Horizontal ) {
420 QDate date=mAgenda->selectedIncidenceDate();
421 if ( date.isValid() )
422 zoomDate=date;
423 else{
424 if ( !t->isActive() ) {
425 zoomDate= mSelectedDates[pos.x()];
427 t->setSingleShot( true );
428 t->start ( 1000 );
430 if ( delta > 0 )
431 zoomOutHorizontally( zoomDate );
432 else
433 zoomInHorizontally( zoomDate );
434 } else {
435 // Vertical zoom
436 QPoint posConstentsOld = mAgenda->gridToContents(pos);
437 if ( delta > 0 ) {
438 zoomOutVertically();
439 } else {
440 zoomInVertically();
442 QPoint posConstentsNew = mAgenda->gridToContents(pos);
443 mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() );
447 void KOAgendaView::createDayLabels()
449 // ### Before deleting and recreating we could check if mSelectedDates changed...
450 // It would remove some flickering and gain speed (since this is called by
451 // each updateView() call)
452 delete mDayLabels;
453 delete mBottomDayLabels;
455 mDayLabels = new QFrame (mDayLabelsFrame);
456 mLayoutDayLabels = new QHBoxLayout(mDayLabels);
457 mLayoutDayLabels->setMargin(0);
458 KVBox *weekLabelBox = new KVBox( mDayLabels );
459 mLayoutDayLabels->addWidget( weekLabelBox );
460 weekLabelBox->setFixedWidth( mTimeLabelsZone->width()
461 - mAgendaLayout->horizontalSpacing() );
462 if ( mIsSideBySide )
463 weekLabelBox->hide();
465 mBottomDayLabels = new QFrame (mBottomDayLabelsFrame);
466 mBottomDayLabelsFrame->setStretchFactor(mBottomDayLabels, 1);
467 mLayoutBottomDayLabels = new QHBoxLayout(mBottomDayLabels);
468 mLayoutBottomDayLabels->setMargin(0);
469 KVBox *bottomWeekLabelBox = new KVBox( mBottomDayLabels );
470 mLayoutBottomDayLabels->addWidget( bottomWeekLabelBox );
471 bottomWeekLabelBox->setFixedWidth( mTimeLabelsZone->width()
472 - mAgendaLayout->horizontalSpacing() );
474 const KCalendarSystem *calsys = KOGlobals::self()->calendarSystem();
476 #ifndef KORG_NOPLUGINS
477 if ( KOPrefs::instance()->decorationsAtAgendaViewTop().count() > 0 ) {
478 mDayLabelsFrame->setParent( mSplitterAgenda );
479 } else {
480 mDayLabelsFrame->setParent( this );
481 mTopLayout->addWidget( mDayLabelsFrame, 0, 0 );
483 if ( KOPrefs::instance()->decorationsAtAgendaViewBottom().count() > 0 ) {
484 mBottomDayLabelsFrame->setParent( mSplitterAgenda );
485 } else {
486 mBottomDayLabelsFrame->setParent( this );
487 mTopLayout->addWidget( mBottomDayLabelsFrame, 0, 0 );
489 #endif
491 DateList::ConstIterator dit;
492 for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
493 QDate date = *dit;
494 KVBox *dayLabelBox = new KVBox( mDayLabels );
495 mLayoutDayLabels->addWidget( dayLabelBox );
496 KVBox *bottomDayLabelBox = new KVBox( mBottomDayLabels );
497 mLayoutBottomDayLabels->addWidget( bottomDayLabelBox );
499 int dW = calsys->dayOfWeek(date);
500 QString veryLongStr = KGlobal::locale()->formatDate( date );
501 QString longstr = i18nc( "short_weekday date (e.g. Mon 13)","%1 %2" ,
502 calsys->weekDayName( dW, KCalendarSystem::ShortDayName ) ,
503 calsys->day(date) );
504 QString shortstr = QString::number(calsys->day(date));
506 KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr,
507 longstr, veryLongStr, dayLabelBox);
508 dayLabel->setMinimumWidth(1);
509 dayLabel->setAlignment(Qt::AlignHCenter);
510 if (date == QDate::currentDate()) {
511 QFont font = dayLabel->font();
512 font.setBold(true);
513 dayLabel->setFont(font);
516 // if a holiday region is selected, show the holiday name
517 QStringList texts = KOGlobals::self()->holiday( date );
518 QStringList::ConstIterator textit = texts.begin();
519 for ( ; textit != texts.end(); ++textit ) {
520 // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
521 KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), QString(), dayLabelBox );
522 label->setMinimumWidth(1);
523 label->setAlignment(Qt::AlignCenter);
526 #ifndef KORG_NOPLUGINS
527 foreach ( QString decoName,
528 KOPrefs::instance()->decorationsAtAgendaViewTop() ) {
529 if ( KOPrefs::instance()->selectedPlugins().contains( decoName ) ) {
530 CalendarDecoration::Decoration* deco
531 = KOCore::self()->loadCalendarDecoration( decoName );
533 CalendarDecoration::Element::List elements;
534 elements = deco->dayElements( date );
535 if ( elements.count() > 0 ) {
536 KHBox *decoHBox = new KHBox( dayLabelBox );
537 decoHBox->setFrameShape( QFrame::StyledPanel );
538 decoHBox->setMinimumWidth( 1 );
540 foreach ( CalendarDecoration::Element* it, elements ) {
541 kDebug() << "adding Element " << it->id()
542 << " of Decoration " << deco->info()
543 << " to the top of the agenda view";
544 KODecorationLabel *label = new KODecorationLabel( it, decoHBox );
545 label->setAlignment( Qt::AlignBottom );
546 label->setMinimumWidth( 1 );
552 foreach ( QString decoName,
553 KOPrefs::instance()->decorationsAtAgendaViewBottom() ) {
554 if ( KOPrefs::instance()->selectedPlugins().contains( decoName ) ) {
555 CalendarDecoration::Decoration* deco
556 = KOCore::self()->loadCalendarDecoration( decoName );
558 CalendarDecoration::Element::List elements;
559 elements = deco->dayElements( date );
560 if ( elements.count() > 0 ) {
561 KHBox *decoHBox = new KHBox( bottomDayLabelBox );
562 decoHBox->setFrameShape( QFrame::StyledPanel );
563 decoHBox->setMinimumWidth( 1 );
565 foreach ( CalendarDecoration::Element* it, elements ) {
566 kDebug() << "adding Element " << it->id()
567 << " of Decoration " << deco->info()
568 << " to the bottom of the agenda view";
569 KODecorationLabel *label = new KODecorationLabel( it, decoHBox );
570 label->setAlignment( Qt::AlignBottom );
571 label->setMinimumWidth( 1 );
576 #endif
579 #ifndef KORG_NOPLUGINS
580 // Week decoration labels
581 foreach ( QString decoName,
582 KOPrefs::instance()->decorationsAtAgendaViewTop() ) {
583 if ( KOPrefs::instance()->selectedPlugins().contains( decoName ) ) {
584 CalendarDecoration::Decoration* deco
585 = KOCore::self()->loadCalendarDecoration( decoName );
587 CalendarDecoration::Element::List elements;
588 elements = deco->weekElements( mSelectedDates.first() );
589 if ( elements.count() > 0 ) {
590 KHBox *decoHBox = new KHBox( weekLabelBox );
591 decoHBox->setFrameShape( QFrame::StyledPanel );
592 decoHBox->setMinimumWidth( 1 );
594 foreach ( CalendarDecoration::Element* it, elements ) {
595 kDebug() << "adding Element " << it->id()
596 << " of Decoration " << deco->info()
597 << " to the week part of the agenda view";
598 KODecorationLabel *label = new KODecorationLabel( it, decoHBox );
599 label->setAlignment( Qt::AlignBottom );
600 label->setMinimumWidth( 1 );
605 foreach ( QString decoName,
606 KOPrefs::instance()->decorationsAtAgendaViewBottom() ) {
607 if ( KOPrefs::instance()->selectedPlugins().contains( decoName ) ) {
608 CalendarDecoration::Decoration* deco
609 = KOCore::self()->loadCalendarDecoration( decoName );
611 CalendarDecoration::Element::List elements;
612 elements = deco->weekElements( mSelectedDates.first() );
613 if ( elements.count() > 0 ) {
614 KHBox *decoHBox = new KHBox( bottomWeekLabelBox );
615 decoHBox->setFrameShape( QFrame::StyledPanel );
616 decoHBox->setMinimumWidth( 1 );
618 foreach ( CalendarDecoration::Element* it, elements ) {
619 kDebug() << "adding Element " << it->id()
620 << " of Decoration " << deco->info()
621 << " to the week part of the agenda view";
622 KODecorationLabel *label = new KODecorationLabel( it, decoHBox );
623 label->setAlignment( Qt::AlignBottom );
624 label->setMinimumWidth( 1 );
629 #endif
631 if ( !mIsSideBySide ) {
632 mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width());
633 mLayoutBottomDayLabels->addSpacing(mAgenda->verticalScrollBar()->width());
635 mDayLabels->show();
636 mBottomDayLabels->show();
639 void KOAgendaView::enableAgendaUpdate( bool enable )
641 mAllowAgendaUpdate = enable;
644 int KOAgendaView::maxDatesHint()
646 // Not sure about the max number of events, so return 0 for now.
647 return 0;
650 int KOAgendaView::currentDateCount()
652 return mSelectedDates.count();
655 Incidence::List KOAgendaView::selectedIncidences()
657 Incidence::List selected;
658 Incidence *incidence;
660 incidence = mAgenda->selectedIncidence();
661 if (incidence) selected.append(incidence);
663 incidence = mAllDayAgenda->selectedIncidence();
664 if (incidence) selected.append(incidence);
666 return selected;
669 DateList KOAgendaView::selectedDates()
671 DateList selected;
672 QDate qd;
674 qd = mAgenda->selectedIncidenceDate();
675 if (qd.isValid()) selected.append(qd);
677 qd = mAllDayAgenda->selectedIncidenceDate();
678 if (qd.isValid()) selected.append(qd);
680 return selected;
683 bool KOAgendaView::eventDurationHint( QDateTime &startDt, QDateTime &endDt,
684 bool &allDay )
686 if ( selectionStart().isValid() ) {
687 QDateTime start = selectionStart();
688 QDateTime end = selectionEnd();
690 if ( start.secsTo( end ) == 15*60 ) {
691 // One cell in the agenda view selected, e.g.
692 // because of a double-click, => Use the default duration
693 QTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() );
694 int addSecs = ( defaultDuration.hour()*3600 ) +
695 ( defaultDuration.minute()*60 );
696 end = start.addSecs( addSecs );
699 startDt = start;
700 endDt = end;
701 allDay = selectedIsAllDay();
702 return true;
704 return false;
707 /** returns if only a single cell is selected, or a range of cells */
708 bool KOAgendaView::selectedIsSingleCell()
710 if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false;
712 if (selectedIsAllDay()) {
713 int days = selectionStart().daysTo(selectionEnd());
714 return ( days < 1 );
715 } else {
716 int secs = selectionStart().secsTo(selectionEnd());
717 return ( secs <= 24*60*60/mAgenda->rows() );
722 void KOAgendaView::updateView()
724 fillAgenda();
729 Update configuration settings for the agenda view. This method is not
730 complete.
732 void KOAgendaView::updateConfig()
734 mAgenda->updateConfig();
735 mAllDayAgenda->updateConfig();
737 mTimeLabelsZone->updateAll();
739 updateTimeBarWidth();
741 setHolidayMasks();
743 createDayLabels();
745 updateView();
748 void KOAgendaView::createTimeBarHeaders()
750 qDeleteAll( mTimeBarHeaders );
751 mTimeBarHeaders.clear();
753 foreach( TimeLabels *timeLabel, mTimeLabelsZone->timeLabels() ) {
754 QLabel *label = new QLabel( i18n("All Day"), mTimeBarHeaderFrame );
755 label->setText( timeLabel->header() );
756 label->setAlignment( Qt::AlignBottom | Qt::AlignHCenter );
757 label->setWordWrap( true );
758 label->setToolTip( timeLabel->headerToolTip() );
759 mTimeBarHeaders.append( label );
763 void KOAgendaView::updateTimeBarWidth()
765 createTimeBarHeaders();
767 int width = mTimeLabelsZone->timeLabelsWidth();
769 mTimeBarHeaderFrame->setFixedWidth( width );
770 mTimeLabelsZone->setTimeLabelsWidth( width );
774 void KOAgendaView::updateEventDates( KOAgendaItem *item )
776 kDebug() << item->text();
778 KDateTime startDt,endDt;
780 // Start date of this incidence, calculate the offset from it (so recurring and
781 // non-recurring items can be treated exactly the same, we never need to check
782 // for recurs(), because we only move the start day by the number of days the
783 // agenda item was really moved. Smart, isn't it?)
784 QDate thisDate;
785 if ( item->cellXLeft() < 0 ) {
786 thisDate = ( mSelectedDates.first() ).addDays( item->cellXLeft() );
787 } else {
788 thisDate = mSelectedDates[ item->cellXLeft() ];
790 QDate oldThisDate( item->itemDate() );
791 int daysOffset = oldThisDate.daysTo( thisDate );
792 int daysLength = 0;
793 // startDt.setDate( startDate );
795 Incidence *incidence = item->incidence();
796 if ( !incidence ) return;
797 if ( !mChanger || !mChanger->beginChange(incidence) ) return;
798 Incidence *oldIncidence = incidence->clone();
800 QTime startTime(0,0,0), endTime(0,0,0);
801 if ( incidence->allDay() ) {
802 daysLength = item->cellWidth() - 1;
803 } else {
804 startTime = mAgenda->gyToTime( item->cellYTop() );
805 if ( item->lastMultiItem() ) {
806 endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 );
807 daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft();
808 } else {
809 endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
813 // FIXME: use a visitor here
814 if ( incidence->type() == "Event" ) {
815 startDt = incidence->dtStart();
816 // convert to calendar timespec because we then manipulate it with time coming from
817 // the calendar
818 startDt = startDt.toTimeSpec( KOPrefs::instance()->timeSpec() );
819 startDt = startDt.addDays( daysOffset );
820 if ( !startDt.isDateOnly() )
821 startDt.setTime( startTime );
822 endDt = startDt.addDays( daysLength );
823 if ( !endDt.isDateOnly() )
824 endDt.setTime( endTime );
825 Event*ev = static_cast<Event*>(incidence);
826 if( incidence->dtStart().toTimeSpec( KOPrefs::instance()->timeSpec() ) == startDt
827 && ev->dtEnd().toTimeSpec( KOPrefs::instance()->timeSpec() ) == endDt ) {
828 // No change
829 delete oldIncidence;
830 return;
832 } else if ( incidence->type() == "Todo" ) {
833 Todo *td = static_cast<Todo*>(incidence);
835 startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
836 // convert to calendar timespec because we then manipulate it with time coming from
837 // the calendar
838 startDt = startDt.toTimeSpec( KOPrefs::instance()->timeSpec() );
839 startDt.setDate( thisDate.addDays( td->dtDue().daysTo( startDt ) ) );
840 if ( !startDt.isDateOnly() ) {
841 startDt.setTime( startTime );
844 endDt = startDt;
845 endDt.setDate( thisDate );
846 if ( !endDt.isDateOnly() )
847 endDt.setTime( endTime );
849 if( td->dtDue().toTimeSpec( KOPrefs::instance()->timeSpec() ) == endDt ) {
850 // No change
851 delete oldIncidence;
852 return;
855 // FIXME: Adjusting the recurrence should really go to CalendarView so this
856 // functionality will also be available in other views!
857 // TODO_Recurrence: This does not belong here, and I'm not really sure
858 // how it's supposed to work anyway.
859 /* Recurrence *recur = incidence->recurrence();
860 if ( recur->recurs() && daysOffset != 0 ) {
861 switch ( recur->recurrenceType() ) {
862 case Recurrence::rYearlyPos: {
863 int freq = recur->frequency();
864 int duration = recur->duration();
865 QDate endDt( recur->endDate() );
866 bool negative = false;
868 QPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() );
869 if ( monthPos.first() ) {
870 negative = monthPos.first()->negative;
872 QBitArray days( 7 );
873 int pos = 0;
874 days.fill( false );
875 days.setBit( thisDate.dayOfWeek() - 1 );
876 if ( negative ) {
877 pos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
878 } else {
879 pos = ( thisDate.day()-1 ) / 7 + 1;
881 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
882 recur->unsetRecurs();
883 if ( duration != 0 ) {
884 recur->setYearly( Recurrence::rYearlyPos, freq, duration );
885 } else {
886 recur->setYearly( Recurrence::rYearlyPos, freq, endDt );
888 recur->addYearlyMonthPos( pos, days );
889 recur->addYearlyNum( thisDate.month() );
891 break; }
892 case Recurrence::rYearlyDay: {
893 int freq = recur->frequency();
894 int duration = recur->duration();
895 QDate endDt( recur->endDate() );
896 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
897 recur->unsetRecurs();
898 if ( duration == 0 ) { // end by date
899 recur->setYearly( Recurrence::rYearlyDay, freq, endDt );
900 } else {
901 recur->setYearly( Recurrence::rYearlyDay, freq, duration );
903 recur->addYearlyNum( thisDate.dayOfYear() );
904 break; }
905 case Recurrence::rYearlyMonth: {
906 int freq = recur->frequency();
907 int duration = recur->duration();
908 QDate endDt( recur->endDate() );
909 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
910 recur->unsetRecurs();
911 if ( duration != 0 ) {
912 recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration );
913 } else {
914 recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt );
916 recur->addYearlyNum( thisDate.month() );
917 break; }
918 case Recurrence::rMonthlyPos: {
919 int freq = recur->frequency();
920 int duration = recur->duration();
921 QDate endDt( recur->endDate() );
922 QPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() );
923 if ( !monthPos.isEmpty() ) {
924 // FIXME: How shall I adapt the day x of week Y if we move the date across month borders???
925 // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
926 // That's fine for korganizer, but might mess up other organizers.
927 QBitArray rDays( 7 );
928 rDays = monthPos.first()->rDays;
929 bool negative = monthPos.first()->negative;
930 int newPos;
931 rDays.fill( false );
932 rDays.setBit( thisDate.dayOfWeek() - 1 );
933 if ( negative ) {
934 newPos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
935 } else {
936 newPos = ( thisDate.day()-1 ) / 7 + 1;
939 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
940 recur->unsetRecurs();
941 if ( duration == 0 ) { // end by date
942 recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt );
943 } else {
944 recur->setMonthly( Recurrence::rMonthlyPos, freq, duration );
946 recur->addMonthlyPos( newPos, rDays );
948 break;}
949 case Recurrence::rMonthlyDay: {
950 int freq = recur->frequency();
951 int duration = recur->duration();
952 QDate endDt( recur->endDate() );
953 QPtrList<int> monthDays( recur->monthDays() );
954 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
955 recur->unsetRecurs();
956 if ( duration == 0 ) { // end by date
957 recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt );
958 } else {
959 recur->setMonthly( Recurrence::rMonthlyDay, freq, duration );
961 // FIXME: How shall I adapt the n-th day if we move the date across month borders???
962 // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
963 // That's fine for korganizer, but might mess up other organizers.
964 recur->addMonthlyDay( thisDate.day() );
966 break;}
967 case Recurrence::rWeekly: {
968 QBitArray days(7), oldDays( recur->days() );
969 int offset = daysOffset % 7;
970 if ( offset<0 ) offset = (offset+7) % 7;
971 // rotate the days
972 for (int d=0; d<7; d++ ) {
973 days.setBit( (d+offset) % 7, oldDays.at(d) );
975 if ( recur->duration() == 0 ) { // end by date
976 recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() );
977 } else { // duration or no end
978 recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() );
980 break;}
981 // nothing to be done for the following:
982 case Recurrence::rDaily:
983 case Recurrence::rHourly:
984 case Recurrence::rMinutely:
985 case Recurrence::rNone:
986 default:
987 break;
989 if ( recur->duration()==0 ) { // end by date
990 recur->setEndDate( recur->endDate().addDays( daysOffset ) );
992 KMessageBox::information( this, i18n("A recurring calendar item was moved "
993 "to a different day. The recurrence settings "
994 "have been updated with that move. Please check "
995 "them in the editor."),
996 i18n("Recurrence Moved"),
997 "RecurrenceMoveInAgendaWarning" );
1000 // FIXME: use a visitor here
1001 if ( incidence->type() == "Event" ) {
1002 incidence->setDtStart( startDt.toTimeSpec( incidence->dtStart().timeSpec() ) );
1003 (static_cast<Event*>( incidence ) )->setDtEnd( endDt.toTimeSpec( incidence->dtEnd().timeSpec() ) );
1004 } else if ( incidence->type() == "Todo" ) {
1005 Todo *td = static_cast<Todo*>( incidence );
1006 if ( td->hasStartDate() )
1007 td->setDtStart( startDt.toTimeSpec( incidence->dtStart().timeSpec() ) );
1008 td->setDtDue( endDt.toTimeSpec( td->dtDue().timeSpec() ) );
1010 item->setItemDate( startDt.toTimeSpec( KOPrefs::instance()->timeSpec() ).date() );
1012 item->setToolTip( IncidenceFormatter::toolTipString( incidence ));
1014 mChanger->changeIncidence( oldIncidence, incidence );
1015 mChanger->endChange(incidence);
1016 delete oldIncidence;
1018 // don't update the agenda as the item already has the correct coordinates.
1019 // an update would delete the current item and recreate it, but we are still
1020 // using a pointer to that item! => CRASH
1021 enableAgendaUpdate( false );
1022 // We need to do this in a timer to make sure we are not deleting the item
1023 // we are currently working on, which would lead to crashes
1024 // Only the actually moved agenda item is already at the correct position and mustn't be
1025 // recreated. All others have to!!!
1026 if ( incidence->recurs() ) {
1027 mUpdateItem = incidence;
1028 QTimer::singleShot( 0, this, SLOT( doUpdateItem() ) );
1031 enableAgendaUpdate( true );
1034 void KOAgendaView::doUpdateItem()
1036 if ( mUpdateItem ) {
1037 changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED );
1038 mUpdateItem = 0;
1042 void KOAgendaView::showDates( const QDate &start, const QDate &end )
1044 mSelectedDates.clear();
1046 QDate d = start;
1047 while (d <= end) {
1048 mSelectedDates.append(d);
1049 d = d.addDays( 1 );
1052 // and update the view
1053 fillAgenda();
1057 void KOAgendaView::showIncidences( const Incidence::List &incidences )
1059 // we must check if they are not filtered; if they are, remove the filter
1060 CalFilter *filter = calendar()->filter();
1061 bool wehaveall = true;
1062 if ( filter )
1063 for ( Incidence::List::ConstIterator it = incidences.constBegin();
1064 it != incidences.constEnd(); ++it )
1065 if ( !( wehaveall = filter->filterIncidence( *it ) ) )
1066 break;
1068 if ( !wehaveall )
1069 calendar()->setFilter( 0 );
1071 KDateTime start = incidences.first()->dtStart().toTimeSpec( KOPrefs::instance()->timeSpec() ),
1072 end = incidences.first()->dtEnd().toTimeSpec( KOPrefs::instance()->timeSpec() );
1073 Incidence *first = incidences.first();
1074 for ( Incidence::List::ConstIterator it = incidences.constBegin();
1075 it != incidences.constEnd(); ++it ) {
1076 if ( ( *it )->dtStart().toTimeSpec( KOPrefs::instance()->timeSpec() ) < start )
1077 first = *it;
1078 start = qMin( start, ( *it )->dtStart().toTimeSpec( KOPrefs::instance()->timeSpec() ) );
1079 end = qMax( start, ( *it )->dtEnd().toTimeSpec( KOPrefs::instance()->timeSpec() ) );
1082 end.toTimeSpec( start ); // allow direct comparison of dates
1083 if ( start.date().daysTo( end.date() ) + 1 <= currentDateCount() )
1084 showDates( start.date(), end.date() );
1085 else
1086 showDates( start.date(), start.date().addDays( currentDateCount() - 1 ) );
1088 mAgenda->selectItemByUID( first->uid() );
1091 void KOAgendaView::insertIncidence( Incidence *incidence, const QDate &curDate,
1092 int curCol )
1094 if ( !filterByResource( incidence ) )
1095 return;
1097 // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's
1098 Event *event = dynamic_cast<Event *>( incidence );
1099 Todo *todo = dynamic_cast<Todo *>( incidence );
1101 if ( curCol < 0 ) {
1102 curCol = mSelectedDates.indexOf( curDate );
1104 // The date for the event is not displayed, just ignore it
1105 if ( curCol < 0 || curCol > int( mSelectedDates.size() ) )
1106 return;
1108 int beginX;
1109 int endX;
1110 if ( event ) {
1111 beginX = curDate.daysTo( incidence->dtStart().toTimeSpec( KOPrefs::instance()->timeSpec() ).date() ) + curCol;
1112 endX = curDate.daysTo( event->dtEnd().toTimeSpec( KOPrefs::instance()->timeSpec() ).date() ) + curCol;
1113 } else if ( todo ) {
1114 if ( ! todo->hasDueDate() ) return; // todo shall not be displayed if it has no date
1115 beginX = curDate.daysTo( todo->dtDue().date() ) + curCol;
1116 endX = beginX;
1117 } else {
1118 return;
1120 if ( todo && todo->isOverdue() ) {
1121 mAllDayAgenda->insertAllDayItem( incidence, curDate, curCol, curCol );
1122 } else if ( incidence->allDay() ) {
1123 // FIXME: This breaks with recurring multi-day events!
1124 if ( incidence->recurrence()->recurs() ) {
1125 mAllDayAgenda->insertAllDayItem( incidence, curDate, curCol, curCol );
1126 } else {
1127 // Insert multi-day events only on the first day, otherwise it will
1128 // appear multiple times
1129 if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) {
1130 mAllDayAgenda->insertAllDayItem( incidence, curDate, beginX, endX );
1133 } else if ( event && event->isMultiDay( KOPrefs::instance()->timeSpec() ) ) {
1134 int startY = mAgenda->timeToY( event->dtStart().toTimeSpec( KOPrefs::instance()->timeSpec() ).time() );
1135 QTime endtime( event->dtEnd().toTimeSpec( KOPrefs::instance()->timeSpec() ).time() );
1136 if ( endtime == QTime( 0, 0, 0 ) ) endtime = QTime( 23, 59, 59 );
1137 int endY = mAgenda->timeToY( endtime ) - 1;
1138 if ( (beginX <= 0 && curCol == 0) || beginX == curCol ) {
1139 mAgenda->insertMultiItem( event, curDate, beginX, endX, startY, endY );
1141 if ( beginX == curCol ) {
1142 mMaxY[curCol] = mAgenda->timeToY( QTime(23,59) );
1143 if ( startY < mMinY[curCol] ) mMinY[curCol] = startY;
1144 } else if ( endX == curCol ) {
1145 mMinY[curCol] = mAgenda->timeToY( QTime(0,0) );
1146 if ( endY > mMaxY[curCol] ) mMaxY[curCol] = endY;
1147 } else {
1148 mMinY[curCol] = mAgenda->timeToY( QTime(0,0) );
1149 mMaxY[curCol] = mAgenda->timeToY( QTime(23,59) );
1151 } else {
1152 int startY = 0, endY = 0;
1153 if ( event ) {
1154 startY = mAgenda->timeToY( incidence->dtStart().toTimeSpec( KOPrefs::instance()->timeSpec() ).time() );
1155 QTime endtime( event->dtEnd().toTimeSpec( KOPrefs::instance()->timeSpec() ).time() );
1156 if ( endtime == QTime( 0, 0, 0 ) ) endtime = QTime( 23, 59, 59 );
1157 endY = mAgenda->timeToY( endtime ) - 1;
1159 if ( todo ) {
1160 QTime t = todo->dtDue().toTimeSpec( KOPrefs::instance()->timeSpec() ).time();
1161 endY = mAgenda->timeToY( t ) - 1;
1162 startY = mAgenda->timeToY( t.addSecs( -1800 ) );
1164 if ( endY < startY ) endY = startY;
1165 mAgenda->insertItem( incidence, curDate, curCol, startY, endY );
1166 if ( startY < mMinY[curCol] ) mMinY[curCol] = startY;
1167 if ( endY > mMaxY[curCol] ) mMaxY[curCol] = endY;
1171 void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence )
1173 Todo *todo = dynamic_cast<Todo *>(incidence);
1174 CalFilter *filter = calendar()->filter();
1175 if ( filter && !filter->filterIncidence( incidence ) ||
1176 ( todo && !KOPrefs::instance()->showAllDayTodo() ) )
1177 return;
1179 QDate f = mSelectedDates.first();
1180 QDate l = mSelectedDates.last();
1182 QDate startDt = incidence->dtStart().toTimeSpec( KOPrefs::instance()->timeSpec() ).date();
1184 if ( incidence->recurs() ) {
1185 DateList::ConstIterator dit;
1186 QDate curDate;
1187 for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
1188 curDate = *dit;
1189 // FIXME: This breaks with recurring multi-day events!
1190 if ( incidence->recursOn( curDate, KOPrefs::instance()->timeSpec() ) ) {
1191 insertIncidence( incidence, curDate );
1194 return;
1197 QDate endDt;
1198 if ( incidence->type() == "Event" )
1199 endDt = (static_cast<Event *>(incidence))->dtEnd().toTimeSpec( KOPrefs::instance()->timeSpec() ).date();
1200 if ( todo ) {
1201 endDt = todo->isOverdue() ? QDate::currentDate()
1202 : todo->dtDue().date();
1204 if ( endDt >= f && endDt <= l ) {
1205 insertIncidence( incidence, endDt );
1206 return;
1209 if ( startDt >= f && startDt <= l ) {
1210 insertIncidence( incidence, startDt );
1214 void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode )
1216 switch ( mode ) {
1217 case KOGlobals::INCIDENCEADDED: {
1218 // Add an event. No need to recreate the whole view!
1219 // recreating everything even causes troubles: dropping to the day matrix
1220 // recreates the agenda items, but the evaluation is still in an agendaItems' code,
1221 // which was deleted in the mean time. Thus KOrg crashes...
1222 changeIncidenceDisplayAdded( incidence );
1223 break;
1225 case KOGlobals::INCIDENCEEDITED: {
1226 if ( !mAllowAgendaUpdate ) {
1227 updateEventIndicators();
1228 } else {
1229 removeIncidence( incidence );
1230 updateEventIndicators();
1231 changeIncidenceDisplayAdded( incidence );
1233 break;
1235 case KOGlobals::INCIDENCEDELETED: {
1236 mAgenda->removeIncidence( incidence );
1237 mAllDayAgenda->removeIncidence( incidence );
1238 updateEventIndicators();
1239 break;
1241 default:
1242 updateView();
1246 void KOAgendaView::fillAgenda( const QDate & )
1248 fillAgenda();
1251 void KOAgendaView::fillAgenda()
1253 /* Remember the uids of the selected items. In case one of the
1254 * items was deleted and re-added, we want to reselect it. */
1255 const QString &selectedAgendaUid = mAgenda->lastSelectedUid();
1256 const QString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
1258 enableAgendaUpdate( true );
1259 clearView();
1261 mAllDayAgenda->changeColumns(mSelectedDates.count());
1262 mAgenda->changeColumns(mSelectedDates.count());
1263 mEventIndicatorTop->changeColumns(mSelectedDates.count());
1264 mEventIndicatorBottom->changeColumns(mSelectedDates.count());
1266 createDayLabels();
1267 setHolidayMasks();
1269 mMinY.resize(mSelectedDates.count());
1270 mMaxY.resize(mSelectedDates.count());
1272 Event::List dayEvents;
1274 // ToDo items shall be displayed for the day they are due, but only shown today if they are already overdue.
1275 // Therefore, get all of them.
1276 Todo::List todos = calendar()->todos();
1278 mAgenda->setDateList(mSelectedDates);
1280 QDate today = QDate::currentDate();
1282 bool somethingReselected = false;
1283 DateList::ConstIterator dit;
1284 int curCol = 0;
1285 for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
1286 QDate currentDate = *dit;
1287 dayEvents = calendar()->events(currentDate, KOPrefs::instance()->timeSpec(),
1288 EventSortStartDate,
1289 SortDirectionAscending);
1291 // Default values, which can never be reached
1292 mMinY[curCol] = mAgenda->timeToY(QTime(23,59)) + 1;
1293 mMaxY[curCol] = mAgenda->timeToY(QTime(0,0)) - 1;
1295 int numEvent;
1296 for(numEvent=0;numEvent<dayEvents.count();++numEvent) {
1297 Event *event = dayEvents.at(numEvent);
1298 insertIncidence( event, currentDate, curCol );
1299 if( event->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
1300 mAgenda->selectItemByUID( event->uid() );
1301 somethingReselected = true;
1303 if( event->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
1304 mAllDayAgenda->selectItemByUID( event->uid() );
1305 somethingReselected = true;
1310 // ---------- [display Todos --------------
1311 if ( KOPrefs::instance()->showAllDayTodo() ) {
1312 int numTodo;
1313 for (numTodo = 0; numTodo < todos.count(); ++numTodo) {
1314 Todo *todo = todos.at(numTodo);
1316 if ( ! todo->hasDueDate() ) continue; // todo shall not be displayed if it has no date
1318 // ToDo items shall be displayed for the day they are due, but only showed today if they are already overdue.
1319 // Already completed items can be displayed on their original due date
1320 bool overdue = todo->isOverdue();
1322 if ( (( todo->dtDue().date() == currentDate) && !overdue) ||
1323 (( currentDate == today) && overdue) ||
1324 ( todo->recursOn( currentDate, KOPrefs::instance()->timeSpec() ) ) ) {
1325 if ( todo->allDay() || overdue ) { // Todo has no due-time set or is already overdue
1326 mAllDayAgenda->insertAllDayItem(todo, currentDate, curCol, curCol);
1327 } else {
1328 int endY = mAgenda->timeToY(todo->dtDue().toTimeSpec( KOPrefs::instance()->timeSpec() ).time()) - 1;
1329 int startY = endY - 1;
1331 mAgenda->insertItem(todo,currentDate,curCol,startY,endY);
1333 if (startY < mMinY[curCol]) mMinY[curCol] = startY;
1334 if (endY > mMaxY[curCol]) mMaxY[curCol] = endY;
1339 // ---------- display Todos] --------------
1341 ++curCol;
1344 mAgenda->checkScrollBoundaries();
1345 updateEventIndicators();
1347 // mAgenda->viewport()->update();
1348 // mAllDayAgenda->viewport()->update();
1350 // make invalid
1351 deleteSelectedDateTime();
1353 if( !somethingReselected ) {
1354 emit incidenceSelected( 0 );
1358 void KOAgendaView::clearView()
1360 mAllDayAgenda->clear();
1361 mAgenda->clear();
1364 CalPrinter::PrintType KOAgendaView::printType()
1366 if ( currentDateCount() == 1 ) return CalPrinter::Day;
1367 else return CalPrinter::Week;
1370 void KOAgendaView::updateEventIndicatorTop( int newY )
1372 for(int i = 0; i < mMinY.size(); ++i ) {
1373 mEventIndicatorTop->enableColumn( i, newY >= mMinY[i] );
1375 mEventIndicatorTop->update();
1378 void KOAgendaView::updateEventIndicatorBottom( int newY )
1380 for(int i = 0; i < mMaxY.size(); ++i ) {
1381 mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
1383 mEventIndicatorBottom->update();
1386 void KOAgendaView::slotTodoDropped( Todo *todo, const QPoint &gpos, bool allDay )
1388 if ( gpos.x()<0 || gpos.y()<0 ) return;
1389 QDate day = mSelectedDates[gpos.x()];
1390 QTime time = mAgenda->gyToTime( gpos.y() );
1391 KDateTime newTime( day, time, KOPrefs::instance()->timeSpec() );
1392 newTime.setDateOnly( allDay );
1394 if ( todo ) {
1395 Todo *existingTodo = calendar()->todo( todo->uid() );
1396 if ( existingTodo ) {
1397 kDebug() << "Drop existing Todo";
1398 Todo *oldTodo = existingTodo->clone();
1399 if ( mChanger && mChanger->beginChange( existingTodo ) ) {
1400 existingTodo->setDtDue( newTime );
1401 existingTodo->setAllDay( allDay );
1402 existingTodo->setHasDueDate( true );
1403 mChanger->changeIncidence( oldTodo, existingTodo );
1404 mChanger->endChange( existingTodo );
1405 } else {
1406 KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
1407 "because it cannot be locked.") );
1409 delete oldTodo;
1410 } else {
1411 kDebug() << "Drop new Todo";
1412 todo->setDtDue( newTime );
1413 todo->setAllDay( allDay );
1414 todo->setHasDueDate( true );
1415 if ( !mChanger->addIncidence( todo, this ) ) {
1416 KODialogManager::errorSaveIncidence( this, todo );
1422 void KOAgendaView::startDrag( Incidence *incidence )
1424 #ifndef KORG_NODND
1425 DndFactory factory( calendar() );
1426 QDrag *drag = factory.createDrag( incidence, this );
1427 if ( drag->start() ) {
1428 kDebug() << "Delete drag source";
1430 #endif
1433 void KOAgendaView::readSettings()
1435 readSettings(KOGlobals::self()->config());
1438 void KOAgendaView::readSettings( KConfig *config )
1440 KConfigGroup group = config->group( "Views" );
1442 QList<int> sizes = group.readEntry( "Separator AgendaView", QList<int>() );
1443 if ( sizes.count() == 4 ) { // Number of sections divided by separators in the view
1444 mSplitterAgenda->setSizes( sizes );
1447 updateConfig();
1450 void KOAgendaView::writeSettings( KConfig *config )
1452 KConfigGroup group = config->group( "Views" );
1454 QList<int> list = mSplitterAgenda->sizes();
1455 group.writeEntry( "Separator AgendaView", list );
1458 void KOAgendaView::setHolidayMasks()
1460 mHolidayMask.resize( mSelectedDates.count() + 1 );
1462 for( int i = 0; i < mSelectedDates.count(); ++i ) {
1463 mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
1466 // Store the information about the day before the visible area (needed for
1467 // overnight working hours) in the last bit of the mask:
1468 bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
1469 mHolidayMask[ mSelectedDates.count() ] = showDay;
1471 mAgenda->setHolidayMask( &mHolidayMask );
1472 mAllDayAgenda->setHolidayMask( &mHolidayMask );
1475 void KOAgendaView::setContentsPos( int y )
1477 if ( y != mAgenda->contentsY() )
1478 mAgenda->setContentsPos( 0, y );
1481 void KOAgendaView::clearSelection()
1483 mAgenda->deselectItem();
1484 mAllDayAgenda->deselectItem();
1487 void KOAgendaView::newTimeSpanSelectedAllDay( const QPoint &start, const QPoint &end )
1489 newTimeSpanSelected( start, end );
1490 mTimeSpanInAllDay = true;
1493 void KOAgendaView::newTimeSpanSelected( const QPoint &start, const QPoint &end )
1495 if (!mSelectedDates.count()) return;
1497 mTimeSpanInAllDay = false;
1499 QDate dayStart = mSelectedDates[start.x()];
1500 QDate dayEnd = mSelectedDates[end.x()];
1502 QTime timeStart = mAgenda->gyToTime(start.y());
1503 QTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
1505 QDateTime dtStart(dayStart,timeStart);
1506 QDateTime dtEnd(dayEnd,timeEnd);
1508 mTimeSpanBegin = dtStart;
1509 mTimeSpanEnd = dtEnd;
1512 void KOAgendaView::deleteSelectedDateTime()
1514 mTimeSpanBegin.setDate(QDate());
1515 mTimeSpanEnd.setDate(QDate());
1516 mTimeSpanInAllDay = false;
1519 void KOAgendaView::setTypeAheadReceiver( QObject *o )
1521 mAgenda->setTypeAheadReceiver( o );
1522 mAllDayAgenda->setTypeAheadReceiver( o );
1525 void KOAgendaView::finishTypeAhead()
1527 mAgenda->finishTypeAhead();
1528 mAllDayAgenda->finishTypeAhead();
1531 void KOAgendaView::removeIncidence( Incidence *incidence )
1533 mAgenda->removeIncidence( incidence );
1534 mAllDayAgenda->removeIncidence( incidence );
1537 void KOAgendaView::updateEventIndicators()
1539 mMinY = mAgenda->minContentsY();
1540 mMaxY = mAgenda->maxContentsY();
1542 mAgenda->checkScrollBoundaries();
1543 updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
1544 updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
1547 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
1549 mChanger = changer;
1550 mAgenda->setIncidenceChanger( changer );
1551 mAllDayAgenda->setIncidenceChanger( changer );
1554 void KOAgendaView::clearTimeSpanSelection()
1556 mAgenda->clearSelection();
1557 mAllDayAgenda->clearSelection();
1558 deleteSelectedDateTime();
1561 void KOAgendaView::setResource(KCal::ResourceCalendar * res, const QString & subResource)
1563 mResource = res;
1564 mSubResource = subResource;
1567 bool KOAgendaView::filterByResource(Incidence * incidence)
1569 if ( !mResource )
1570 return true;
1571 CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() );
1572 if ( !calRes )
1573 return true;
1574 if ( calRes->resource( incidence ) != mResource )
1575 return false;
1576 if ( !mSubResource.isEmpty() ) {
1577 if ( mResource->subresourceIdentifier( incidence ) != mSubResource )
1578 return false;
1580 return true;
1583 #include "koagendaview.moc"