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