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