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 >= 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 ) {
01183         removeIncidence( incidence );
01184         changeIncidenceDisplayAdded( incidence );
01185       }
01186       updateEventIndicators();
01187       break;
01188     }
01189     case KOGlobals::INCIDENCEDELETED:
01190     {
01191       removeIncidence( incidence );
01192       updateEventIndicators();
01193       break;
01194     }
01195     default:
01196       return;
01197   }
01198 
01199   // HACK: Update the view if the all-day agenda has been modified.
01200   // Do this because there are some layout problems in the
01201   // all-day agenda that are not easily solved, but clearing
01202   // and redrawing works ok.
01203   if ( incidence->doesFloat() ) {
01204     updateView();
01205   }
01206 }
01207 
01208 void KOAgendaView::fillAgenda( const QDate & )
01209 {
01210   fillAgenda();
01211 }
01212 
01213 void KOAgendaView::fillAgenda()
01214 {
01215   if ( !mAreDatesInitialized ) {
01216     return;
01217   }
01218 
01219   mPendingChanges = false;
01220 
01221   /* Remember the uids of the selected items. In case one of the
01222    * items was deleted and re-added, we want to reselect it. */
01223   const QString &selectedAgendaUid = mAgenda->lastSelectedUid();
01224   const QString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
01225 
01226   enableAgendaUpdate( true );
01227   clearView();
01228 
01229   mAllDayAgenda->changeColumns( mSelectedDates.count() );
01230   mAgenda->changeColumns( mSelectedDates.count() );
01231   mEventIndicatorTop->changeColumns( mSelectedDates.count() );
01232   mEventIndicatorBottom->changeColumns( mSelectedDates.count() );
01233 
01234   createDayLabels( false );
01235   setHolidayMasks();
01236 
01237   mMinY.resize( mSelectedDates.count() );
01238   mMaxY.resize( mSelectedDates.count() );
01239 
01240   mAgenda->setDateList( mSelectedDates );
01241 
01242   bool somethingReselected = false;
01243   Incidence::List incidences = calendar()->incidences();
01244 
01245   for ( Incidence::List::ConstIterator it = incidences.begin(); it!=incidences.constEnd(); ++it ) {
01246     Incidence *incidence = (*it);
01247     displayIncidence( incidence );
01248 
01249     if( incidence->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
01250       mAgenda->selectItemByUID( incidence->uid() );
01251       somethingReselected = true;
01252     }
01253 
01254     if( incidence->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
01255       mAllDayAgenda->selectItemByUID( incidence->uid() );
01256       somethingReselected = true;
01257     }
01258 
01259   }
01260 
01261   mAgenda->checkScrollBoundaries();
01262   updateEventIndicators();
01263 
01264   //  mAgenda->viewport()->update();
01265   //  mAllDayAgenda->viewport()->update();
01266 
01267   // make invalid
01268   deleteSelectedDateTime();
01269 
01270   if( !somethingReselected ) {
01271     emit incidenceSelected( 0, QDate() );
01272   }
01273 }
01274 
01275 void KOAgendaView::displayIncidence( Incidence *incidence )
01276 {
01277   QDate today = QDate::currentDate();
01278   DateTimeList::iterator t;
01279 
01280   // FIXME: use a visitor here
01281   Todo *todo = dynamic_cast<Todo *>( incidence );
01282   Event *event = dynamic_cast<Event *>( incidence );
01283 
01284   QDateTime firstVisibleDateTime = mSelectedDates.first();
01285   QDateTime lastVisibleDateTime = mSelectedDates.last();
01286 
01287   lastVisibleDateTime.setTime( QTime( 23, 59, 59, 59 ) );
01288   firstVisibleDateTime.setTime( QTime( 0, 0 ) );
01289   DateTimeList dateTimeList;
01290 
01291   QDateTime incDtStart = incidence->dtStart();
01292   QDateTime incDtEnd   = incidence->dtEnd();
01293 
01294   if ( todo &&
01295        ( !KOPrefs::instance()->showAllDayTodo() || !todo->hasDueDate() ) ) {
01296     return;
01297   }
01298 
01299   if ( incidence->doesRecur() ) {
01300     int eventDuration = event ? incDtStart.daysTo( incDtEnd ) : 0;
01301 
01302     // if there's a multiday event that starts before firstVisibleDateTime but ends after
01303     // lets include it. timesInInterval() ignores incidences that aren't totaly inside
01304     // the range
01305     QDateTime startDateTimeWithOffset = firstVisibleDateTime.addDays( -eventDuration );
01306     dateTimeList =
01307       incidence->recurrence()->timesInInterval( startDateTimeWithOffset,
01308                                                 lastVisibleDateTime );
01309   } else {
01310     QDateTime dateToAdd; // date to add to our date list
01311     QDateTime incidenceStart;
01312     QDateTime incidenceEnd;
01313 
01314     if ( todo && todo->hasDueDate() && !todo->isOverdue() ) {
01315       // If it's not overdue it will be shown at the original date (not today)
01316       dateToAdd = todo->dtDue();
01317 
01318       // To-dos are drawn with the bottom of the rectangle at dtDue
01319       // if dtDue is at 00:00, then it should be displayed in the previous day, at 23:59
01320       if ( !todo->doesFloat() && dateToAdd.time() == QTime( 0, 0 ) ) {
01321         dateToAdd = dateToAdd.addSecs( -1 );
01322       }
01323 
01324       incidenceEnd = dateToAdd;
01325     } else if ( event ) {
01326       dateToAdd = incDtStart;
01327       incidenceEnd = incDtEnd;
01328     }
01329 
01330     if ( incidence->doesFloat() ) {
01331       // so comparisons with < > actually work
01332       dateToAdd.setTime( QTime( 0, 0 ) );
01333       incidenceEnd.setTime( QTime( 23, 59, 59, 59 ) );
01334     }
01335 
01336     if ( dateToAdd <= lastVisibleDateTime && incidenceEnd > firstVisibleDateTime ) {
01337       dateTimeList += dateToAdd;
01338     }
01339   }
01340 
01341   // ToDo items shall be displayed today if they are already overdude
01342   QDateTime dateTimeToday = today;
01343   if ( todo &&
01344        todo->isOverdue() &&
01345        dateTimeToday >= firstVisibleDateTime &&
01346        dateTimeToday <= lastVisibleDateTime ) {
01347 
01348     bool doAdd = true;
01349 
01350     if ( todo->doesRecur() ) {
01351       /* If there's a recurring instance showing up today don't add "today" again
01352        * we don't want the event to appear duplicated */
01353       for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01354         if ( (*t).date() == today ) {
01355           doAdd = false;
01356           break;
01357        }
01358       }
01359     }
01360 
01361     if ( doAdd ) {
01362       dateTimeList += dateTimeToday;
01363     }
01364   }
01365 
01366   const bool busyDay = KOEventView::makesWholeDayBusy( incidence );
01367   for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01368     if ( busyDay ) {
01369       Event::List &busyEvents = mBusyDays[(*t).date()];
01370       busyEvents.append( event );
01371     }
01372 
01373     insertIncidence( incidence, (*t).date() );
01374   }
01375 }
01376 
01377 void KOAgendaView::clearView()
01378 {
01379 //  kdDebug(5850) << "ClearView" << endl;
01380   mAllDayAgenda->clear();
01381   mAgenda->clear();
01382   mBusyDays.clear();
01383 }
01384 
01385 CalPrinterBase::PrintType KOAgendaView::printType()
01386 {
01387   if ( currentDateCount() == 1 ) return CalPrinterBase::Day;
01388   else return CalPrinterBase::Week;
01389 }
01390 
01391 void KOAgendaView::updateEventIndicatorTop( int newY )
01392 {
01393   uint i;
01394   for( i = 0; i < mMinY.size(); ++i ) {
01395     mEventIndicatorTop->enableColumn( i, newY > mMinY[i] );
01396   }
01397   mEventIndicatorTop->update();
01398 }
01399 
01400 void KOAgendaView::updateEventIndicatorBottom( int newY )
01401 {
01402   uint i;
01403   for( i = 0; i < mMaxY.size(); ++i ) {
01404     mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
01405   }
01406   mEventIndicatorBottom->update();
01407 }
01408 
01409 void KOAgendaView::slotTodoDropped( Todo *todo, const QPoint &gpos, bool allDay )
01410 {
01411   if ( gpos.x()<0 || gpos.y()<0 ) return;
01412   QDate day = mSelectedDates[gpos.x()];
01413   QTime time = mAgenda->gyToTime( gpos.y() );
01414   QDateTime newTime( day, time );
01415 
01416   if ( todo ) {
01417     Todo *existingTodo = calendar()->todo( todo->uid() );
01418     if ( existingTodo ) {
01419       kdDebug(5850) << "Drop existing Todo" << endl;
01420       Todo *oldTodo = existingTodo->clone();
01421       if ( mChanger &&
01422            mChanger->beginChange( existingTodo, resourceCalendar(), subResourceCalendar() ) ) {
01423         existingTodo->setDtDue( newTime );
01424         existingTodo->setFloats( allDay );
01425         existingTodo->setHasDueDate( true );
01426         mChanger->changeIncidence( oldTodo, existingTodo,
01427                                    KOGlobals::DATE_MODIFIED, this );
01428         mChanger->endChange( existingTodo, resourceCalendar(), subResourceCalendar() );
01429       } else {
01430         KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
01431                             "because it cannot be locked.") );
01432       }
01433       delete oldTodo;
01434     } else {
01435       kdDebug(5850) << "Drop new Todo" << endl;
01436       todo->setDtDue( newTime );
01437       todo->setFloats( allDay );
01438       todo->setHasDueDate( true );
01439       if ( !mChanger->addIncidence( todo, 0, QString(), this ) ) {
01440         KODialogManager::errorSaveIncidence( this, todo );
01441       }
01442     }
01443   }
01444 }
01445 
01446 void KOAgendaView::startDrag( Incidence *incidence )
01447 {
01448 #ifndef KORG_NODND
01449   DndFactory factory( calendar() );
01450   ICalDrag *vd = factory.createDrag( incidence, this );
01451   if ( vd->drag() ) {
01452     kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl;
01453   }
01454 #endif
01455 }
01456 
01457 void KOAgendaView::readSettings()
01458 {
01459   readSettings(KOGlobals::self()->config());
01460 }
01461 
01462 void KOAgendaView::readSettings(KConfig *config)
01463 {
01464 //  kdDebug(5850) << "KOAgendaView::readSettings()" << endl;
01465 
01466   config->setGroup("Views");
01467 
01468 #ifndef KORG_NOSPLITTER
01469   QValueList<int> sizes = config->readIntListEntry("Separator AgendaView");
01470   if (sizes.count() == 2) {
01471     mSplitterAgenda->setSizes(sizes);
01472   }
01473 #endif
01474 
01475   updateConfig();
01476 }
01477 
01478 void KOAgendaView::writeSettings(KConfig *config)
01479 {
01480 //  kdDebug(5850) << "KOAgendaView::writeSettings()" << endl;
01481 
01482   config->setGroup("Views");
01483 
01484 #ifndef KORG_NOSPLITTER
01485   QValueList<int> list = mSplitterAgenda->sizes();
01486   config->writeEntry("Separator AgendaView",list);
01487 #endif
01488 }
01489 
01490 void KOAgendaView::setHolidayMasks()
01491 {
01492   if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) {
01493     return;
01494   }
01495 
01496   mHolidayMask.resize( mSelectedDates.count() + 1 );
01497 
01498   for( uint i = 0; i < mSelectedDates.count(); ++i ) {
01499     mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
01500   }
01501 
01502   // Store the information about the day before the visible area (needed for
01503   // overnight working hours) in the last bit of the mask:
01504   bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
01505   mHolidayMask[ mSelectedDates.count() ] = showDay;
01506 
01507   mAgenda->setHolidayMask( &mHolidayMask );
01508   mAllDayAgenda->setHolidayMask( &mHolidayMask );
01509 }
01510 
01511 QMemArray<bool> KOAgendaView::busyDayMask()
01512 {
01513   if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) {
01514     return QMemArray<bool>();
01515   }
01516 
01517   QMemArray<bool> busyDayMask;
01518   busyDayMask.resize( mSelectedDates.count() );
01519 
01520   for( uint i = 0; i < mSelectedDates.count(); ++i ) {
01521     busyDayMask[i] = !mBusyDays[mSelectedDates[i]].isEmpty();
01522   }
01523 
01524   return busyDayMask;
01525 }
01526 
01527 void KOAgendaView::setContentsPos( int y )
01528 {
01529   mAgenda->setContentsPos( 0, y );
01530 }
01531 
01532 void KOAgendaView::setExpandedButton( bool expanded )
01533 {
01534   if ( !mExpandButton ) return;
01535 
01536   if ( expanded ) {
01537     mExpandButton->setPixmap( mExpandedPixmap );
01538   } else {
01539     mExpandButton->setPixmap( mNotExpandedPixmap );
01540   }
01541 }
01542 
01543 void KOAgendaView::clearSelection()
01544 {
01545   mAgenda->deselectItem();
01546   mAllDayAgenda->deselectItem();
01547 }
01548 
01549 void KOAgendaView::newTimeSpanSelectedAllDay( const QPoint &start, const QPoint &end )
01550 {
01551   newTimeSpanSelected( start, end );
01552   mTimeSpanInAllDay = true;
01553 }
01554 
01555 void KOAgendaView::newTimeSpanSelected( const QPoint &start, const QPoint &end )
01556 {
01557   if (!mSelectedDates.count()) return;
01558 
01559   mTimeSpanInAllDay = false;
01560 
01561   QDate dayStart = mSelectedDates[ kClamp( start.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01562   QDate dayEnd = mSelectedDates[ kClamp( end.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01563 
01564   QTime timeStart = mAgenda->gyToTime(start.y());
01565   QTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
01566 
01567   QDateTime dtStart(dayStart,timeStart);
01568   QDateTime dtEnd(dayEnd,timeEnd);
01569 
01570   mTimeSpanBegin = dtStart;
01571   mTimeSpanEnd = dtEnd;
01572 }
01573 
01574 void KOAgendaView::deleteSelectedDateTime()
01575 {
01576   mTimeSpanBegin.setDate(QDate());
01577   mTimeSpanEnd.setDate(QDate());
01578   mTimeSpanInAllDay = false;
01579 }
01580 
01581 void KOAgendaView::setTypeAheadReceiver( QObject *o )
01582 {
01583   mAgenda->setTypeAheadReceiver( o );
01584   mAllDayAgenda->setTypeAheadReceiver( o );
01585 }
01586 
01587 void KOAgendaView::finishTypeAhead()
01588 {
01589   mAgenda->finishTypeAhead();
01590   mAllDayAgenda->finishTypeAhead();
01591 }
01592 
01593 void KOAgendaView::removeIncidence( Incidence *incidence )
01594 {
01595   mAgenda->removeIncidence( incidence );
01596   mAllDayAgenda->removeIncidence( incidence );
01597 }
01598 
01599 void KOAgendaView::updateEventIndicators()
01600 {
01601   mMinY = mAgenda->minContentsY();
01602   mMaxY = mAgenda->maxContentsY();
01603 
01604   mAgenda->checkScrollBoundaries();
01605   updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
01606   updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
01607 }
01608 
01609 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
01610 {
01611   mChanger = changer;
01612   mAgenda->setIncidenceChanger( changer );
01613   mAllDayAgenda->setIncidenceChanger( changer );
01614 }
01615 
01616 void KOAgendaView::clearTimeSpanSelection()
01617 {
01618   mAgenda->clearSelection();
01619   mAllDayAgenda->clearSelection();
01620   deleteSelectedDateTime();
01621 }
01622 
01623 bool KOAgendaView::filterByResource( Incidence *incidence )
01624 {
01625   // Special handling for groupware to-dos that are in Task folders.
01626   // Put them in the top-level "Calendar" folder for lack of a better
01627   // place since we never show Task type folders even in the
01628   // multiagenda view.
01629   if ( resourceCalendar() && incidence->type() == "Todo" ) {
01630     QString subRes = resourceCalendar()->subresourceIdentifier( incidence );
01631     if ( resourceCalendar()->subresourceType( subRes ) == "todo" ) {
01632       QString calmatch = "/.INBOX.directory/Calendar";
01633       QString i18nmatch = "/.INBOX.directory/" + i18n( "Calendar" );
01634       if ( subResourceCalendar().contains( calmatch ) ||
01635            subResourceCalendar().contains( i18nmatch ) ) {
01636         return true;
01637       }
01638     }
01639   }
01640 
01641   // Normal handling
01642   if ( !resourceCalendar() )
01643     return true;
01644   CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() );
01645   if ( !calRes )
01646     return true;
01647   if ( calRes->resource( incidence ) != resourceCalendar() )
01648     return false;
01649   if ( !subResourceCalendar().isEmpty() ) {
01650     if ( resourceCalendar()->subresourceIdentifier( incidence ) != subResourceCalendar() )
01651       return false;
01652   }
01653   return true;
01654 }
01655 
01656 void KOAgendaView::resourcesChanged()
01657 {
01658   mPendingChanges = true;
01659 }
01660 
01661 void KOAgendaView::calendarIncidenceAdded(Incidence * incidence)
01662 {
01663   Q_UNUSED( incidence );
01664   mPendingChanges = true;
01665 }
01666 
01667 void KOAgendaView::calendarIncidenceChanged(Incidence * incidence)
01668 {
01669   Q_UNUSED( incidence );
01670   mPendingChanges = true;
01671 }
01672 
01673 void KOAgendaView::calendarIncidenceDeleted(Incidence * incidence)
01674 {
01675   Q_UNUSED( incidence );
01676   mPendingChanges = true;
01677 }
KDE Home | KDE Accessibility Home | Description of Access Keys