korganizer

koagendaview.cpp

00001 /*
00002     This file is part of KOrganizer.
00003     Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00004     Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 
00020     As a special exception, permission is given to link this program
00021     with any edition of Qt, and distribute the resulting executable,
00022     without including the source code for Qt in the source distribution.
00023 */
00024 
00025 #include <qhbox.h>
00026 #include <qvbox.h>
00027 #include <qlabel.h>
00028 #include <qframe.h>
00029 #include <qlayout.h>
00030 #ifndef KORG_NOSPLITTER
00031 #include <qsplitter.h>
00032 #endif
00033 #include <qfont.h>
00034 #include <qfontmetrics.h>
00035 #include <qpopupmenu.h>
00036 #include <qtooltip.h>
00037 #include <qpainter.h>
00038 #include <qpushbutton.h>
00039 #include <qcursor.h>
00040 #include <qbitarray.h>
00041 
00042 #include <kapplication.h>
00043 #include <kdebug.h>
00044 #include <kstandarddirs.h>
00045 #include <kiconloader.h>
00046 #include <klocale.h>
00047 #include <kconfig.h>
00048 #include <kglobal.h>
00049 #include <kglobalsettings.h>
00050 #include <kholidays.h>
00051 
00052 #include <libkcal/calendar.h>
00053 #include <libkcal/icaldrag.h>
00054 #include <libkcal/dndfactory.h>
00055 #include <libkcal/calfilter.h>
00056 
00057 #include <kcalendarsystem.h>
00058 
00059 #include "koglobals.h"
00060 #ifndef KORG_NOPLUGINS
00061 #include "kocore.h"
00062 #endif
00063 #include "koprefs.h"
00064 #include "koagenda.h"
00065 #include "koagendaitem.h"
00066 #include "timelabels.h"
00067 
00068 #include "koincidencetooltip.h"
00069 #include "kogroupware.h"
00070 #include "kodialogmanager.h"
00071 #include "koeventpopupmenu.h"
00072 
00073 #include "koagendaview.h"
00074 #include "koagendaview.moc"
00075 
00076 using namespace KOrg;
00077 
00078 
00079 EventIndicator::EventIndicator(Location loc,QWidget *parent,const char *name)
00080   : QFrame(parent,name)
00081 {
00082   mColumns = 1;
00083   mEnabled.resize( mColumns );
00084   mLocation = loc;
00085 
00086   if (mLocation == Top) mPixmap = KOGlobals::self()->smallIcon("upindicator");
00087   else mPixmap = KOGlobals::self()->smallIcon("downindicator");
00088 
00089   setMinimumHeight(mPixmap.height());
00090 }
00091 
00092 EventIndicator::~EventIndicator()
00093 {
00094 }
00095 
00096 void EventIndicator::drawContents(QPainter *p)
00097 {
00098 //  kdDebug(5850) << "======== top: " << contentsRect().top() << "  bottom "
00099 //         << contentsRect().bottom() << "  left " << contentsRect().left()
00100 //         << "  right " << contentsRect().right() << endl;
00101 
00102   int i;
00103   for(i=0;i<mColumns;++i) {
00104     if (mEnabled[i]) {
00105       int cellWidth = contentsRect().right()/mColumns;
00106       int xOffset = KOGlobals::self()->reverseLayout() ?
00107                (mColumns - 1 - i)*cellWidth + cellWidth/2 -mPixmap.width()/2 :
00108                i*cellWidth + cellWidth/2 -mPixmap.width()/2;
00109       p->drawPixmap(QPoint(xOffset,0),mPixmap);
00110     }
00111   }
00112 }
00113 
00114 void EventIndicator::changeColumns(int columns)
00115 {
00116   mColumns = columns;
00117   mEnabled.resize(mColumns);
00118 
00119   update();
00120 }
00121 
00122 void EventIndicator::enableColumn(int column, bool enable)
00123 {
00124   mEnabled[column] = enable;
00125 }
00126 
00127 
00128 #include <libkcal/incidence.h>
00129 
00133 
00134 
00135 KOAlternateLabel::KOAlternateLabel(const QString &shortlabel, const QString &longlabel,
00136     const QString &extensivelabel, QWidget *parent, const char *name )
00137   : QLabel(parent, name), mTextTypeFixed(false), mShortText(shortlabel),
00138     mLongText(longlabel), mExtensiveText(extensivelabel)
00139 {
00140   setSizePolicy(QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ));
00141   if (mExtensiveText.isEmpty()) mExtensiveText = mLongText;
00142   squeezeTextToLabel();
00143 }
00144 
00145 KOAlternateLabel::~KOAlternateLabel()
00146 {
00147 }
00148 
00149 void KOAlternateLabel::useShortText()
00150 {
00151   mTextTypeFixed = true;
00152   QLabel::setText( mShortText );
00153   QToolTip::remove( this );
00154   QToolTip::add( this, mExtensiveText );
00155 }
00156 
00157 void KOAlternateLabel::useLongText()
00158 {
00159   mTextTypeFixed = true;
00160   QLabel::setText( mLongText );
00161   QToolTip::remove( this );
00162   QToolTip::add( this, mExtensiveText );
00163 }
00164 
00165 void KOAlternateLabel::useExtensiveText()
00166 {
00167   mTextTypeFixed = true;
00168   QLabel::setText( mExtensiveText );
00169   QToolTip::remove( this );
00170   QToolTip::add( this, "" );
00171 }
00172 
00173 void KOAlternateLabel::useDefaultText()
00174 {
00175   mTextTypeFixed = false;
00176   squeezeTextToLabel();
00177 }
00178 
00179 void KOAlternateLabel::squeezeTextToLabel()
00180 {
00181   if (mTextTypeFixed) return;
00182 
00183   QFontMetrics fm(fontMetrics());
00184   int labelWidth = size().width();
00185   int textWidth = fm.width(mLongText);
00186   int longTextWidth = fm.width(mExtensiveText);
00187   if (longTextWidth <= labelWidth) {
00188     QLabel::setText( mExtensiveText );
00189     QToolTip::remove( this );
00190     QToolTip::add( this, "" );
00191   } else if (textWidth <= labelWidth) {
00192     QLabel::setText( mLongText );
00193     QToolTip::remove( this );
00194     QToolTip::add( this, mExtensiveText );
00195   } else {
00196     QLabel::setText( mShortText );
00197     QToolTip::remove( this );
00198     QToolTip::add( this, mExtensiveText );
00199   }
00200 }
00201 
00202 void KOAlternateLabel::resizeEvent( QResizeEvent * )
00203 {
00204   squeezeTextToLabel();
00205 }
00206 
00207 QSize KOAlternateLabel::minimumSizeHint() const
00208 {
00209   QSize sh = QLabel::minimumSizeHint();
00210   sh.setWidth(-1);
00211   return sh;
00212 }
00213 
00214 void KOAlternateLabel::setText( const QString &text ) {
00215   mLongText = text;
00216   squeezeTextToLabel();
00217 }
00218 
00219 
00223 
00224 KOAgendaView::KOAgendaView( Calendar *cal, QWidget *parent,const char *name, bool isSideBySide ) :
00225   KOrg::AgendaView (cal,parent,name), mExpandButton( 0 ), mAllowAgendaUpdate( true ),
00226   mUpdateItem( 0 ),
00227   mIsSideBySide( isSideBySide ),
00228   mPendingChanges( true )
00229 {
00230   mSelectedDates.append(QDate::currentDate());
00231 
00232   mLayoutDayLabels = 0;
00233   mDayLabelsFrame = 0;
00234   mDayLabels = 0;
00235 
00236   bool isRTL = KOGlobals::self()->reverseLayout();
00237 
00238   if ( KOPrefs::instance()->compactDialogs() ) {
00239     if ( KOPrefs::instance()->mVerticalScreen ) {
00240       mExpandedPixmap = KOGlobals::self()->smallIcon( "1downarrow" );
00241       mNotExpandedPixmap = KOGlobals::self()->smallIcon( "1uparrow" );
00242     } else {
00243       mExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1leftarrow" : "1rightarrow" );
00244       mNotExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1rightarrow" : "1leftarrow" );
00245     }
00246   }
00247 
00248   QBoxLayout *topLayout = new QVBoxLayout(this);
00249 
00250   // Create day name labels for agenda columns
00251   mDayLabelsFrame = new QHBox(this);
00252   topLayout->addWidget(mDayLabelsFrame);
00253 
00254   // Create agenda splitter
00255 #ifndef KORG_NOSPLITTER
00256   mSplitterAgenda = new QSplitter(Vertical,this);
00257   topLayout->addWidget(mSplitterAgenda);
00258 
00259 #if KDE_IS_VERSION( 3, 1, 93 )
00260   mSplitterAgenda->setOpaqueResize( KGlobalSettings::opaqueResize() );
00261 #else
00262   mSplitterAgenda->setOpaqueResize();
00263 #endif
00264 
00265   mAllDayFrame = new QHBox(mSplitterAgenda);
00266 
00267   QWidget *agendaFrame = new QWidget(mSplitterAgenda);
00268 #else
00269   QVBox *mainBox = new QVBox( this );
00270   topLayout->addWidget( mainBox );
00271 
00272   mAllDayFrame = new QHBox(mainBox);
00273 
00274   QWidget *agendaFrame = new QWidget(mainBox);
00275 #endif
00276 
00277   // Create all-day agenda widget
00278   mDummyAllDayLeft = new QVBox( mAllDayFrame );
00279   if ( isSideBySide )
00280     mDummyAllDayLeft->hide();
00281 
00282   if ( KOPrefs::instance()->compactDialogs() ) {
00283     mExpandButton = new QPushButton(mDummyAllDayLeft);
00284     mExpandButton->setPixmap( mNotExpandedPixmap );
00285     mExpandButton->setSizePolicy( QSizePolicy( QSizePolicy::Fixed,
00286                                   QSizePolicy::Fixed ) );
00287     connect( mExpandButton, SIGNAL( clicked() ), SIGNAL( toggleExpand() ) );
00288   } else {
00289     QLabel *label = new QLabel( i18n("All Day"), mDummyAllDayLeft );
00290     label->setAlignment( Qt::AlignRight | Qt::AlignVCenter | Qt::WordBreak );
00291   }
00292 
00293   mAllDayAgenda = new KOAgenda(1,mAllDayFrame);
00294   mAllDayAgenda->setCalendar( calendar() );
00295   QWidget *dummyAllDayRight = new QWidget(mAllDayFrame);
00296 
00297   // Create agenda frame
00298   QGridLayout *agendaLayout = new QGridLayout(agendaFrame,3,3);
00299 //  QHBox *agendaFrame = new QHBox(splitterAgenda);
00300 
00301   // create event indicator bars
00302   mEventIndicatorTop = new EventIndicator(EventIndicator::Top,agendaFrame);
00303   agendaLayout->addWidget(mEventIndicatorTop,0,1);
00304   mEventIndicatorBottom = new EventIndicator(EventIndicator::Bottom,
00305                                              agendaFrame);
00306   agendaLayout->addWidget(mEventIndicatorBottom,2,1);
00307   QWidget *dummyAgendaRight = new QWidget(agendaFrame);
00308   agendaLayout->addWidget(dummyAgendaRight,0,2);
00309 
00310   // Create time labels
00311   mTimeLabels = new TimeLabels(24,agendaFrame);
00312   agendaLayout->addWidget(mTimeLabels,1,0);
00313 
00314   // Create agenda
00315   mAgenda = new KOAgenda(1,96,KOPrefs::instance()->mHourSize,agendaFrame);
00316   mAgenda->setCalendar( calendar() );
00317   agendaLayout->addMultiCellWidget(mAgenda,1,1,1,2);
00318   agendaLayout->setColStretch(1,1);
00319 
00320   // Create event context menu for agenda
00321   mAgendaPopup = eventPopup();
00322 
00323   // Create event context menu for all day agenda
00324   mAllDayAgendaPopup = eventPopup();
00325 
00326   // make connections between dependent widgets
00327   mTimeLabels->setAgenda(mAgenda);
00328   if ( isSideBySide )
00329     mTimeLabels->hide();
00330 
00331   // Update widgets to reflect user preferences
00332 //  updateConfig();
00333 
00334   createDayLabels();
00335 
00336   if ( !isSideBySide ) {
00337     // these blank widgets make the All Day Event box line up with the agenda
00338     dummyAllDayRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
00339     dummyAgendaRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
00340   }
00341 
00342   updateTimeBarWidth();
00343 
00344   // Scrolling
00345   connect(mAgenda->verticalScrollBar(),SIGNAL(valueChanged(int)),
00346           mTimeLabels, SLOT(positionChanged()));
00347 
00348   connect( mAgenda,
00349     SIGNAL( zoomView( const int, const QPoint & ,const Qt::Orientation ) ),
00350     SLOT( zoomView( const int, const QPoint &, const Qt::Orientation ) ) );
00351 
00352   connect(mTimeLabels->verticalScrollBar(),SIGNAL(valueChanged(int)),
00353           SLOT(setContentsPos(int)));
00354 
00355   // Create Events, depends on type of agenda
00356   connect( mAgenda, SIGNAL(newTimeSpanSignal(const QPoint &, const QPoint &)),
00357                     SLOT(newTimeSpanSelected(const QPoint &, const QPoint &)));
00358   connect( mAllDayAgenda, SIGNAL(newTimeSpanSignal(const QPoint &, const QPoint &)),
00359                           SLOT(newTimeSpanSelectedAllDay(const QPoint &, const QPoint &)));
00360 
00361   // event indicator update
00362   connect( mAgenda, SIGNAL(lowerYChanged(int)),
00363                     SLOT(updateEventIndicatorTop(int)));
00364   connect( mAgenda, SIGNAL(upperYChanged(int)),
00365                     SLOT(updateEventIndicatorBottom(int)));
00366 
00367   if ( !readOnly() ) {
00368     connectAgenda( mAgenda, mAgendaPopup, mAllDayAgenda );
00369     connectAgenda( mAllDayAgenda, mAllDayAgendaPopup, mAgenda);
00370   }
00371 
00372   if ( cal ) {
00373     cal->registerObserver( this );
00374   }
00375 }
00376 
00377 
00378 KOAgendaView::~KOAgendaView()
00379 {
00380   if ( calendar() )
00381     calendar()->unregisterObserver( this );
00382   delete mAgendaPopup;
00383   delete mAllDayAgendaPopup;
00384 }
00385 
00386 void KOAgendaView::connectAgenda( KOAgenda *agenda, QPopupMenu *popup,
00387                                   KOAgenda *otherAgenda )
00388 {
00389   connect( agenda, SIGNAL(showIncidencePopupSignal(Calendar *,Incidence *,const QDate &)),
00390            popup, SLOT(showIncidencePopup(Calendar *,Incidence *,const QDate &)) );
00391 
00392   connect( agenda, SIGNAL(showNewEventPopupSignal()),
00393            SLOT(showNewEventPopup()) );
00394 
00395 
00396   // Create/Show/Edit/Delete Event
00397   connect( agenda, SIGNAL(newEventSignal(ResourceCalendar *,const QString &)),
00398            SIGNAL(newEventSignal(ResourceCalendar *,const QString &)) );
00399 
00400   connect( agenda, SIGNAL( newStartSelectSignal() ),
00401            otherAgenda, SLOT( clearSelection() ) );
00402   connect( agenda, SIGNAL( newStartSelectSignal() ),
00403            SIGNAL( timeSpanSelectionChanged()) );
00404 
00405   connect( agenda, SIGNAL( editIncidenceSignal( Incidence * ) ),
00406                    SIGNAL( editIncidenceSignal( Incidence * ) ) );
00407   connect( agenda, SIGNAL( showIncidenceSignal( Incidence * ) ),
00408                    SIGNAL( showIncidenceSignal( Incidence * ) ) );
00409   connect( agenda, SIGNAL( deleteIncidenceSignal( Incidence * ) ),
00410                    SIGNAL( deleteIncidenceSignal( Incidence * ) ) );
00411 
00412   connect( agenda, SIGNAL( startMultiModify( const QString & ) ),
00413                    SIGNAL( startMultiModify( const QString & ) ) );
00414   connect( agenda, SIGNAL( endMultiModify() ),
00415                    SIGNAL( endMultiModify() ) );
00416 
00417   connect( agenda, SIGNAL( itemModified( KOAgendaItem * ) ),
00418                    SLOT( updateEventDates( KOAgendaItem * ) ) );
00419   connect( agenda, SIGNAL( enableAgendaUpdate( bool ) ),
00420                    SLOT( enableAgendaUpdate( bool ) ) );
00421 
00422   // drag signals
00423   connect( agenda, SIGNAL( startDragSignal( Incidence * ) ),
00424            SLOT( startDrag( Incidence * ) ) );
00425 
00426   // synchronize selections
00427   connect( agenda, SIGNAL( incidenceSelected( Incidence *,const QDate & ) ),
00428            otherAgenda, SLOT( deselectItem() ) );
00429   connect( agenda, SIGNAL( incidenceSelected( Incidence *,const QDate & ) ),
00430            SIGNAL( incidenceSelected( Incidence *, const QDate & ) ) );
00431 
00432   // rescheduling of todos by d'n'd
00433   connect( agenda, SIGNAL( droppedToDo( Todo *, const QPoint &, bool ) ),
00434            SLOT( slotTodoDropped( Todo *, const QPoint &, bool ) ) );
00435 
00436 }
00437 
00438 void KOAgendaView::zoomInVertically( )
00439 {
00440   if ( !mIsSideBySide )
00441     KOPrefs::instance()->mHourSize++;
00442   mAgenda->updateConfig();
00443   mAgenda->checkScrollBoundaries();
00444 
00445   mTimeLabels->updateConfig();
00446   mTimeLabels->positionChanged();
00447   mTimeLabels->repaint();
00448 
00449   updateView();
00450 }
00451 
00452 void KOAgendaView::zoomOutVertically( )
00453 {
00454 
00455   if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) {
00456 
00457     if ( !mIsSideBySide )
00458       KOPrefs::instance()->mHourSize--;
00459     mAgenda->updateConfig();
00460     mAgenda->checkScrollBoundaries();
00461 
00462     mTimeLabels->updateConfig();
00463     mTimeLabels->positionChanged();
00464     mTimeLabels->repaint();
00465 
00466     updateView();
00467   }
00468 }
00469 
00470 void KOAgendaView::zoomInHorizontally( const QDate &date)
00471 {
00472   QDate begin;
00473   QDate newBegin;
00474   QDate dateToZoom = date;
00475   int ndays,count;
00476 
00477   begin = mSelectedDates.first();
00478   ndays = begin.daysTo( mSelectedDates.last() );
00479 
00480   // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it.
00481   if ( ! dateToZoom.isValid () )
00482     dateToZoom=mAgenda->selectedIncidenceDate();
00483 
00484   if( !dateToZoom.isValid() ) {
00485     if ( ndays > 1 ) {
00486       newBegin=begin.addDays(1);
00487       count = ndays-1;
00488       emit zoomViewHorizontally ( newBegin , count );
00489     }
00490   } else {
00491     if ( ndays <= 2 ) {
00492       newBegin = dateToZoom;
00493       count = 1;
00494     } else  {
00495       newBegin = dateToZoom.addDays( -ndays/2 +1  );
00496       count = ndays -1 ;
00497     }
00498     emit zoomViewHorizontally ( newBegin , count );
00499   }
00500 }
00501 
00502 void KOAgendaView::zoomOutHorizontally( const QDate &date )
00503 {
00504   QDate begin;
00505   QDate newBegin;
00506   QDate dateToZoom = date;
00507   int ndays,count;
00508 
00509   begin = mSelectedDates.first();
00510   ndays = begin.daysTo( mSelectedDates.last() );
00511 
00512   // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it.
00513   if ( ! dateToZoom.isValid () )
00514     dateToZoom=mAgenda->selectedIncidenceDate();
00515 
00516   if ( !dateToZoom.isValid() ) {
00517     newBegin = begin.addDays(-1);
00518     count = ndays+3 ;
00519   } else {
00520     newBegin = dateToZoom.addDays( -ndays/2-1 );
00521     count = ndays+3;
00522   }
00523 
00524   if ( abs( count ) >= 31 )
00525     kdDebug(5850) << "change to the mounth view?"<<endl;
00526   else
00527     //We want to center the date
00528     emit zoomViewHorizontally( newBegin, count );
00529 }
00530 
00531 void KOAgendaView::zoomView( const int delta, const QPoint &pos,
00532   const Qt::Orientation orient )
00533 {
00534   static QDate zoomDate;
00535   static QTimer *t = new QTimer( this );
00536 
00537 
00538   //Zoom to the selected incidence, on the other way
00539   // zoom to the date on screen after the first mousewheel move.
00540   if ( orient == Qt::Horizontal ) {
00541     QDate date=mAgenda->selectedIncidenceDate();
00542     if ( date.isValid() )
00543       zoomDate=date;
00544     else{
00545       if ( !t->isActive() ) {
00546         zoomDate= mSelectedDates[pos.x()];
00547       }
00548       t->start ( 1000,true );
00549     }
00550     if ( delta > 0 )
00551       zoomOutHorizontally( zoomDate );
00552     else
00553       zoomInHorizontally( zoomDate );
00554   } else {
00555     // Vertical zoom
00556     QPoint posConstentsOld = mAgenda->gridToContents(pos);
00557     if ( delta > 0 ) {
00558       zoomOutVertically();
00559     } else {
00560       zoomInVertically();
00561     }
00562     QPoint posConstentsNew = mAgenda->gridToContents(pos);
00563     mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() );
00564   }
00565 }
00566 
00567 void KOAgendaView::createDayLabels()
00568 {
00569 //  kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl;
00570 
00571   // ### Before deleting and recreating we could check if mSelectedDates changed...
00572   // It would remove some flickering and gain speed (since this is called by
00573   // each updateView() call)
00574   delete mDayLabels;
00575 
00576   mDayLabels = new QFrame (mDayLabelsFrame);
00577   mLayoutDayLabels = new QHBoxLayout(mDayLabels);
00578   if ( !mIsSideBySide )
00579     mLayoutDayLabels->addSpacing(mTimeLabels->width());
00580 
00581   const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
00582 
00583   DateList::ConstIterator dit;
00584   for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
00585     QDate date = *dit;
00586     QBoxLayout *dayLayout = new QVBoxLayout(mLayoutDayLabels);
00587     mLayoutDayLabels->setStretchFactor(dayLayout, 1);
00588 //    dayLayout->setMinimumWidth(1);
00589 
00590     int dW = calsys->dayOfWeek(date);
00591     QString veryLongStr = KGlobal::locale()->formatDate( date );
00592     QString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" )
00593         .arg( calsys->weekDayName( dW, true ) )
00594         .arg( calsys->day(date) );
00595     QString shortstr = QString::number(calsys->day(date));
00596 
00597     KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr,
00598       longstr, veryLongStr, mDayLabels);
00599     dayLabel->setMinimumWidth(1);
00600     dayLabel->setAlignment(QLabel::AlignHCenter);
00601     if (date == QDate::currentDate()) {
00602       QFont font = dayLabel->font();
00603       font.setBold(true);
00604       dayLabel->setFont(font);
00605     }
00606     dayLayout->addWidget(dayLabel);
00607 
00608     // if a holiday region is selected, show the holiday name
00609     QStringList texts = KOGlobals::self()->holiday( date );
00610     QStringList::ConstIterator textit = texts.begin();
00611     for ( ; textit != texts.end(); ++textit ) {
00612       // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00613       KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), QString::null, mDayLabels );
00614       label->setMinimumWidth(1);
00615       label->setAlignment(AlignCenter);
00616       dayLayout->addWidget(label);
00617     }
00618 
00619 #ifndef KORG_NOPLUGINS
00620     CalendarDecoration::List cds = KOCore::self()->calendarDecorations();
00621     CalendarDecoration *it;
00622     for(it = cds.first(); it; it = cds.next()) {
00623       QString text = it->shortText( date );
00624       if ( !text.isEmpty() ) {
00625         // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00626         KOAlternateLabel*label = new KOAlternateLabel( text, text, QString::null, mDayLabels );
00627         label->setMinimumWidth(1);
00628         label->setAlignment(AlignCenter);
00629         dayLayout->addWidget(label);
00630       }
00631     }
00632 
00633     for(it = cds.first(); it; it = cds.next()) {
00634       QWidget *wid = it->smallWidget(mDayLabels,date);
00635       if ( wid ) {
00636 //      wid->setHeight(20);
00637         dayLayout->addWidget(wid);
00638       }
00639     }
00640 #endif
00641   }
00642 
00643   if ( !mIsSideBySide )
00644     mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width());
00645   mDayLabels->show();
00646 }
00647 
00648 void KOAgendaView::enableAgendaUpdate( bool enable )
00649 {
00650   mAllowAgendaUpdate = enable;
00651 }
00652 
00653 int KOAgendaView::maxDatesHint()
00654 {
00655   // Not sure about the max number of events, so return 0 for now.
00656   return 0;
00657 }
00658 
00659 int KOAgendaView::currentDateCount()
00660 {
00661   return mSelectedDates.count();
00662 }
00663 
00664 Incidence::List KOAgendaView::selectedIncidences()
00665 {
00666   Incidence::List selected;
00667   Incidence *incidence;
00668 
00669   incidence = mAgenda->selectedIncidence();
00670   if (incidence) selected.append(incidence);
00671 
00672   incidence = mAllDayAgenda->selectedIncidence();
00673   if (incidence) selected.append(incidence);
00674 
00675   return selected;
00676 }
00677 
00678 DateList KOAgendaView::selectedDates()
00679 {
00680   DateList selected;
00681   QDate qd;
00682 
00683   qd = mAgenda->selectedIncidenceDate();
00684   if (qd.isValid()) selected.append(qd);
00685 
00686   qd = mAllDayAgenda->selectedIncidenceDate();
00687   if (qd.isValid()) selected.append(qd);
00688 
00689   return selected;
00690 }
00691 
00692 bool KOAgendaView::eventDurationHint( QDateTime &startDt, QDateTime &endDt,
00693                                       bool &allDay )
00694 {
00695   if ( selectionStart().isValid() ) {
00696     QDateTime start = selectionStart();
00697     QDateTime end = selectionEnd();
00698 
00699     if ( start.secsTo( end ) == 15*60 ) {
00700       // One cell in the agenda view selected, e.g.
00701       // because of a double-click, => Use the default duration
00702       QTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() );
00703       int addSecs = ( defaultDuration.hour()*3600 ) +
00704                     ( defaultDuration.minute()*60 );
00705       end = start.addSecs( addSecs );
00706     }
00707 
00708     startDt = start;
00709     endDt = end;
00710     allDay = selectedIsAllDay();
00711     return true;
00712   }
00713   return false;
00714 }
00715 
00717 bool KOAgendaView::selectedIsSingleCell()
00718 {
00719   if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false;
00720 
00721   if (selectedIsAllDay()) {
00722     int days = selectionStart().daysTo(selectionEnd());
00723     return ( days < 1 );
00724   } else {
00725     int secs = selectionStart().secsTo(selectionEnd());
00726     return ( secs <= 24*60*60/mAgenda->rows() );
00727   }
00728 }
00729 
00730 
00731 void KOAgendaView::updateView()
00732 {
00733 //  kdDebug(5850) << "KOAgendaView::updateView()" << endl;
00734   fillAgenda();
00735 }
00736 
00737 
00738 /*
00739   Update configuration settings for the agenda view. This method is not
00740   complete.
00741 */
00742 void KOAgendaView::updateConfig()
00743 {
00744 //  kdDebug(5850) << "KOAgendaView::updateConfig()" << endl;
00745 
00746   // update config for children
00747   mTimeLabels->updateConfig();
00748   mAgenda->updateConfig();
00749   mAllDayAgenda->updateConfig();
00750 
00751   // widget synchronization
00752   // FIXME: find a better way, maybe signal/slot
00753   mTimeLabels->positionChanged();
00754 
00755   // for some reason, this needs to be called explicitly
00756   mTimeLabels->repaint();
00757 
00758   updateTimeBarWidth();
00759 
00760   // ToolTips displaying summary of events
00761   KOAgendaItem::toolTipGroup()->setEnabled(KOPrefs::instance()
00762                                            ->mEnableToolTips);
00763 
00764   setHolidayMasks();
00765 
00766   createDayLabels();
00767 
00768   updateView();
00769 }
00770 
00771 void KOAgendaView::updateTimeBarWidth()
00772 {
00773   int width;
00774 
00775   width = mDummyAllDayLeft->fontMetrics().width( i18n("All Day") );
00776   width = QMAX( width, mTimeLabels->width() );
00777 
00778   mDummyAllDayLeft->setFixedWidth( width );
00779   mTimeLabels->setFixedWidth( width );
00780 }
00781 
00782 
00783 void KOAgendaView::updateEventDates( KOAgendaItem *item )
00784 {
00785   kdDebug(5850) << "KOAgendaView::updateEventDates(): " << item->text() << endl;
00786 
00787   QDateTime startDt,endDt;
00788 
00789   // Start date of this incidence, calculate the offset from it (so recurring and
00790   // non-recurring items can be treated exactly the same, we never need to check
00791   // for doesRecur(), because we only move the start day by the number of days the
00792   // agenda item was really moved. Smart, isn't it?)
00793   QDate thisDate;
00794   if ( item->cellXLeft() < 0 ) {
00795     thisDate = ( mSelectedDates.first() ).addDays( item->cellXLeft() );
00796   } else {
00797     thisDate = mSelectedDates[ item->cellXLeft() ];
00798   }
00799   QDate oldThisDate( item->itemDate() );
00800   int daysOffset = oldThisDate.daysTo( thisDate );
00801   int daysLength = 0;
00802 
00803 //  startDt.setDate( startDate );
00804 
00805   Incidence *incidence = item->incidence();
00806   if ( !incidence ) return;
00807   if ( !mChanger || !mChanger->beginChange(incidence) ) return;
00808   Incidence *oldIncidence = incidence->clone();
00809 
00810   QTime startTime(0,0,0), endTime(0,0,0);
00811   if ( incidence->doesFloat() ) {
00812     daysLength = item->cellWidth() - 1;
00813   } else {
00814     startTime = mAgenda->gyToTime( item->cellYTop() );
00815     if ( item->lastMultiItem() ) {
00816       endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 );
00817       daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft();
00818     } else {
00819       endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
00820     }
00821   }
00822 
00823 //  kdDebug(5850) << "KOAgendaView::updateEventDates(): now setting dates" << endl;
00824   // FIXME: use a visitor here
00825   if ( incidence->type() == "Event" ) {
00826     startDt = incidence->dtStart();
00827     startDt = startDt.addDays( daysOffset );
00828     startDt.setTime( startTime );
00829     endDt = startDt.addDays( daysLength );
00830     endDt.setTime( endTime );
00831     Event*ev = static_cast<Event*>(incidence);
00832     if( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) {
00833       // No change
00834       delete oldIncidence;
00835       return;
00836     }
00837     incidence->setDtStart( startDt );
00838     ev->setDtEnd( endDt );
00839   } else if ( incidence->type() == "Todo" ) {
00840     Todo *td = static_cast<Todo*>(incidence);
00841     startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
00842     startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) );
00843     startDt.setTime( startTime );
00844     endDt.setDate( thisDate );
00845     endDt.setTime( endTime );
00846 
00847     if( td->dtDue() == endDt ) {
00848       // No change
00849       delete oldIncidence;
00850       return;
00851     }
00852   }
00853   // FIXME: Adjusting the recurrence should really go to CalendarView so this
00854   // functionality will also be available in other views!
00855   // TODO_Recurrence: This does not belong here, and I'm not really sure
00856   // how it's supposed to work anyway.
00857 /*
00858     Recurrence *recur = incidence->recurrence();
00859     if ( recur->doesRecur() && daysOffset != 0 ) {
00860     switch ( recur->recurrenceType() ) {
00861       case Recurrence::rYearlyPos: {
00862         int freq = recur->frequency();
00863         int duration = recur->duration();
00864         QDate endDt( recur->endDate() );
00865         bool negative = false;
00866 
00867         QPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() );
00868         if ( monthPos.first() ) {
00869           negative = monthPos.first()->negative;
00870         }
00871         QBitArray days( 7 );
00872         int pos = 0;
00873         days.fill( false );
00874         days.setBit( thisDate.dayOfWeek() - 1 );
00875         if ( negative ) {
00876           pos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
00877         } else {
00878           pos =  ( thisDate.day()-1 ) / 7 + 1;
00879         }
00880         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00881         recur->unsetRecurs();
00882         if ( duration != 0 ) {
00883           recur->setYearly( Recurrence::rYearlyPos, freq, duration );
00884         } else {
00885           recur->setYearly( Recurrence::rYearlyPos, freq, endDt );
00886         }
00887         recur->addYearlyMonthPos( pos, days );
00888         recur->addYearlyNum( thisDate.month() );
00889 
00890         break; }
00891         case Recurrence::rYearlyDay: {
00892           int freq = recur->frequency();
00893           int duration = recur->duration();
00894           QDate endDt( recur->endDate() );
00895         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00896           recur->unsetRecurs();
00897           if ( duration == 0 ) { // end by date
00898             recur->setYearly( Recurrence::rYearlyDay, freq, endDt );
00899           } else {
00900             recur->setYearly( Recurrence::rYearlyDay, freq, duration );
00901           }
00902           recur->addYearlyNum( thisDate.dayOfYear() );
00903           break; }
00904           case Recurrence::rYearlyMonth: {
00905             int freq = recur->frequency();
00906             int duration = recur->duration();
00907             QDate endDt( recur->endDate() );
00908         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00909             recur->unsetRecurs();
00910             if ( duration != 0 ) {
00911               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration );
00912             } else {
00913               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt );
00914             }
00915             recur->addYearlyNum( thisDate.month() );
00916             break; }
00917             case Recurrence::rMonthlyPos: {
00918               int freq = recur->frequency();
00919               int duration = recur->duration();
00920               QDate endDt( recur->endDate() );
00921               QPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() );
00922               if ( !monthPos.isEmpty() ) {
00923           // FIXME: How shall I adapt the day x of week Y if we move the date across month borders???
00924           // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
00925           // That's fine for korganizer, but might mess up other organizers.
00926                 QBitArray rDays( 7 );
00927                 rDays = monthPos.first()->rDays;
00928                 bool negative = monthPos.first()->negative;
00929                 int newPos;
00930                 rDays.fill( false );
00931                 rDays.setBit( thisDate.dayOfWeek() - 1 );
00932                 if ( negative ) {
00933                   newPos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
00934                 } else {
00935                   newPos =  ( thisDate.day()-1 ) / 7 + 1;
00936                 }
00937 
00938           // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00939                 recur->unsetRecurs();
00940                 if ( duration == 0 ) { // end by date
00941                   recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt );
00942                 } else {
00943                   recur->setMonthly( Recurrence::rMonthlyPos, freq, duration );
00944                 }
00945                 recur->addMonthlyPos( newPos, rDays );
00946               }
00947               break;}
00948               case Recurrence::rMonthlyDay: {
00949                 int freq = recur->frequency();
00950                 int duration = recur->duration();
00951                 QDate endDt( recur->endDate() );
00952                 QPtrList<int> monthDays( recur->monthDays() );
00953         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00954                 recur->unsetRecurs();
00955                 if ( duration == 0 ) { // end by date
00956                   recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt );
00957                 } else {
00958                   recur->setMonthly( Recurrence::rMonthlyDay, freq, duration );
00959                 }
00960         // FIXME: How shall I adapt the n-th day if we move the date across month borders???
00961         // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
00962         // That's fine for korganizer, but might mess up other organizers.
00963                 recur->addMonthlyDay( thisDate.day() );
00964 
00965                 break;}
00966                 case Recurrence::rWeekly: {
00967                   QBitArray days(7), oldDays( recur->days() );
00968                   int offset = daysOffset % 7;
00969                   if ( offset<0 ) offset = (offset+7) % 7;
00970         // rotate the days
00971                   for (int d=0; d<7; d++ ) {
00972                     days.setBit( (d+offset) % 7, oldDays.at(d) );
00973                   }
00974                   if ( recur->duration() == 0 ) { // end by date
00975                     recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() );
00976                   } else { // duration or no end
00977                     recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() );
00978                   }
00979                   break;}
00980       // nothing to be done for the following:
00981       case Recurrence::rDaily:
00982       case Recurrence::rHourly:
00983       case Recurrence::rMinutely:
00984       case Recurrence::rNone:
00985       default:
00986         break;
00987     }
00988     if ( recur->duration()==0 ) { // end by date
00989       recur->setEndDate( recur->endDate().addDays( daysOffset ) );
00990     }
00991     KMessageBox::information( this, i18n("A recurring calendar item was moved "
00992                               "to a different day. The recurrence settings "
00993                               "have been updated with that move. Please check "
00994                               "them in the editor."),
00995                               i18n("Recurrence Moved"),
00996                               "RecurrenceMoveInAgendaWarning" );
00997   }*/
00998 
00999   // FIXME: use a visitor here
01000   if ( incidence->type() == "Event" ) {
01001     incidence->setDtStart( startDt );
01002     (static_cast<Event*>( incidence ) )->setDtEnd( endDt );
01003   } else if ( incidence->type() == "Todo" ) {
01004     Todo *td = static_cast<Todo*>( incidence );
01005     if ( td->hasStartDate() )
01006       td->setDtStart( startDt );
01007     td->setDtDue( endDt );
01008   }
01009 
01010   item->setItemDate( startDt.date() );
01011 
01012   KOIncidenceToolTip::remove( item );
01013   KOIncidenceToolTip::add( item, calendar(), incidence, thisDate, KOAgendaItem::toolTipGroup() );
01014 
01015   const bool result = mChanger->changeIncidence( oldIncidence, incidence,
01016                                                  KOGlobals::DATE_MODIFIED, this );
01017   mChanger->endChange( incidence );
01018   delete oldIncidence;
01019 
01020   if ( !result ) {
01021     mPendingChanges = true;
01022     QTimer::singleShot( 0, this, SLOT(updateView()) );
01023     return;
01024   }
01025 
01026   // don't update the agenda as the item already has the correct coordinates.
01027   // an update would delete the current item and recreate it, but we are still
01028   // using a pointer to that item! => CRASH
01029   enableAgendaUpdate( false );
01030   // We need to do this in a timer to make sure we are not deleting the item
01031   // we are currently working on, which would lead to crashes
01032   // Only the actually moved agenda item is already at the correct position and mustn't be
01033   // recreated. All others have to!!!
01034   if ( incidence->doesRecur() ) {
01035     mUpdateItem = incidence;
01036     QTimer::singleShot( 0, this, SLOT( doUpdateItem() ) );
01037   }
01038 
01039     enableAgendaUpdate( true );
01040 
01041 //  kdDebug(5850) << "KOAgendaView::updateEventDates() done " << endl;
01042 }
01043 
01044 void KOAgendaView::doUpdateItem()
01045 {
01046   if ( mUpdateItem ) {
01047     changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED );
01048     mUpdateItem = 0;
01049   }
01050 }
01051 
01052 
01053 
01054 void KOAgendaView::showDates( const QDate &start, const QDate &end )
01055 {
01056 //  kdDebug(5850) << "KOAgendaView::selectDates" << endl;
01057   if ( !mSelectedDates.isEmpty() && mSelectedDates.first() == start
01058         && mSelectedDates.last() == end && !mPendingChanges )
01059     return;
01060 
01061   mSelectedDates.clear();
01062 
01063   QDate d = start;
01064   while (d <= end) {
01065     mSelectedDates.append(d);
01066     d = d.addDays( 1 );
01067   }
01068 
01069   // and update the view
01070   fillAgenda();
01071 }
01072 
01073 
01074 void KOAgendaView::showIncidences( const Incidence::List &, const QDate & )
01075 {
01076   kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl;
01077 }
01078 
01079 void KOAgendaView::insertIncidence( Incidence *incidence, const QDate &curDate )
01080 {
01081   if ( !filterByResource( incidence ) ) {
01082     return;
01083   }
01084 
01085   // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's
01086   Event *event = dynamic_cast<Event *>( incidence );
01087   Todo  *todo  = dynamic_cast<Todo  *>( incidence );
01088 
01089   int curCol = mSelectedDates.first().daysTo( curDate );
01090 
01091   // In case incidence->dtStart() isn't visible (crosses bounderies)
01092   if ( curCol < 0 ) {
01093     curCol = 0;
01094   }
01095 
01096   // The date for the event is not displayed, just ignore it
01097   if ( curCol >= int( mSelectedDates.count() ) ) {
01098     return;
01099   }
01100 
01101   // Default values, which can never be reached
01102   mMinY[curCol] = mAgenda->timeToY( QTime( 23, 59 ) ) + 1;
01103   mMaxY[curCol] = mAgenda->timeToY( QTime( 0, 0 ) ) - 1;
01104 
01105   int beginX;
01106   int endX;
01107   QDate columnDate;
01108   if ( event ) {
01109     QDate firstVisibleDate = mSelectedDates.first();
01110     // its crossing bounderies, lets calculate beginX and endX
01111     if ( curDate < firstVisibleDate ) {
01112       beginX = curCol + firstVisibleDate.daysTo( curDate );
01113       endX   = beginX + event->dtStart().daysTo( event->dtEnd() );
01114       columnDate = firstVisibleDate;
01115     } else {
01116       beginX = curCol;
01117       endX   = beginX + event->dtStart().daysTo( event->dtEnd() );
01118       columnDate = curDate;
01119     }
01120   } else if ( todo ) {
01121     if ( !todo->hasDueDate() ) {
01122       return;  // todo shall not be displayed if it has no date
01123     }
01124     columnDate = curDate;
01125     beginX = endX = curCol;
01126 
01127   } else {
01128     return;
01129   }
01130   if ( todo && todo->isOverdue() ) {
01131     mAllDayAgenda->insertAllDayItem( incidence, columnDate, curCol, curCol );
01132   } else if ( incidence->doesFloat() ||
01133               ( todo &&
01134                 ( todo->dtDue().time() == QTime( 0, 0 ) ||
01135                   !todo->dtDue().isValid() ) ) ) {
01136       mAllDayAgenda->insertAllDayItem( incidence, columnDate, beginX, endX );
01137   } else if ( event && event->isMultiDay() ) {
01138     int startY = mAgenda->timeToY( event->dtStart().time() );
01139     QTime endtime = event->dtEnd().time();
01140     if ( endtime == QTime( 0, 0, 0 ) ) {
01141       endtime = QTime( 23, 59, 59 );
01142     }
01143     int endY = mAgenda->timeToY( endtime ) - 1;
01144     if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) {
01145       mAgenda->insertMultiItem( event, columnDate, beginX, endX, startY, endY );
01146 
01147     }
01148     if ( beginX == curCol ) {
01149       mMaxY[curCol] = mAgenda->timeToY( QTime( 23, 59 ) );
01150       if ( startY < mMinY[curCol] ) {
01151         mMinY[curCol] = startY;
01152       }
01153     } else if ( endX == curCol ) {
01154       mMinY[curCol] = mAgenda->timeToY( QTime( 0, 0 ) );
01155       if ( endY > mMaxY[curCol] ) {
01156         mMaxY[curCol] = endY;
01157       }
01158     } else {
01159       mMinY[curCol] = mAgenda->timeToY( QTime( 0, 0 ) );
01160       mMaxY[curCol] = mAgenda->timeToY( QTime( 23, 59 ) );
01161     }
01162   } else {
01163     int startY = 0, endY = 0;
01164     if ( event ) {
01165       startY = mAgenda->timeToY( incidence->dtStart().time() );
01166       QTime endtime = event->dtEnd().time();
01167       if ( endtime == QTime( 0, 0, 0 ) ) {
01168         endtime = QTime( 23, 59, 59 );
01169       }
01170       endY = mAgenda->timeToY( endtime ) - 1;
01171     }
01172     if ( todo ) {
01173       QTime t = todo->dtDue().time();
01174 
01175       int halfHour = 1800;
01176       if ( t.addSecs( -halfHour ) < t ) {
01177         startY = mAgenda->timeToY( t.addSecs( -halfHour ) );
01178         endY   = mAgenda->timeToY( t ) - 1;
01179       } else {
01180         startY = 0;
01181         endY   = mAgenda->timeToY( t.addSecs( halfHour ) ) - 1;
01182       }
01183     }
01184     if ( endY < startY ) {
01185       endY = startY;
01186     }
01187     mAgenda->insertItem( incidence, columnDate, curCol, startY, endY );
01188     if ( startY < mMinY[curCol] ) {
01189       mMinY[curCol] = startY;
01190     }
01191     if ( endY > mMaxY[curCol] ) {
01192       mMaxY[curCol] = endY;
01193     }
01194   }
01195 }
01196 
01197 void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence )
01198 {
01199   Todo *todo = dynamic_cast<Todo *>(incidence);
01200   CalFilter *filter = calendar()->filter();
01201   if ( ( filter && !filter->filterIncidence( incidence ) ) ||
01202        ( ( todo && !KOPrefs::instance()->showAllDayTodo() ) ) ) {
01203     return;
01204   }
01205 
01206   displayIncidence( incidence );
01207 }
01208 
01209 void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode )
01210 {
01211   switch ( mode ) {
01212     case KOGlobals::INCIDENCEADDED:
01213     {
01214       // Add an event. No need to recreate the whole view!
01215       // recreating everything even causes troubles: dropping to the
01216       // day matrix recreates the agenda items, but the evaluation is
01217       // still in an agendaItems' code, which was deleted in the mean time.
01218       // Thus KOrg crashes...
01219       changeIncidenceDisplayAdded( incidence );
01220       updateEventIndicators();
01221       break;
01222     }
01223     case KOGlobals::INCIDENCEEDITED:
01224     {
01225       if ( mAllowAgendaUpdate ) {
01226         removeIncidence( incidence );
01227         changeIncidenceDisplayAdded( incidence );
01228       }
01229       updateEventIndicators();
01230       break;
01231     }
01232     case KOGlobals::INCIDENCEDELETED:
01233     {
01234       mAgenda->removeIncidence( incidence );
01235       mAllDayAgenda->removeIncidence( incidence );
01236       updateEventIndicators();
01237       break;
01238     }
01239     default:
01240       updateView();
01241   }
01242 }
01243 
01244 void KOAgendaView::fillAgenda( const QDate & )
01245 {
01246   fillAgenda();
01247 }
01248 
01249 void KOAgendaView::fillAgenda()
01250 {
01251   mPendingChanges = false;
01252 
01253   /* Remember the uids of the selected items. In case one of the
01254    * items was deleted and re-added, we want to reselect it. */
01255   const QString &selectedAgendaUid = mAgenda->lastSelectedUid();
01256   const QString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
01257 
01258   enableAgendaUpdate( true );
01259   clearView();
01260 
01261   mAllDayAgenda->changeColumns( mSelectedDates.count() );
01262   mAgenda->changeColumns( mSelectedDates.count() );
01263   mEventIndicatorTop->changeColumns( mSelectedDates.count() );
01264   mEventIndicatorBottom->changeColumns( mSelectedDates.count() );
01265 
01266   createDayLabels();
01267   setHolidayMasks();
01268 
01269   mMinY.resize( mSelectedDates.count() );
01270   mMaxY.resize( mSelectedDates.count() );
01271 
01272   mAgenda->setDateList( mSelectedDates );
01273 
01274   bool somethingReselected = false;
01275   Incidence::List incidences = calendar()->incidences();
01276 
01277   for ( Incidence::List::ConstIterator it = incidences.begin(); it!=incidences.constEnd(); ++it ) {
01278     Incidence *incidence = (*it);
01279     displayIncidence( incidence );
01280 
01281     if( incidence->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
01282       mAgenda->selectItemByUID( incidence->uid() );
01283       somethingReselected = true;
01284     }
01285 
01286     if( incidence->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
01287       mAllDayAgenda->selectItemByUID( incidence->uid() );
01288       somethingReselected = true;
01289     }
01290 
01291   }
01292 
01293   mAgenda->checkScrollBoundaries();
01294   updateEventIndicators();
01295 
01296   //  mAgenda->viewport()->update();
01297   //  mAllDayAgenda->viewport()->update();
01298 
01299   // make invalid
01300   deleteSelectedDateTime();
01301 
01302   if( !somethingReselected ) {
01303     emit incidenceSelected( 0, QDate() );
01304   }
01305 }
01306 
01307 void KOAgendaView::displayIncidence( Incidence *incidence )
01308 {
01309   QDate today = QDate::currentDate();
01310   DateTimeList::iterator t;
01311 
01312   // FIXME: use a visitor here
01313   Todo *todo = dynamic_cast<Todo *>( incidence );
01314   Event *event = dynamic_cast<Event *>( incidence );
01315 
01316   QDateTime firstVisibleDateTime = mSelectedDates.first();
01317   QDateTime lastVisibleDateTime = mSelectedDates.last();
01318 
01319   lastVisibleDateTime.setTime( QTime( 23, 59 ) );
01320   firstVisibleDateTime.setTime( QTime( 0, 0 ) );
01321   DateTimeList dateTimeList;
01322 
01323   QDateTime incDtStart = incidence->dtStart();
01324   QDateTime incDtEnd   = incidence->dtEnd();
01325 
01326   if ( todo &&
01327        ( !KOPrefs::instance()->showAllDayTodo() || !todo->hasDueDate() ) ) {
01328     return;
01329   }
01330 
01331   if ( incidence->doesRecur() ) {
01332     int eventDuration = incDtStart.daysTo( incDtEnd );
01333 
01334     // if there's a multiday event that starts before firstVisibleDateTime but ends after
01335     // lets include it. timesInInterval() ignores incidences that aren't totaly inside
01336     // the range
01337     QDateTime startDateTimeWithOffset = firstVisibleDateTime.addDays( -eventDuration );
01338     dateTimeList =
01339       incidence->recurrence()->timesInInterval( startDateTimeWithOffset,
01340                                                 lastVisibleDateTime );
01341   } else {
01342     QDateTime dateToAdd; // date to add to our date list
01343     QDateTime incidenceStart;
01344     QDateTime incidenceEnd;
01345 
01346     if ( todo && todo->hasDueDate() && !todo->isOverdue() ) {
01347       // If it's not overdue it will be shown at the original date (not today)
01348       dateToAdd = todo->dtDue();
01349       incidenceEnd = dateToAdd;
01350     } else if ( event ) {
01351       dateToAdd = incDtStart;
01352       incidenceEnd = incDtEnd;
01353 
01354       if ( !dateToAdd.time().isValid() ) {
01355         // so comparisons with < > actually work
01356         dateToAdd.setTime( QTime( 0, 0 ) );
01357       }
01358     }
01359 
01360     if  ( dateToAdd <= lastVisibleDateTime && incidenceEnd >= firstVisibleDateTime ) {
01361       dateTimeList += dateToAdd;
01362     }
01363   }
01364 
01365   // ToDo items shall be displayed today if they are already overdude
01366   QDateTime dateTimeToday = today;
01367   if ( todo &&
01368        todo->isOverdue() &&
01369        dateTimeToday >= firstVisibleDateTime &&
01370        dateTimeToday <= lastVisibleDateTime ) {
01371 
01372     bool doAdd = true;
01373 
01374     if ( todo->doesRecur() ) {
01375       /* If there's a recurring instance showing up today don't add "today" again
01376        * we don't want the event to appear duplicated */
01377       for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01378         if ( (*t).date() == today ) {
01379           doAdd = false;
01380           break;
01381        }
01382       }
01383     }
01384 
01385     if ( doAdd ) {
01386       dateTimeList += dateTimeToday;
01387     }
01388   }
01389 
01390   for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01391     insertIncidence( incidence, (*t).date() );
01392   }
01393 }
01394 
01395 void KOAgendaView::clearView()
01396 {
01397 //  kdDebug(5850) << "ClearView" << endl;
01398   mAllDayAgenda->clear();
01399   mAgenda->clear();
01400 }
01401 
01402 CalPrinterBase::PrintType KOAgendaView::printType()
01403 {
01404   if ( currentDateCount() == 1 ) return CalPrinterBase::Day;
01405   else return CalPrinterBase::Week;
01406 }
01407 
01408 void KOAgendaView::updateEventIndicatorTop( int newY )
01409 {
01410   uint i;
01411   for( i = 0; i < mMinY.size(); ++i ) {
01412     mEventIndicatorTop->enableColumn( i, newY >= mMinY[i] );
01413   }
01414   mEventIndicatorTop->update();
01415 }
01416 
01417 void KOAgendaView::updateEventIndicatorBottom( int newY )
01418 {
01419   uint i;
01420   for( i = 0; i < mMaxY.size(); ++i ) {
01421     mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
01422   }
01423   mEventIndicatorBottom->update();
01424 }
01425 
01426 void KOAgendaView::slotTodoDropped( Todo *todo, const QPoint &gpos, bool allDay )
01427 {
01428   if ( gpos.x()<0 || gpos.y()<0 ) return;
01429   QDate day = mSelectedDates[gpos.x()];
01430   QTime time = mAgenda->gyToTime( gpos.y() );
01431   QDateTime newTime( day, time );
01432 
01433   if ( todo ) {
01434     Todo *existingTodo = calendar()->todo( todo->uid() );
01435     if ( existingTodo ) {
01436       kdDebug(5850) << "Drop existing Todo" << endl;
01437       Todo *oldTodo = existingTodo->clone();
01438       if ( mChanger && mChanger->beginChange( existingTodo ) ) {
01439         existingTodo->setDtDue( newTime );
01440         existingTodo->setFloats( allDay );
01441         existingTodo->setHasDueDate( true );
01442         mChanger->changeIncidence( oldTodo, existingTodo,
01443                                    KOGlobals::DATE_MODIFIED, this );
01444         mChanger->endChange( existingTodo );
01445       } else {
01446         KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
01447                             "because it cannot be locked.") );
01448       }
01449       delete oldTodo;
01450     } else {
01451       kdDebug(5850) << "Drop new Todo" << endl;
01452       todo->setDtDue( newTime );
01453       todo->setFloats( allDay );
01454       todo->setHasDueDate( true );
01455       if ( !mChanger->addIncidence( todo, 0, QString(), this ) ) {
01456         KODialogManager::errorSaveIncidence( this, todo );
01457       }
01458     }
01459   }
01460 }
01461 
01462 void KOAgendaView::startDrag( Incidence *incidence )
01463 {
01464 #ifndef KORG_NODND
01465   DndFactory factory( calendar() );
01466   ICalDrag *vd = factory.createDrag( incidence, this );
01467   if ( vd->drag() ) {
01468     kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl;
01469   }
01470 #endif
01471 }
01472 
01473 void KOAgendaView::readSettings()
01474 {
01475   readSettings(KOGlobals::self()->config());
01476 }
01477 
01478 void KOAgendaView::readSettings(KConfig *config)
01479 {
01480 //  kdDebug(5850) << "KOAgendaView::readSettings()" << endl;
01481 
01482   config->setGroup("Views");
01483 
01484 #ifndef KORG_NOSPLITTER
01485   QValueList<int> sizes = config->readIntListEntry("Separator AgendaView");
01486   if (sizes.count() == 2) {
01487     mSplitterAgenda->setSizes(sizes);
01488   }
01489 #endif
01490 
01491   updateConfig();
01492 }
01493 
01494 void KOAgendaView::writeSettings(KConfig *config)
01495 {
01496 //  kdDebug(5850) << "KOAgendaView::writeSettings()" << endl;
01497 
01498   config->setGroup("Views");
01499 
01500 #ifndef KORG_NOSPLITTER
01501   QValueList<int> list = mSplitterAgenda->sizes();
01502   config->writeEntry("Separator AgendaView",list);
01503 #endif
01504 }
01505 
01506 void KOAgendaView::setHolidayMasks()
01507 {
01508   if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) {
01509     return;
01510   }
01511 
01512   mHolidayMask.resize( mSelectedDates.count() + 1 );
01513 
01514   for( uint i = 0; i < mSelectedDates.count(); ++i ) {
01515     mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
01516   }
01517 
01518   // Store the information about the day before the visible area (needed for
01519   // overnight working hours) in the last bit of the mask:
01520   bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
01521   mHolidayMask[ mSelectedDates.count() ] = showDay;
01522 
01523   mAgenda->setHolidayMask( &mHolidayMask );
01524   mAllDayAgenda->setHolidayMask( &mHolidayMask );
01525 }
01526 
01527 void KOAgendaView::setContentsPos( int y )
01528 {
01529   mAgenda->setContentsPos( 0, y );
01530 }
01531 
01532 void KOAgendaView::setExpandedButton( bool expanded )
01533 {
01534   if ( !mExpandButton ) return;
01535 
01536   if ( expanded ) {
01537     mExpandButton->setPixmap( mExpandedPixmap );
01538   } else {
01539     mExpandButton->setPixmap( mNotExpandedPixmap );
01540   }
01541 }
01542 
01543 void KOAgendaView::clearSelection()
01544 {
01545   mAgenda->deselectItem();
01546   mAllDayAgenda->deselectItem();
01547 }
01548 
01549 void KOAgendaView::newTimeSpanSelectedAllDay( const QPoint &start, const QPoint &end )
01550 {
01551   newTimeSpanSelected( start, end );
01552   mTimeSpanInAllDay = true;
01553 }
01554 
01555 void KOAgendaView::newTimeSpanSelected( const QPoint &start, const QPoint &end )
01556 {
01557   if (!mSelectedDates.count()) return;
01558 
01559   mTimeSpanInAllDay = false;
01560 
01561   QDate dayStart = mSelectedDates[ kClamp( start.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01562   QDate dayEnd = mSelectedDates[ kClamp( end.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01563 
01564   QTime timeStart = mAgenda->gyToTime(start.y());
01565   QTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
01566 
01567   QDateTime dtStart(dayStart,timeStart);
01568   QDateTime dtEnd(dayEnd,timeEnd);
01569 
01570   mTimeSpanBegin = dtStart;
01571   mTimeSpanEnd = dtEnd;
01572 }
01573 
01574 void KOAgendaView::deleteSelectedDateTime()
01575 {
01576   mTimeSpanBegin.setDate(QDate());
01577   mTimeSpanEnd.setDate(QDate());
01578   mTimeSpanInAllDay = false;
01579 }
01580 
01581 void KOAgendaView::setTypeAheadReceiver( QObject *o )
01582 {
01583   mAgenda->setTypeAheadReceiver( o );
01584   mAllDayAgenda->setTypeAheadReceiver( o );
01585 }
01586 
01587 void KOAgendaView::finishTypeAhead()
01588 {
01589   mAgenda->finishTypeAhead();
01590   mAllDayAgenda->finishTypeAhead();
01591 }
01592 
01593 void KOAgendaView::removeIncidence( Incidence *incidence )
01594 {
01595   mAgenda->removeIncidence( incidence );
01596   mAllDayAgenda->removeIncidence( incidence );
01597 }
01598 
01599 void KOAgendaView::updateEventIndicators()
01600 {
01601   mMinY = mAgenda->minContentsY();
01602   mMaxY = mAgenda->maxContentsY();
01603 
01604   mAgenda->checkScrollBoundaries();
01605   updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
01606   updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
01607 }
01608 
01609 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
01610 {
01611   mChanger = changer;
01612   mAgenda->setIncidenceChanger( changer );
01613   mAllDayAgenda->setIncidenceChanger( changer );
01614 }
01615 
01616 void KOAgendaView::clearTimeSpanSelection()
01617 {
01618   mAgenda->clearSelection();
01619   mAllDayAgenda->clearSelection();
01620   deleteSelectedDateTime();
01621 }
01622 
01623 bool KOAgendaView::filterByResource( Incidence *incidence )
01624 {
01625   // Special handling for groupware to-dos that are in Task folders.
01626   // Put them in the top-level "Calendar" folder for lack of a better
01627   // place since we never show Task type folders even in the
01628   // multiagenda view.
01629   if ( resourceCalendar() && incidence->type() == "Todo" ) {
01630     QString subRes = resourceCalendar()->subresourceIdentifier( incidence );
01631     if ( resourceCalendar()->subresourceType( subRes ) == "todo" ) {
01632       if ( subResourceCalendar().contains( "/.INBOX.directory/Calendar" ) ) {
01633         return true;
01634       }
01635     }
01636   }
01637 
01638   // Normal handling
01639   if ( !resourceCalendar() )
01640     return true;
01641   CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() );
01642   if ( !calRes )
01643     return true;
01644   if ( calRes->resource( incidence ) != resourceCalendar() )
01645     return false;
01646   if ( !subResourceCalendar().isEmpty() ) {
01647     if ( resourceCalendar()->subresourceIdentifier( incidence ) != subResourceCalendar() )
01648       return false;
01649   }
01650   return true;
01651 }
01652 
01653 void KOAgendaView::resourcesChanged()
01654 {
01655   mPendingChanges = true;
01656 }
01657 
01658 void KOAgendaView::calendarIncidenceAdded(Incidence * incidence)
01659 {
01660   Q_UNUSED( incidence );
01661   mPendingChanges = true;
01662 }
01663 
01664 void KOAgendaView::calendarIncidenceChanged(Incidence * incidence)
01665 {
01666   Q_UNUSED( incidence );
01667   mPendingChanges = true;
01668 }
01669 
01670 void KOAgendaView::calendarIncidenceDeleted(Incidence * incidence)
01671 {
01672   Q_UNUSED( incidence );
01673   mPendingChanges = true;
01674 }
KDE Home | KDE Accessibility Home | Description of Access Keys