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->startUpdates();
00880   Incidence *oldIncidence = incidence->clone();
00881 
00882   QTime startTime( 0, 0, 0 ), endTime( 0, 0, 0 );
00883   if ( incidence->doesFloat() ) {
00884     daysLength = item->cellWidth() - 1;
00885   } else {
00886     startTime = mAgenda->gyToTime( item->cellYTop() );
00887     if ( item->lastMultiItem() ) {
00888       endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 );
00889       daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft();
00890       kdDebug(5850) << "item->lastMultiItem()->cellXLeft(): " << item->lastMultiItem()->cellXLeft()
00891                     << endl;
00892     } else if ( item->itemPos() == item->itemCount() && item->itemCount() > 1 ) {
00893       /* multiitem handling in agenda assumes two things:
00894          - The start (first KOAgendaItem) is always visible.
00895          - The first KOAgendaItem of the incidence has a non-null item->lastMultiItem()
00896              pointing to the last KOagendaItem.
00897 
00898         But those aren't always met, for example when in day-view.
00899         kolab/issue4417
00900        */
00901 
00902       // Cornercase 1: - Resizing the end of the event but the start isn't visible
00903       endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
00904       daysLength = item->itemCount() - 1;
00905       startTime = incidence->dtStart().time();
00906     } else if ( item->itemPos() == 1 && item->itemCount() > 1 ) {
00907       // Cornercase 2: - Resizing the start of the event but the end isn't visible
00908       endTime = incidence->dtEnd().time();
00909       daysLength = item->itemCount() - 1;
00910     } else {
00911       endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
00912     }
00913   }
00914 
00915   kdDebug(5850) << "daysLength: " << daysLength << "; startTime: " << startTime
00916                 << "; endTime: " << endTime << "; thisDate: " << thisDate
00917                 << "; incidence->dtStart(): " << incidence->dtStart() << endl;
00918 
00919   if ( incidence->type() == "Event" ) {
00920     startDt = incidence->dtStart();
00921     startDt = startDt.addDays( daysOffset );
00922     startDt.setTime( startTime );
00923     endDt = startDt.addDays( daysLength );
00924     endDt.setTime( endTime );
00925     Event* ev = static_cast<Event*>( incidence );
00926     if ( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) {
00927       // No change
00928       delete oldIncidence;
00929       incidence->cancelUpdates();
00930       return;
00931     }
00932     incidence->setDtStart( startDt );
00933     ev->setDtEnd( endDt );
00934   } else if ( incidence->type() == "Todo" ) {
00935     // To-do logic must be reviewed.
00936 
00937     Todo *td = static_cast<Todo*>( incidence );
00938     startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
00939     startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) );
00940     startDt.setTime( startTime );
00941     endDt.setDate( thisDate );
00942     endDt.setTime( endTime );
00943 
00944     if ( td->dtDue() == endDt ) {
00945       // No change
00946       delete oldIncidence;
00947       incidence->cancelUpdates();
00948       return;
00949     }
00950 
00951     if ( td->hasStartDate() ) {
00952       td->setDtStart( startDt );
00953     }
00954     td->setDtDue( endDt );
00955   }
00956 
00957   item->setItemDate( startDt.date() );
00958 
00959   KOIncidenceToolTip::remove( item );
00960   KOIncidenceToolTip::add( item, calendar(), incidence, thisDate, KOAgendaItem::toolTipGroup() );
00961 
00962 
00963   bool result;
00964   kdDebug() << "New date is " << incidence->dtStart() << endl;
00965   if ( addIncidence ) {
00966     result = mChanger->addIncidence( incidence, res, subRes, this, useLastGroupwareDialogAnswer );
00967   } else {
00968     result = mChanger->changeIncidence( oldIncidence, incidence,
00969                                                    KOGlobals::DATE_MODIFIED, this, useLastGroupwareDialogAnswer );
00970     mChanger->endChange( incidence, resourceCalendar(), subResourceCalendar() );
00971   }
00972 
00973   delete oldIncidence;
00974 
00975   if ( !result ) {
00976     mPendingChanges = true;
00977     QTimer::singleShot( 0, this, SLOT(updateView()) );
00978     incidence->cancelUpdates();
00979     return;
00980   } else {
00981     incidence->endUpdates();
00982   }
00983 
00984   // don't update the agenda as the item already has the correct coordinates.
00985   // an update would delete the current item and recreate it, but we are still
00986   // using a pointer to that item! => CRASH
00987   enableAgendaUpdate( false );
00988 
00989   // We need to do this in a timer to make sure we are not deleting the item
00990   // we are currently working on, which would lead to crashes
00991   // Only the actually moved agenda item is already at the correct position and mustn't be
00992   // recreated. All others have to!!!
00993   if ( incidence->doesRecur() ) {
00994     mUpdateItem = incidence;
00995     QTimer::singleShot( 0, this, SLOT( doUpdateItem() ) );
00996   }
00997 
00998   enableAgendaUpdate( true );
00999 }
01000 
01001 void KOAgendaView::doUpdateItem()
01002 {
01003   if ( mUpdateItem ) {
01004     changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED );
01005     mUpdateItem = 0;
01006   }
01007 }
01008 
01009 
01010 
01011 void KOAgendaView::showDates( const QDate &start, const QDate &end )
01012 {
01013 //  kdDebug(5850) << "KOAgendaView::selectDates" << endl;
01014   if ( !mSelectedDates.isEmpty() && mSelectedDates.first() == start
01015         && mSelectedDates.last() == end && !mPendingChanges )
01016     return;
01017 
01018   mSelectedDates.clear();
01019 
01020   QDate d = start;
01021   while ( d <= end ) {
01022     mSelectedDates.append( d );
01023     d = d.addDays( 1 );
01024   }
01025 
01026   mAreDatesInitialized = true;
01027 
01028   // and update the view
01029   fillAgenda();
01030 }
01031 
01032 
01033 void KOAgendaView::showIncidences( const Incidence::List &, const QDate & )
01034 {
01035   kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl;
01036 }
01037 
01038 void KOAgendaView::insertIncidence( Incidence *incidence, const QDate &curDate )
01039 {
01040   if ( !filterByResource( incidence ) ) {
01041     return;
01042   }
01043 
01044   // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's
01045   Event *event = dynamic_cast<Event *>( incidence );
01046   Todo  *todo  = dynamic_cast<Todo  *>( incidence );
01047 
01048   int curCol = mSelectedDates.first().daysTo( curDate );
01049 
01050   // In case incidence->dtStart() isn't visible (crosses bounderies)
01051   if ( curCol < 0 ) {
01052     curCol = 0;
01053   }
01054 
01055   // The date for the event is not displayed, just ignore it
01056   if ( curCol >= static_cast<int>( mSelectedDates.count() ) ) {
01057     return;
01058   }
01059 
01060   // Default values, which can never be reached
01061   mMinY[curCol] = mAgenda->timeToY( QTime( 23, 59 ) ) + 1;
01062   mMaxY[curCol] = mAgenda->timeToY( QTime( 0, 0 ) ) - 1;
01063 
01064   int beginX;
01065   int endX;
01066   QDate columnDate;
01067   if ( event ) {
01068     QDate firstVisibleDate = mSelectedDates.first();
01069     // its crossing bounderies, lets calculate beginX and endX
01070     if ( curDate < firstVisibleDate ) {
01071       beginX = curCol + firstVisibleDate.daysTo( curDate );
01072       endX   = beginX + event->dtStart().daysTo( event->dtEnd() );
01073       columnDate = firstVisibleDate;
01074     } else {
01075       beginX = curCol;
01076       endX   = beginX + event->dtStart().daysTo( event->dtEnd() );
01077       columnDate = curDate;
01078     }
01079   } else if ( todo ) {
01080     if ( !todo->hasDueDate() ) {
01081       return;  // todo shall not be displayed if it has no date
01082     }
01083     columnDate = curDate;
01084     beginX = endX = curCol;
01085 
01086   } else {
01087     return;
01088   }
01089   if ( todo && todo->isOverdue() ) {
01090     mAllDayAgenda->insertAllDayItem( incidence, columnDate, curCol, curCol );
01091   } else if ( incidence->doesFloat() ||
01092               ( todo &&
01093                   !todo->dtDue().isValid() ) ) {
01094       mAllDayAgenda->insertAllDayItem( incidence, columnDate, beginX, endX );
01095   } else if ( event && event->isMultiDay() ) {
01096     int startY = mAgenda->timeToY( event->dtStart().time() );
01097     QTime endtime = event->dtEnd().time();
01098     if ( endtime == QTime( 0, 0, 0 ) ) {
01099       endtime = QTime( 23, 59, 59 );
01100     }
01101     int endY = mAgenda->timeToY( endtime ) - 1;
01102     if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) {
01103       mAgenda->insertMultiItem( event, columnDate, beginX, endX, startY, endY );
01104 
01105     }
01106     if ( beginX == curCol ) {
01107       mMaxY[curCol] = mAgenda->timeToY( QTime( 23, 59 ) );
01108       if ( startY < mMinY[curCol] ) {
01109         mMinY[curCol] = startY;
01110       }
01111     } else if ( endX == curCol ) {
01112       mMinY[curCol] = mAgenda->timeToY( QTime( 0, 0 ) );
01113       if ( endY > mMaxY[curCol] ) {
01114         mMaxY[curCol] = endY;
01115       }
01116     } else {
01117       mMinY[curCol] = mAgenda->timeToY( QTime( 0, 0 ) );
01118       mMaxY[curCol] = mAgenda->timeToY( QTime( 23, 59 ) );
01119     }
01120   } else {
01121     int startY = 0, endY = 0;
01122     if ( event ) {
01123       startY = mAgenda->timeToY( incidence->dtStart().time() );
01124       QTime endtime = event->dtEnd().time();
01125       if ( endtime == QTime( 0, 0, 0 ) ) {
01126         endtime = QTime( 23, 59, 59 );
01127       }
01128       endY = mAgenda->timeToY( endtime ) - 1;
01129     }
01130     if ( todo ) {
01131       QTime t = todo->dtDue().time();
01132 
01133       if ( t == QTime( 0, 0 ) ) {
01134         t = QTime( 23, 59 );
01135       }
01136 
01137       int halfHour = 1800;
01138       if ( t.addSecs( -halfHour ) < t ) {
01139         startY = mAgenda->timeToY( t.addSecs( -halfHour ) );
01140         endY   = mAgenda->timeToY( t ) - 1;
01141       } else {
01142         startY = 0;
01143         endY   = mAgenda->timeToY( t.addSecs( halfHour ) ) - 1;
01144       }
01145     }
01146     if ( endY < startY ) {
01147       endY = startY;
01148     }
01149     mAgenda->insertItem( incidence, columnDate, curCol, startY, endY, 1, 1 );
01150     if ( startY < mMinY[curCol] ) {
01151       mMinY[curCol] = startY;
01152     }
01153     if ( endY > mMaxY[curCol] ) {
01154       mMaxY[curCol] = endY;
01155     }
01156   }
01157 }
01158 
01159 void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence )
01160 {
01161   Todo *todo = dynamic_cast<Todo *>(incidence);
01162   CalFilter *filter = calendar()->filter();
01163   if ( ( filter && !filter->filterIncidence( incidence ) ) ||
01164        ( ( todo && !KOPrefs::instance()->showAllDayTodo() ) ) ) {
01165     return;
01166   }
01167 
01168   displayIncidence( incidence );
01169 }
01170 
01171 void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode )
01172 {
01173   switch ( mode ) {
01174     case KOGlobals::INCIDENCEADDED:
01175     {
01176       // Add an event. No need to recreate the whole view!
01177       // recreating everything even causes troubles: dropping to the
01178       // day matrix recreates the agenda items, but the evaluation is
01179       // still in an agendaItems' code, which was deleted in the mean time.
01180       // Thus KOrg crashes...
01181       changeIncidenceDisplayAdded( incidence );
01182       updateEventIndicators();
01183       break;
01184     }
01185     case KOGlobals::INCIDENCEEDITED:
01186     {
01187       if ( mAllowAgendaUpdate ) {
01194         removeIncidence( incidence, false  );
01195         changeIncidenceDisplayAdded( incidence );
01196       }
01197       updateEventIndicators();
01198       break;
01199     }
01200     case KOGlobals::INCIDENCEDELETED:
01201     {
01202       removeIncidence( incidence );
01203       updateEventIndicators();
01204       break;
01205     }
01206     default:
01207       return;
01208   }
01209 
01210   // HACK: Update the view if the all-day agenda has been modified.
01211   // Do this because there are some layout problems in the
01212   // all-day agenda that are not easily solved, but clearing
01213   // and redrawing works ok.
01214   if ( incidence->doesFloat() ) {
01215     updateView();
01216   }
01217 }
01218 
01219 void KOAgendaView::fillAgenda( const QDate & )
01220 {
01221   fillAgenda();
01222 }
01223 
01224 void KOAgendaView::fillAgenda()
01225 {
01226   if ( !mAreDatesInitialized ) {
01227     return;
01228   }
01229 
01230   mPendingChanges = false;
01231 
01232   /* Remember the uids of the selected items. In case one of the
01233    * items was deleted and re-added, we want to reselect it. */
01234   const QString &selectedAgendaUid = mAgenda->lastSelectedUid();
01235   const QString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
01236 
01237   enableAgendaUpdate( true );
01238   clearView();
01239 
01240   mAllDayAgenda->changeColumns( mSelectedDates.count() );
01241   mAgenda->changeColumns( mSelectedDates.count() );
01242   mEventIndicatorTop->changeColumns( mSelectedDates.count() );
01243   mEventIndicatorBottom->changeColumns( mSelectedDates.count() );
01244 
01245   createDayLabels( false );
01246   setHolidayMasks();
01247 
01248   mMinY.resize( mSelectedDates.count() );
01249   mMaxY.resize( mSelectedDates.count() );
01250 
01251   mAgenda->setDateList( mSelectedDates );
01252 
01253   bool somethingReselected = false;
01254   Incidence::List incidences = calendar()->incidences();
01255 
01256   for ( Incidence::List::ConstIterator it = incidences.begin(); it!=incidences.constEnd(); ++it ) {
01257     Incidence *incidence = (*it);
01258     displayIncidence( incidence );
01259 
01260     if( incidence->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
01261       mAgenda->selectItemByUID( incidence->uid() );
01262       somethingReselected = true;
01263     }
01264 
01265     if( incidence->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
01266       mAllDayAgenda->selectItemByUID( incidence->uid() );
01267       somethingReselected = true;
01268     }
01269 
01270   }
01271 
01272   mAgenda->checkScrollBoundaries();
01273   updateEventIndicators();
01274 
01275   //  mAgenda->viewport()->update();
01276   //  mAllDayAgenda->viewport()->update();
01277 
01278   // make invalid
01279   deleteSelectedDateTime();
01280 
01281   if( !somethingReselected ) {
01282     emit incidenceSelected( 0, QDate() );
01283   }
01284 }
01285 
01286 void KOAgendaView::displayIncidence( Incidence *incidence )
01287 {
01288   QDate today = QDate::currentDate();
01289   DateTimeList::iterator t;
01290 
01291   // FIXME: use a visitor here
01292   Todo *todo = dynamic_cast<Todo *>( incidence );
01293   Event *event = dynamic_cast<Event *>( incidence );
01294 
01295   QDateTime firstVisibleDateTime = mSelectedDates.first();
01296   QDateTime lastVisibleDateTime = mSelectedDates.last();
01297 
01298   lastVisibleDateTime.setTime( QTime( 23, 59, 59, 59 ) );
01299   firstVisibleDateTime.setTime( QTime( 0, 0 ) );
01300   DateTimeList dateTimeList;
01301 
01302   QDateTime incDtStart = incidence->dtStart();
01303   QDateTime incDtEnd   = incidence->dtEnd();
01304 
01305   if ( todo &&
01306        ( !KOPrefs::instance()->showAllDayTodo() || !todo->hasDueDate() ) ) {
01307     return;
01308   }
01309 
01310   if ( incidence->doesRecur() ) {
01311     int eventDuration = event ? incDtStart.daysTo( incDtEnd ) : 0;
01312 
01313     // if there's a multiday event that starts before firstVisibleDateTime but ends after
01314     // lets include it. timesInInterval() ignores incidences that aren't totaly inside
01315     // the range
01316     QDateTime startDateTimeWithOffset = firstVisibleDateTime.addDays( -eventDuration );
01317     dateTimeList =
01318       incidence->recurrence()->timesInInterval( startDateTimeWithOffset,
01319                                                 lastVisibleDateTime );
01320   } else {
01321     QDateTime dateToAdd; // date to add to our date list
01322     QDateTime incidenceStart;
01323     QDateTime incidenceEnd;
01324 
01325     if ( todo && todo->hasDueDate() && !todo->isOverdue() ) {
01326       // If it's not overdue it will be shown at the original date (not today)
01327       dateToAdd = todo->dtDue();
01328 
01329       // To-dos are drawn with the bottom of the rectangle at dtDue
01330       // if dtDue is at 00:00, then it should be displayed in the previous day, at 23:59
01331       if ( !todo->doesFloat() && dateToAdd.time() == QTime( 0, 0 ) ) {
01332         dateToAdd = dateToAdd.addSecs( -1 );
01333       }
01334 
01335       incidenceEnd = dateToAdd;
01336     } else if ( event ) {
01337       dateToAdd = incDtStart;
01338       incidenceEnd = incDtEnd;
01339     }
01340 
01341     if ( incidence->doesFloat() ) {
01342       // so comparisons with < > actually work
01343       dateToAdd.setTime( QTime( 0, 0 ) );
01344       incidenceEnd.setTime( QTime( 23, 59, 59, 59 ) );
01345     }
01346 
01347     if ( dateToAdd <= lastVisibleDateTime && incidenceEnd > firstVisibleDateTime ) {
01348       dateTimeList += dateToAdd;
01349     }
01350   }
01351 
01352   // ToDo items shall be displayed today if they are already overdude
01353   QDateTime dateTimeToday = today;
01354   if ( todo &&
01355        todo->isOverdue() &&
01356        dateTimeToday >= firstVisibleDateTime &&
01357        dateTimeToday <= lastVisibleDateTime ) {
01358 
01359     bool doAdd = true;
01360 
01361     if ( todo->doesRecur() ) {
01362       /* If there's a recurring instance showing up today don't add "today" again
01363        * we don't want the event to appear duplicated */
01364       for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01365         if ( (*t).date() == today ) {
01366           doAdd = false;
01367           break;
01368        }
01369       }
01370     }
01371 
01372     if ( doAdd ) {
01373       dateTimeList += dateTimeToday;
01374     }
01375   }
01376 
01377   const bool makesDayBusy = KOEventView::makesWholeDayBusy( incidence );
01378   for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01379     if ( makesDayBusy ) {
01380       Event::List &busyEvents = mBusyDays[(*t).date()];
01381       busyEvents.append( event );
01382     }
01383     insertIncidence( incidence, (*t).date() );
01384   }
01385 
01386   // Can be multiday
01387   if ( event && makesDayBusy && event->isMultiDay() ) {
01388     const QDate lastVisibleDate = mSelectedDates.last();
01389     for ( QDate date = event->dtStart().date();
01390           date <= event->dtEnd().date() && date <= lastVisibleDate ;
01391           date = date.addDays( 1 ) ) {
01392       Event::List &busyEvents = mBusyDays[date];
01393       busyEvents.append( event );
01394     }
01395   }
01396 }
01397 
01398 void KOAgendaView::clearView()
01399 {
01400 //  kdDebug(5850) << "ClearView" << endl;
01401   mAllDayAgenda->clear();
01402   mAgenda->clear();
01403   mBusyDays.clear();
01404 }
01405 
01406 CalPrinterBase::PrintType KOAgendaView::printType()
01407 {
01408   if ( currentDateCount() == 1 ) return CalPrinterBase::Day;
01409   else return CalPrinterBase::Week;
01410 }
01411 
01412 void KOAgendaView::updateEventIndicatorTop( int newY )
01413 {
01414   uint i;
01415   for( i = 0; i < mMinY.size(); ++i ) {
01416     mEventIndicatorTop->enableColumn( i, newY > mMinY[i] );
01417   }
01418   mEventIndicatorTop->update();
01419 }
01420 
01421 void KOAgendaView::updateEventIndicatorBottom( int newY )
01422 {
01423   uint i;
01424   for( i = 0; i < mMaxY.size(); ++i ) {
01425     mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
01426   }
01427   mEventIndicatorBottom->update();
01428 }
01429 
01430 void KOAgendaView::slotTodoDropped( Todo *todo, const QPoint &gpos, bool allDay )
01431 {
01432   if ( gpos.x()<0 || gpos.y()<0 ) return;
01433   QDate day = mSelectedDates[gpos.x()];
01434   QTime time = mAgenda->gyToTime( gpos.y() );
01435   QDateTime newTime( day, time );
01436 
01437   if ( todo ) {
01438     Todo *existingTodo = calendar()->todo( todo->uid() );
01439     if ( existingTodo ) {
01440       kdDebug(5850) << "Drop existing Todo" << endl;
01441       Todo *oldTodo = existingTodo->clone();
01442       if ( mChanger &&
01443            mChanger->beginChange( existingTodo, resourceCalendar(), subResourceCalendar() ) ) {
01444         existingTodo->setDtDue( newTime );
01445         existingTodo->setFloats( allDay );
01446         existingTodo->setHasDueDate( true );
01447         mChanger->changeIncidence( oldTodo, existingTodo,
01448                                    KOGlobals::DATE_MODIFIED, this );
01449         mChanger->endChange( existingTodo, resourceCalendar(), subResourceCalendar() );
01450       } else {
01451         KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
01452                             "because it cannot be locked.") );
01453       }
01454       delete oldTodo;
01455     } else {
01456       kdDebug(5850) << "Drop new Todo" << endl;
01457       todo->setDtDue( newTime );
01458       todo->setFloats( allDay );
01459       todo->setHasDueDate( true );
01460       if ( !mChanger->addIncidence( todo, 0, QString(), this ) ) {
01461         KODialogManager::errorSaveIncidence( this, todo );
01462       }
01463     }
01464   }
01465 }
01466 
01467 void KOAgendaView::startDrag( Incidence *incidence )
01468 {
01469 #ifndef KORG_NODND
01470   DndFactory factory( calendar() );
01471   ICalDrag *vd = factory.createDrag( incidence, this );
01472   if ( vd->drag() ) {
01473     kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl;
01474   }
01475 #endif
01476 }
01477 
01478 void KOAgendaView::readSettings()
01479 {
01480   readSettings(KOGlobals::self()->config());
01481 }
01482 
01483 void KOAgendaView::readSettings(KConfig *config)
01484 {
01485 //  kdDebug(5850) << "KOAgendaView::readSettings()" << endl;
01486 
01487   config->setGroup("Views");
01488 
01489 #ifndef KORG_NOSPLITTER
01490   QValueList<int> sizes = config->readIntListEntry("Separator AgendaView");
01491   if (sizes.count() == 2) {
01492     mSplitterAgenda->setSizes(sizes);
01493   }
01494 #endif
01495 
01496   updateConfig();
01497 }
01498 
01499 void KOAgendaView::writeSettings(KConfig *config)
01500 {
01501 //  kdDebug(5850) << "KOAgendaView::writeSettings()" << endl;
01502 
01503   config->setGroup("Views");
01504 
01505 #ifndef KORG_NOSPLITTER
01506   QValueList<int> list = mSplitterAgenda->sizes();
01507   config->writeEntry("Separator AgendaView",list);
01508 #endif
01509 }
01510 
01511 void KOAgendaView::setHolidayMasks()
01512 {
01513   if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) {
01514     return;
01515   }
01516 
01517   mHolidayMask.resize( mSelectedDates.count() + 1 );
01518 
01519   for( uint i = 0; i < mSelectedDates.count(); ++i ) {
01520     mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
01521   }
01522 
01523   // Store the information about the day before the visible area (needed for
01524   // overnight working hours) in the last bit of the mask:
01525   bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
01526   mHolidayMask[ mSelectedDates.count() ] = showDay;
01527 
01528   mAgenda->setHolidayMask( &mHolidayMask );
01529   mAllDayAgenda->setHolidayMask( &mHolidayMask );
01530 }
01531 
01532 QMemArray<bool> KOAgendaView::busyDayMask()
01533 {
01534   if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) {
01535     return QMemArray<bool>();
01536   }
01537 
01538   QMemArray<bool> busyDayMask;
01539   busyDayMask.resize( mSelectedDates.count() );
01540 
01541   for( uint i = 0; i < mSelectedDates.count(); ++i ) {
01542     busyDayMask[i] = !mBusyDays[mSelectedDates[i]].isEmpty();
01543   }
01544 
01545   return busyDayMask;
01546 }
01547 
01548 void KOAgendaView::setContentsPos( int y )
01549 {
01550   mAgenda->setContentsPos( 0, y );
01551 }
01552 
01553 void KOAgendaView::setExpandedButton( bool expanded )
01554 {
01555   if ( !mExpandButton ) return;
01556 
01557   if ( expanded ) {
01558     mExpandButton->setPixmap( mExpandedPixmap );
01559   } else {
01560     mExpandButton->setPixmap( mNotExpandedPixmap );
01561   }
01562 }
01563 
01564 void KOAgendaView::clearSelection()
01565 {
01566   mAgenda->deselectItem();
01567   mAllDayAgenda->deselectItem();
01568 }
01569 
01570 void KOAgendaView::newTimeSpanSelectedAllDay( const QPoint &start, const QPoint &end )
01571 {
01572   newTimeSpanSelected( start, end );
01573   mTimeSpanInAllDay = true;
01574 }
01575 
01576 void KOAgendaView::newTimeSpanSelected( const QPoint &start, const QPoint &end )
01577 {
01578   if (!mSelectedDates.count()) return;
01579 
01580   mTimeSpanInAllDay = false;
01581 
01582   QDate dayStart = mSelectedDates[ kClamp( start.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01583   QDate dayEnd = mSelectedDates[ kClamp( end.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01584 
01585   QTime timeStart = mAgenda->gyToTime(start.y());
01586   QTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
01587 
01588   QDateTime dtStart(dayStart,timeStart);
01589   QDateTime dtEnd(dayEnd,timeEnd);
01590 
01591   mTimeSpanBegin = dtStart;
01592   mTimeSpanEnd = dtEnd;
01593 }
01594 
01595 void KOAgendaView::deleteSelectedDateTime()
01596 {
01597   mTimeSpanBegin.setDate(QDate());
01598   mTimeSpanEnd.setDate(QDate());
01599   mTimeSpanInAllDay = false;
01600 }
01601 
01602 void KOAgendaView::setTypeAheadReceiver( QObject *o )
01603 {
01604   mAgenda->setTypeAheadReceiver( o );
01605   mAllDayAgenda->setTypeAheadReceiver( o );
01606 }
01607 
01608 void KOAgendaView::finishTypeAhead()
01609 {
01610   mAgenda->finishTypeAhead();
01611   mAllDayAgenda->finishTypeAhead();
01612 }
01613 
01614 void KOAgendaView::removeIncidence( Incidence *incidence, bool relayoutNeighbours )
01615 {
01616   mAgenda->removeIncidence( incidence, relayoutNeighbours );
01617   mAllDayAgenda->removeIncidence( incidence, relayoutNeighbours );
01618 }
01619 
01620 void KOAgendaView::updateEventIndicators()
01621 {
01622   mMinY = mAgenda->minContentsY();
01623   mMaxY = mAgenda->maxContentsY();
01624 
01625   mAgenda->checkScrollBoundaries();
01626   updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
01627   updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
01628 }
01629 
01630 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
01631 {
01632   mChanger = changer;
01633   mAgenda->setIncidenceChanger( changer );
01634   mAllDayAgenda->setIncidenceChanger( changer );
01635 }
01636 
01637 void KOAgendaView::clearTimeSpanSelection()
01638 {
01639   mAgenda->clearSelection();
01640   mAllDayAgenda->clearSelection();
01641   deleteSelectedDateTime();
01642 }
01643 
01644 bool KOAgendaView::filterByResource( Incidence *incidence )
01645 {
01646   // Special handling for groupware to-dos that are in Task folders.
01647   // Put them in the top-level "Calendar" folder for lack of a better
01648   // place since we never show Task type folders even in the
01649   // multiagenda view.
01650   if ( resourceCalendar() && incidence->type() == "Todo" ) {
01651     QString subRes = resourceCalendar()->subresourceIdentifier( incidence );
01652     if ( resourceCalendar()->subresourceType( subRes ) == "todo" ) {
01653       QString calmatch = "/.INBOX.directory/Calendar";
01654       QString i18nmatch = "/.INBOX.directory/" + i18n( "Calendar" );
01655       if ( subResourceCalendar().contains( calmatch ) ||
01656            subResourceCalendar().contains( i18nmatch ) ) {
01657         return true;
01658       }
01659     }
01660   }
01661 
01662   // Normal handling
01663   if ( !resourceCalendar() )
01664     return true;
01665   CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() );
01666   if ( !calRes )
01667     return true;
01668   if ( calRes->resource( incidence ) != resourceCalendar() )
01669     return false;
01670   if ( !subResourceCalendar().isEmpty() ) {
01671     if ( resourceCalendar()->subresourceIdentifier( incidence ) != subResourceCalendar() )
01672       return false;
01673   }
01674   return true;
01675 }
01676 
01677 void KOAgendaView::resourcesChanged()
01678 {
01679   mPendingChanges = true;
01680 }
01681 
01682 void KOAgendaView::calendarIncidenceAdded(Incidence * incidence)
01683 {
01684   Q_UNUSED( incidence );
01685   mPendingChanges = true;
01686 }
01687 
01688 void KOAgendaView::calendarIncidenceChanged(Incidence * incidence)
01689 {
01690   Q_UNUSED( incidence );
01691   mPendingChanges = true;
01692 }
01693 
01694 void KOAgendaView::calendarIncidenceDeleted(Incidence * incidence)
01695 {
01696   Q_UNUSED( incidence );
01697   mPendingChanges = true;
01698 }
KDE Home | KDE Accessibility Home | Description of Access Keys