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, 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, 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   connect( agenda, SIGNAL(itemModified(KOAgendaItem *)),
00449            SLOT(updateEventDates(KOAgendaItem *)) );
00450 
00451   connect( agenda, SIGNAL(enableAgendaUpdate(bool)),
00452            SLOT(enableAgendaUpdate(bool)) );
00453 
00454   // drag signals
00455   connect( agenda, SIGNAL(startDragSignal(Incidence *)),
00456            SLOT(startDrag(Incidence *)) );
00457 
00458   // synchronize selections
00459   connect( agenda, SIGNAL(incidenceSelected(Incidence *,const QDate &)),
00460            otherAgenda, SLOT(deselectItem()) );
00461   connect( agenda, SIGNAL(incidenceSelected(Incidence *,const QDate &)),
00462            SIGNAL(incidenceSelected(Incidence *,const QDate &)) );
00463 
00464   // rescheduling of todos by d'n'd
00465   connect( agenda, SIGNAL(droppedToDo(Todo *,const QPoint &,bool)),
00466            SLOT(slotTodoDropped(Todo *,const QPoint &,bool)) );
00467 
00468 }
00469 
00470 void KOAgendaView::zoomInVertically( )
00471 {
00472   if ( !mIsSideBySide )
00473     KOPrefs::instance()->mHourSize++;
00474   mAgenda->updateConfig();
00475   mAgenda->checkScrollBoundaries();
00476 
00477   mTimeLabels->updateConfig();
00478   mTimeLabels->positionChanged();
00479   mTimeLabels->repaint();
00480 
00481   updateView();
00482 }
00483 
00484 void KOAgendaView::zoomOutVertically( )
00485 {
00486 
00487   if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) {
00488 
00489     if ( !mIsSideBySide )
00490       KOPrefs::instance()->mHourSize--;
00491     mAgenda->updateConfig();
00492     mAgenda->checkScrollBoundaries();
00493 
00494     mTimeLabels->updateConfig();
00495     mTimeLabels->positionChanged();
00496     mTimeLabels->repaint();
00497 
00498     updateView();
00499   }
00500 }
00501 
00502 void KOAgendaView::zoomInHorizontally( const QDate &date)
00503 {
00504   QDate begin;
00505   QDate newBegin;
00506   QDate dateToZoom = date;
00507   int ndays,count;
00508 
00509   begin = mSelectedDates.first();
00510   ndays = begin.daysTo( mSelectedDates.last() );
00511 
00512   // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it.
00513   if ( ! dateToZoom.isValid () )
00514     dateToZoom=mAgenda->selectedIncidenceDate();
00515 
00516   if( !dateToZoom.isValid() ) {
00517     if ( ndays > 1 ) {
00518       newBegin=begin.addDays(1);
00519       count = ndays-1;
00520       emit zoomViewHorizontally ( newBegin , count );
00521     }
00522   } else {
00523     if ( ndays <= 2 ) {
00524       newBegin = dateToZoom;
00525       count = 1;
00526     } else  {
00527       newBegin = dateToZoom.addDays( -ndays/2 +1  );
00528       count = ndays -1 ;
00529     }
00530     emit zoomViewHorizontally ( newBegin , count );
00531   }
00532 }
00533 
00534 void KOAgendaView::zoomOutHorizontally( const QDate &date )
00535 {
00536   QDate begin;
00537   QDate newBegin;
00538   QDate dateToZoom = date;
00539   int ndays,count;
00540 
00541   begin = mSelectedDates.first();
00542   ndays = begin.daysTo( mSelectedDates.last() );
00543 
00544   // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it.
00545   if ( ! dateToZoom.isValid () )
00546     dateToZoom=mAgenda->selectedIncidenceDate();
00547 
00548   if ( !dateToZoom.isValid() ) {
00549     newBegin = begin.addDays(-1);
00550     count = ndays+3 ;
00551   } else {
00552     newBegin = dateToZoom.addDays( -ndays/2-1 );
00553     count = ndays+3;
00554   }
00555 
00556   if ( abs( count ) >= 31 )
00557     kdDebug(5850) << "change to the mounth view?"<<endl;
00558   else
00559     //We want to center the date
00560     emit zoomViewHorizontally( newBegin, count );
00561 }
00562 
00563 void KOAgendaView::zoomView( const int delta, const QPoint &pos,
00564   const Qt::Orientation orient )
00565 {
00566   static QDate zoomDate;
00567   static QTimer *t = new QTimer( this );
00568 
00569 
00570   //Zoom to the selected incidence, on the other way
00571   // zoom to the date on screen after the first mousewheel move.
00572   if ( orient == Qt::Horizontal ) {
00573     QDate date=mAgenda->selectedIncidenceDate();
00574     if ( date.isValid() )
00575       zoomDate=date;
00576     else{
00577       if ( !t->isActive() ) {
00578         zoomDate= mSelectedDates[pos.x()];
00579       }
00580       t->start ( 1000,true );
00581     }
00582     if ( delta > 0 )
00583       zoomOutHorizontally( zoomDate );
00584     else
00585       zoomInHorizontally( zoomDate );
00586   } else {
00587     // Vertical zoom
00588     QPoint posConstentsOld = mAgenda->gridToContents(pos);
00589     if ( delta > 0 ) {
00590       zoomOutVertically();
00591     } else {
00592       zoomInVertically();
00593     }
00594     QPoint posConstentsNew = mAgenda->gridToContents(pos);
00595     mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() );
00596   }
00597 }
00598 
00599 void KOAgendaView::createDayLabels( bool force )
00600 {
00601 //  kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl;
00602 
00603   // Check if mSelectedDates has changed, if not just return
00604   // Removes some flickering and gains speed (since this is called by each updateView())
00605   if ( !force && mSaveSelectedDates == mSelectedDates ) {
00606     return;
00607   }
00608   mSaveSelectedDates = mSelectedDates;
00609 
00610   delete mDayLabels;
00611   mDateDayLabels.clear();
00612 
00613   mDayLabels = new QFrame (mDayLabelsFrame);
00614   mLayoutDayLabels = new QHBoxLayout(mDayLabels);
00615   if ( !mIsSideBySide )
00616     mLayoutDayLabels->addSpacing(mTimeLabels->width());
00617 
00618   const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
00619 
00620   DateList::ConstIterator dit;
00621   for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
00622     QDate date = *dit;
00623     QBoxLayout *dayLayout = new QVBoxLayout(mLayoutDayLabels);
00624     mLayoutDayLabels->setStretchFactor(dayLayout, 1);
00625 //    dayLayout->setMinimumWidth(1);
00626 
00627     int dW = calsys->dayOfWeek(date);
00628     QString veryLongStr = KGlobal::locale()->formatDate( date );
00629     QString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" )
00630         .arg( calsys->weekDayName( dW, true ) )
00631         .arg( calsys->day(date) );
00632     QString shortstr = QString::number(calsys->day(date));
00633 
00634     KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr,
00635                                                       longstr, veryLongStr, mDayLabels);
00636     dayLabel->useShortText(); // will be recalculated in updateDayLabelSizes() anyway
00637     dayLabel->setMinimumWidth(1);
00638     dayLabel->setAlignment(QLabel::AlignHCenter);
00639     if (date == QDate::currentDate()) {
00640       QFont font = dayLabel->font();
00641       font.setBold(true);
00642       dayLabel->setFont(font);
00643     }
00644     dayLayout->addWidget(dayLabel);
00645     mDateDayLabels.append( dayLabel );
00646 
00647     // if a holiday region is selected, show the holiday name
00648     QStringList texts = KOGlobals::self()->holiday( date );
00649     QStringList::ConstIterator textit = texts.begin();
00650     for ( ; textit != texts.end(); ++textit ) {
00651       // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00652       KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), QString::null, mDayLabels );
00653       label->setMinimumWidth(1);
00654       label->setAlignment(AlignCenter);
00655       dayLayout->addWidget(label);
00656     }
00657 
00658 #ifndef KORG_NOPLUGINS
00659     CalendarDecoration::List cds = KOCore::self()->calendarDecorations();
00660     CalendarDecoration *it;
00661     for(it = cds.first(); it; it = cds.next()) {
00662       QString text = it->shortText( date );
00663       if ( !text.isEmpty() ) {
00664         // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00665         KOAlternateLabel*label = new KOAlternateLabel( text, text, QString::null, mDayLabels );
00666         label->setMinimumWidth(1);
00667         label->setAlignment(AlignCenter);
00668         dayLayout->addWidget(label);
00669       }
00670     }
00671 
00672     for(it = cds.first(); it; it = cds.next()) {
00673       QWidget *wid = it->smallWidget(mDayLabels,date);
00674       if ( wid ) {
00675 //      wid->setHeight(20);
00676         dayLayout->addWidget(wid);
00677       }
00678     }
00679 #endif
00680   }
00681 
00682   if ( !mIsSideBySide )
00683     mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width());
00684   mDayLabels->show();
00685   QTimer::singleShot( 0, this, SLOT( updateDayLabelSizes() ) );
00686 }
00687 
00688 void KOAgendaView::enableAgendaUpdate( bool enable )
00689 {
00690   mAllowAgendaUpdate = enable;
00691 }
00692 
00693 int KOAgendaView::maxDatesHint()
00694 {
00695   // Not sure about the max number of events, so return 0 for now.
00696   return 0;
00697 }
00698 
00699 int KOAgendaView::currentDateCount()
00700 {
00701   return mSelectedDates.count();
00702 }
00703 
00704 Incidence::List KOAgendaView::selectedIncidences()
00705 {
00706   Incidence::List selected;
00707   Incidence *incidence;
00708 
00709   incidence = mAgenda->selectedIncidence();
00710   if (incidence) selected.append(incidence);
00711 
00712   incidence = mAllDayAgenda->selectedIncidence();
00713   if (incidence) selected.append(incidence);
00714 
00715   return selected;
00716 }
00717 
00718 DateList KOAgendaView::selectedIncidenceDates()
00719 {
00720   DateList selected;
00721   QDate qd;
00722 
00723   qd = mAgenda->selectedIncidenceDate();
00724   if (qd.isValid()) selected.append(qd);
00725 
00726   qd = mAllDayAgenda->selectedIncidenceDate();
00727   if (qd.isValid()) selected.append(qd);
00728 
00729   return selected;
00730 }
00731 
00732 bool KOAgendaView::eventDurationHint( QDateTime &startDt, QDateTime &endDt,
00733                                       bool &allDay )
00734 {
00735   if ( selectionStart().isValid() ) {
00736     QDateTime start = selectionStart();
00737     QDateTime end = selectionEnd();
00738 
00739     if ( start.secsTo( end ) == 15*60 ) {
00740       // One cell in the agenda view selected, e.g.
00741       // because of a double-click, => Use the default duration
00742       QTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() );
00743       int addSecs = ( defaultDuration.hour()*3600 ) +
00744                     ( defaultDuration.minute()*60 );
00745       end = start.addSecs( addSecs );
00746     }
00747 
00748     startDt = start;
00749     endDt = end;
00750     allDay = selectedIsAllDay();
00751     return true;
00752   }
00753   return false;
00754 }
00755 
00757 bool KOAgendaView::selectedIsSingleCell()
00758 {
00759   if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false;
00760 
00761   if (selectedIsAllDay()) {
00762     int days = selectionStart().daysTo(selectionEnd());
00763     return ( days < 1 );
00764   } else {
00765     int secs = selectionStart().secsTo(selectionEnd());
00766     return ( secs <= 24*60*60/mAgenda->rows() );
00767   }
00768 }
00769 
00770 
00771 void KOAgendaView::updateView()
00772 {
00773 //  kdDebug(5850) << "KOAgendaView::updateView()" << endl;
00774   fillAgenda();
00775 }
00776 
00777 
00778 /*
00779   Update configuration settings for the agenda view. This method is not
00780   complete.
00781 */
00782 void KOAgendaView::updateConfig()
00783 {
00784 //  kdDebug(5850) << "KOAgendaView::updateConfig()" << endl;
00785 
00786   // update config for children
00787   mTimeLabels->updateConfig();
00788   mAgenda->updateConfig();
00789   mAllDayAgenda->updateConfig();
00790 
00791   // widget synchronization
00792   // FIXME: find a better way, maybe signal/slot
00793   mTimeLabels->positionChanged();
00794 
00795   // for some reason, this needs to be called explicitly
00796   mTimeLabels->repaint();
00797 
00798   updateTimeBarWidth();
00799 
00800   // ToolTips displaying summary of events
00801   KOAgendaItem::toolTipGroup()->setEnabled(KOPrefs::instance()
00802                                            ->mEnableToolTips);
00803 
00804   setHolidayMasks();
00805 
00806   createDayLabels( true );
00807 
00808   updateView();
00809 }
00810 
00811 void KOAgendaView::updateTimeBarWidth()
00812 {
00813   int width;
00814 
00815   width = mDummyAllDayLeft->fontMetrics().width( i18n("All Day") );
00816   width = QMAX( width, mTimeLabels->width() );
00817 
00818   mDummyAllDayLeft->setFixedWidth( width );
00819   mTimeLabels->setFixedWidth( width );
00820 }
00821 
00822 void KOAgendaView::updateDayLabelSizes()
00823 {
00824   // First, calculate the maximum text type that fits for all labels
00825   KOAlternateLabel::TextType overallType = KOAlternateLabel::Extensive;
00826   QPtrList<KOAlternateLabel>::const_iterator it = mDateDayLabels.constBegin();
00827   for( ; it != mDateDayLabels.constEnd(); it++ ) {
00828     KOAlternateLabel::TextType type = (*it)->largestFittingTextType();
00829     if ( type < overallType )
00830       overallType = type;
00831   }
00832 
00833   // Then, set that maximum text type to all the labels
00834   it = mDateDayLabels.constBegin();
00835   for( ; it != mDateDayLabels.constEnd(); it++ ) {
00836     (*it)->setFixedType( overallType );
00837   }
00838 }
00839 
00840 void KOAgendaView::resizeEvent( QResizeEvent *resizeEvent )
00841 {
00842   updateDayLabelSizes();
00843   KOrg::AgendaView::resizeEvent( resizeEvent );
00844 }
00845 
00846 void KOAgendaView::updateEventDates( KOAgendaItem *item )
00847 {
00848   kdDebug(5850) << "KOAgendaView::updateEventDates(): " << item->text()
00849                 << "; item->cellXLeft(): " << item->cellXLeft()
00850                 << "; item->cellYTop(): " << item->cellYTop()
00851                 << "; item->lastMultiItem(): " << item->lastMultiItem()
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   QDate oldThisDate( item->itemDate() );
00867   int daysOffset = oldThisDate.daysTo( thisDate );
00868   int daysLength = 0;
00869 
00870   // startDt.setDate( startDate );
00871 
00872   Incidence *incidence = item->incidence();
00873   if ( !incidence ) {
00874     return;
00875   }
00876   if ( !mChanger ||
00877        !mChanger->beginChange( incidence, resourceCalendar(), subResourceCalendar() ) ) {
00878     return;
00879   }
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 {
00893       endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
00894     }
00895   }
00896 
00897   kdDebug(5850) << "daysLength: " << daysLength << "; startTime: " << startTime
00898                 << "; endTime: " << endTime << "; thisDate: " << thisDate
00899                 << "; incidence->dtStart(): " << incidence->dtStart() << endl;
00900 
00901   // FIXME: use a visitor here
00902   if ( incidence->type() == "Event" ) {
00903     startDt = incidence->dtStart();
00904     startDt = startDt.addDays( daysOffset );
00905     startDt.setTime( startTime );
00906     endDt = startDt.addDays( daysLength );
00907     endDt.setTime( endTime );
00908     Event* ev = static_cast<Event*>( incidence );
00909     if ( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) {
00910       // No change
00911       delete oldIncidence;
00912       return;
00913     }
00914     incidence->setDtStart( startDt );
00915     ev->setDtEnd( endDt );
00916   } else if ( incidence->type() == "Todo" ) {
00917     Todo *td = static_cast<Todo*>( incidence );
00918     startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
00919     startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) );
00920     startDt.setTime( startTime );
00921     endDt.setDate( thisDate );
00922     endDt.setTime( endTime );
00923 
00924     if( td->dtDue() == endDt ) {
00925       // No change
00926       delete oldIncidence;
00927       return;
00928     }
00929   }
00930   // FIXME: Adjusting the recurrence should really go to CalendarView so this
00931   // functionality will also be available in other views!
00932   // TODO_Recurrence: This does not belong here, and I'm not really sure
00933   // how it's supposed to work anyway.
00934 /*
00935     Recurrence *recur = incidence->recurrence();
00936     if ( recur->doesRecur() && daysOffset != 0 ) {
00937     switch ( recur->recurrenceType() ) {
00938       case Recurrence::rYearlyPos: {
00939         int freq = recur->frequency();
00940         int duration = recur->duration();
00941         QDate endDt( recur->endDate() );
00942         bool negative = false;
00943 
00944         QPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() );
00945         if ( monthPos.first() ) {
00946           negative = monthPos.first()->negative;
00947         }
00948         QBitArray days( 7 );
00949         int pos = 0;
00950         days.fill( false );
00951         days.setBit( thisDate.dayOfWeek() - 1 );
00952         if ( negative ) {
00953           pos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
00954         } else {
00955           pos =  ( thisDate.day()-1 ) / 7 + 1;
00956         }
00957         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00958         recur->unsetRecurs();
00959         if ( duration != 0 ) {
00960           recur->setYearly( Recurrence::rYearlyPos, freq, duration );
00961         } else {
00962           recur->setYearly( Recurrence::rYearlyPos, freq, endDt );
00963         }
00964         recur->addYearlyMonthPos( pos, days );
00965         recur->addYearlyNum( thisDate.month() );
00966 
00967         break; }
00968         case Recurrence::rYearlyDay: {
00969           int freq = recur->frequency();
00970           int duration = recur->duration();
00971           QDate endDt( recur->endDate() );
00972         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00973           recur->unsetRecurs();
00974           if ( duration == 0 ) { // end by date
00975             recur->setYearly( Recurrence::rYearlyDay, freq, endDt );
00976           } else {
00977             recur->setYearly( Recurrence::rYearlyDay, freq, duration );
00978           }
00979           recur->addYearlyNum( thisDate.dayOfYear() );
00980           break; }
00981           case Recurrence::rYearlyMonth: {
00982             int freq = recur->frequency();
00983             int duration = recur->duration();
00984             QDate endDt( recur->endDate() );
00985         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00986             recur->unsetRecurs();
00987             if ( duration != 0 ) {
00988               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration );
00989             } else {
00990               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt );
00991             }
00992             recur->addYearlyNum( thisDate.month() );
00993             break; }
00994             case Recurrence::rMonthlyPos: {
00995               int freq = recur->frequency();
00996               int duration = recur->duration();
00997               QDate endDt( recur->endDate() );
00998               QPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() );
00999               if ( !monthPos.isEmpty() ) {
01000           // FIXME: How shall I adapt the day x of week Y if we move the date across month borders???
01001           // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
01002           // That's fine for korganizer, but might mess up other organizers.
01003                 QBitArray rDays( 7 );
01004                 rDays = monthPos.first()->rDays;
01005                 bool negative = monthPos.first()->negative;
01006                 int newPos;
01007                 rDays.fill( false );
01008                 rDays.setBit( thisDate.dayOfWeek() - 1 );
01009                 if ( negative ) {
01010                   newPos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
01011                 } else {
01012                   newPos =  ( thisDate.day()-1 ) / 7 + 1;
01013                 }
01014 
01015           // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01016                 recur->unsetRecurs();
01017                 if ( duration == 0 ) { // end by date
01018                   recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt );
01019                 } else {
01020                   recur->setMonthly( Recurrence::rMonthlyPos, freq, duration );
01021                 }
01022                 recur->addMonthlyPos( newPos, rDays );
01023               }
01024               break;}
01025               case Recurrence::rMonthlyDay: {
01026                 int freq = recur->frequency();
01027                 int duration = recur->duration();
01028                 QDate endDt( recur->endDate() );
01029                 QPtrList<int> monthDays( recur->monthDays() );
01030         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01031                 recur->unsetRecurs();
01032                 if ( duration == 0 ) { // end by date
01033                   recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt );
01034                 } else {
01035                   recur->setMonthly( Recurrence::rMonthlyDay, freq, duration );
01036                 }
01037         // FIXME: How shall I adapt the n-th day if we move the date across month borders???
01038         // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
01039         // That's fine for korganizer, but might mess up other organizers.
01040                 recur->addMonthlyDay( thisDate.day() );
01041 
01042                 break;}
01043                 case Recurrence::rWeekly: {
01044                   QBitArray days(7), oldDays( recur->days() );
01045                   int offset = daysOffset % 7;
01046                   if ( offset<0 ) offset = (offset+7) % 7;
01047         // rotate the days
01048                   for (int d=0; d<7; d++ ) {
01049                     days.setBit( (d+offset) % 7, oldDays.at(d) );
01050                   }
01051                   if ( recur->duration() == 0 ) { // end by date
01052                     recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() );
01053                   } else { // duration or no end
01054                     recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() );
01055                   }
01056                   break;}
01057       // nothing to be done for the following:
01058       case Recurrence::rDaily:
01059       case Recurrence::rHourly:
01060       case Recurrence::rMinutely:
01061       case Recurrence::rNone:
01062       default:
01063         break;
01064     }
01065     if ( recur->duration()==0 ) { // end by date
01066       recur->setEndDate( recur->endDate().addDays( daysOffset ) );
01067     }
01068     KMessageBox::information( this, i18n("A recurring calendar item was moved "
01069                               "to a different day. The recurrence settings "
01070                               "have been updated with that move. Please check "
01071                               "them in the editor."),
01072                               i18n("Recurrence Moved"),
01073                               "RecurrenceMoveInAgendaWarning" );
01074   }*/
01075 
01076   // FIXME: use a visitor here
01077   if ( incidence->type() == "Event" ) {
01078     incidence->setDtStart( startDt );
01079     static_cast<Event*>( incidence )->setDtEnd( endDt );
01080   } else if ( incidence->type() == "Todo" ) {
01081     Todo *td = static_cast<Todo*>( incidence );
01082     if ( td->hasStartDate() ) {
01083       td->setDtStart( startDt );
01084     }
01085     td->setDtDue( endDt );
01086   }
01087 
01088   item->setItemDate( startDt.date() );
01089 
01090   KOIncidenceToolTip::remove( item );
01091   KOIncidenceToolTip::add( item, calendar(), incidence, thisDate, KOAgendaItem::toolTipGroup() );
01092 
01093   const bool result = mChanger->changeIncidence( oldIncidence, incidence,
01094                                                  KOGlobals::DATE_MODIFIED, this );
01095   mChanger->endChange( incidence, resourceCalendar(), subResourceCalendar() );
01096   delete oldIncidence;
01097 
01098   if ( !result ) {
01099     mPendingChanges = true;
01100     QTimer::singleShot( 0, this, SLOT(updateView()) );
01101     return;
01102   }
01103 
01104   // don't update the agenda as the item already has the correct coordinates.
01105   // an update would delete the current item and recreate it, but we are still
01106   // using a pointer to that item! => CRASH
01107   enableAgendaUpdate( false );
01108   // We need to do this in a timer to make sure we are not deleting the item
01109   // we are currently working on, which would lead to crashes
01110   // Only the actually moved agenda item is already at the correct position and mustn't be
01111   // recreated. All others have to!!!
01112   if ( incidence->doesRecur() ) {
01113     mUpdateItem = incidence;
01114     QTimer::singleShot( 0, this, SLOT( doUpdateItem() ) );
01115   }
01116 
01117     enableAgendaUpdate( true );
01118 
01119 //  kdDebug(5850) << "KOAgendaView::updateEventDates() done " << endl;
01120 }
01121 
01122 void KOAgendaView::doUpdateItem()
01123 {
01124   if ( mUpdateItem ) {
01125     changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED );
01126     mUpdateItem = 0;
01127   }
01128 }
01129 
01130 
01131 
01132 void KOAgendaView::showDates( const QDate &start, const QDate &end )
01133 {
01134 //  kdDebug(5850) << "KOAgendaView::selectDates" << endl;
01135   if ( !mSelectedDates.isEmpty() && mSelectedDates.first() == start
01136         && mSelectedDates.last() == end && !mPendingChanges )
01137     return;
01138 
01139   mSelectedDates.clear();
01140 
01141   QDate d = start;
01142   while ( d <= end ) {
01143     mSelectedDates.append( d );
01144     d = d.addDays( 1 );
01145   }
01146 
01147   mAreDatesInitialized = true;
01148 
01149   // and update the view
01150   fillAgenda();
01151 }
01152 
01153 
01154 void KOAgendaView::showIncidences( const Incidence::List &, const QDate & )
01155 {
01156   kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl;
01157 }
01158 
01159 void KOAgendaView::insertIncidence( Incidence *incidence, const QDate &curDate )
01160 {
01161   if ( !filterByResource( incidence ) ) {
01162     return;
01163   }
01164 
01165   // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's
01166   Event *event = dynamic_cast<Event *>( incidence );
01167   Todo  *todo  = dynamic_cast<Todo  *>( incidence );
01168 
01169   int curCol = mSelectedDates.first().daysTo( curDate );
01170 
01171   // In case incidence->dtStart() isn't visible (crosses bounderies)
01172   if ( curCol < 0 ) {
01173     curCol = 0;
01174   }
01175 
01176   // The date for the event is not displayed, just ignore it
01177   if ( curCol >= int( mSelectedDates.count() ) ) {
01178     return;
01179   }
01180 
01181   // Default values, which can never be reached
01182   mMinY[curCol] = mAgenda->timeToY( QTime( 23, 59 ) ) + 1;
01183   mMaxY[curCol] = mAgenda->timeToY( QTime( 0, 0 ) ) - 1;
01184 
01185   int beginX;
01186   int endX;
01187   QDate columnDate;
01188   if ( event ) {
01189     QDate firstVisibleDate = mSelectedDates.first();
01190     // its crossing bounderies, lets calculate beginX and endX
01191     if ( curDate < firstVisibleDate ) {
01192       beginX = curCol + firstVisibleDate.daysTo( curDate );
01193       endX   = beginX + event->dtStart().daysTo( event->dtEnd() );
01194       columnDate = firstVisibleDate;
01195     } else {
01196       beginX = curCol;
01197       endX   = beginX + event->dtStart().daysTo( event->dtEnd() );
01198       columnDate = curDate;
01199     }
01200   } else if ( todo ) {
01201     if ( !todo->hasDueDate() ) {
01202       return;  // todo shall not be displayed if it has no date
01203     }
01204     columnDate = curDate;
01205     beginX = endX = curCol;
01206 
01207   } else {
01208     return;
01209   }
01210   if ( todo && todo->isOverdue() ) {
01211     mAllDayAgenda->insertAllDayItem( incidence, columnDate, curCol, curCol );
01212   } else if ( incidence->doesFloat() ||
01213               ( todo &&
01214                   !todo->dtDue().isValid() ) ) {
01215       mAllDayAgenda->insertAllDayItem( incidence, columnDate, beginX, endX );
01216   } else if ( event && event->isMultiDay() ) {
01217     int startY = mAgenda->timeToY( event->dtStart().time() );
01218     QTime endtime = event->dtEnd().time();
01219     if ( endtime == QTime( 0, 0, 0 ) ) {
01220       endtime = QTime( 23, 59, 59 );
01221     }
01222     int endY = mAgenda->timeToY( endtime ) - 1;
01223     if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) {
01224       mAgenda->insertMultiItem( event, columnDate, beginX, endX, startY, endY );
01225 
01226     }
01227     if ( beginX == curCol ) {
01228       mMaxY[curCol] = mAgenda->timeToY( QTime( 23, 59 ) );
01229       if ( startY < mMinY[curCol] ) {
01230         mMinY[curCol] = startY;
01231       }
01232     } else if ( endX == curCol ) {
01233       mMinY[curCol] = mAgenda->timeToY( QTime( 0, 0 ) );
01234       if ( endY > mMaxY[curCol] ) {
01235         mMaxY[curCol] = endY;
01236       }
01237     } else {
01238       mMinY[curCol] = mAgenda->timeToY( QTime( 0, 0 ) );
01239       mMaxY[curCol] = mAgenda->timeToY( QTime( 23, 59 ) );
01240     }
01241   } else {
01242     int startY = 0, endY = 0;
01243     if ( event ) {
01244       startY = mAgenda->timeToY( incidence->dtStart().time() );
01245       QTime endtime = event->dtEnd().time();
01246       if ( endtime == QTime( 0, 0, 0 ) ) {
01247         endtime = QTime( 23, 59, 59 );
01248       }
01249       endY = mAgenda->timeToY( endtime ) - 1;
01250     }
01251     if ( todo ) {
01252       QTime t = todo->dtDue().time();
01253 
01254       if ( t == QTime( 0, 0 ) ) {
01255         t = QTime( 23, 59 );
01256       }
01257 
01258       int halfHour = 1800;
01259       if ( t.addSecs( -halfHour ) < t ) {
01260         startY = mAgenda->timeToY( t.addSecs( -halfHour ) );
01261         endY   = mAgenda->timeToY( t ) - 1;
01262       } else {
01263         startY = 0;
01264         endY   = mAgenda->timeToY( t.addSecs( halfHour ) ) - 1;
01265       }
01266     }
01267     if ( endY < startY ) {
01268       endY = startY;
01269     }
01270     mAgenda->insertItem( incidence, columnDate, curCol, startY, endY );
01271     if ( startY < mMinY[curCol] ) {
01272       mMinY[curCol] = startY;
01273     }
01274     if ( endY > mMaxY[curCol] ) {
01275       mMaxY[curCol] = endY;
01276     }
01277   }
01278 }
01279 
01280 void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence )
01281 {
01282   Todo *todo = dynamic_cast<Todo *>(incidence);
01283   CalFilter *filter = calendar()->filter();
01284   if ( ( filter && !filter->filterIncidence( incidence ) ) ||
01285        ( ( todo && !KOPrefs::instance()->showAllDayTodo() ) ) ) {
01286     return;
01287   }
01288 
01289   displayIncidence( incidence );
01290 }
01291 
01292 void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode )
01293 {
01294   switch ( mode ) {
01295     case KOGlobals::INCIDENCEADDED:
01296     {
01297       // Add an event. No need to recreate the whole view!
01298       // recreating everything even causes troubles: dropping to the
01299       // day matrix recreates the agenda items, but the evaluation is
01300       // still in an agendaItems' code, which was deleted in the mean time.
01301       // Thus KOrg crashes...
01302       changeIncidenceDisplayAdded( incidence );
01303       updateEventIndicators();
01304       break;
01305     }
01306     case KOGlobals::INCIDENCEEDITED:
01307     {
01308       if ( mAllowAgendaUpdate ) {
01309         removeIncidence( incidence );
01310         changeIncidenceDisplayAdded( incidence );
01311       }
01312       updateEventIndicators();
01313       break;
01314     }
01315     case KOGlobals::INCIDENCEDELETED:
01316     {
01317       removeIncidence( incidence );
01318       updateEventIndicators();
01319       break;
01320     }
01321     default:
01322       return;
01323   }
01324 
01325   // HACK: Update the view if the all-day agenda has been modified.
01326   // Do this because there are some layout problems in the
01327   // all-day agenda that are not easily solved, but clearing
01328   // and redrawing works ok.
01329   if ( incidence->doesFloat() ) {
01330     updateView();
01331   }
01332 }
01333 
01334 void KOAgendaView::fillAgenda( const QDate & )
01335 {
01336   fillAgenda();
01337 }
01338 
01339 void KOAgendaView::fillAgenda()
01340 {
01341   if ( !mAreDatesInitialized ) {
01342     return;
01343   }
01344 
01345   mPendingChanges = false;
01346 
01347   /* Remember the uids of the selected items. In case one of the
01348    * items was deleted and re-added, we want to reselect it. */
01349   const QString &selectedAgendaUid = mAgenda->lastSelectedUid();
01350   const QString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
01351 
01352   enableAgendaUpdate( true );
01353   clearView();
01354 
01355   mAllDayAgenda->changeColumns( mSelectedDates.count() );
01356   mAgenda->changeColumns( mSelectedDates.count() );
01357   mEventIndicatorTop->changeColumns( mSelectedDates.count() );
01358   mEventIndicatorBottom->changeColumns( mSelectedDates.count() );
01359 
01360   createDayLabels( false );
01361   setHolidayMasks();
01362 
01363   mMinY.resize( mSelectedDates.count() );
01364   mMaxY.resize( mSelectedDates.count() );
01365 
01366   mAgenda->setDateList( mSelectedDates );
01367 
01368   bool somethingReselected = false;
01369   Incidence::List incidences = calendar()->incidences();
01370 
01371   for ( Incidence::List::ConstIterator it = incidences.begin(); it!=incidences.constEnd(); ++it ) {
01372     Incidence *incidence = (*it);
01373     displayIncidence( incidence );
01374 
01375     if( incidence->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
01376       mAgenda->selectItemByUID( incidence->uid() );
01377       somethingReselected = true;
01378     }
01379 
01380     if( incidence->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
01381       mAllDayAgenda->selectItemByUID( incidence->uid() );
01382       somethingReselected = true;
01383     }
01384 
01385   }
01386 
01387   mAgenda->checkScrollBoundaries();
01388   updateEventIndicators();
01389 
01390   //  mAgenda->viewport()->update();
01391   //  mAllDayAgenda->viewport()->update();
01392 
01393   // make invalid
01394   deleteSelectedDateTime();
01395 
01396   if( !somethingReselected ) {
01397     emit incidenceSelected( 0, QDate() );
01398   }
01399 }
01400 
01401 void KOAgendaView::displayIncidence( Incidence *incidence )
01402 {
01403   QDate today = QDate::currentDate();
01404   DateTimeList::iterator t;
01405 
01406   // FIXME: use a visitor here
01407   Todo *todo = dynamic_cast<Todo *>( incidence );
01408   Event *event = dynamic_cast<Event *>( incidence );
01409 
01410   QDateTime firstVisibleDateTime = mSelectedDates.first();
01411   QDateTime lastVisibleDateTime = mSelectedDates.last();
01412 
01413   lastVisibleDateTime.setTime( QTime( 23, 59, 59, 59 ) );
01414   firstVisibleDateTime.setTime( QTime( 0, 0 ) );
01415   DateTimeList dateTimeList;
01416 
01417   QDateTime incDtStart = incidence->dtStart();
01418   QDateTime incDtEnd   = incidence->dtEnd();
01419 
01420   if ( todo &&
01421        ( !KOPrefs::instance()->showAllDayTodo() || !todo->hasDueDate() ) ) {
01422     return;
01423   }
01424 
01425   if ( incidence->doesRecur() ) {
01426     int eventDuration = event ? incDtStart.daysTo( incDtEnd ) : 0;
01427 
01428     // if there's a multiday event that starts before firstVisibleDateTime but ends after
01429     // lets include it. timesInInterval() ignores incidences that aren't totaly inside
01430     // the range
01431     QDateTime startDateTimeWithOffset = firstVisibleDateTime.addDays( -eventDuration );
01432     dateTimeList =
01433       incidence->recurrence()->timesInInterval( startDateTimeWithOffset,
01434                                                 lastVisibleDateTime );
01435   } else {
01436     QDateTime dateToAdd; // date to add to our date list
01437     QDateTime incidenceStart;
01438     QDateTime incidenceEnd;
01439 
01440     if ( todo && todo->hasDueDate() && !todo->isOverdue() ) {
01441       // If it's not overdue it will be shown at the original date (not today)
01442       dateToAdd = todo->dtDue();
01443 
01444       // To-dos are drawn with the bottom of the rectangle at dtDue
01445       // if dtDue is at 00:00, then it should be displayed in the previous day, at 23:59
01446       if ( !todo->doesFloat() && dateToAdd.time() == QTime( 0, 0 ) ) {
01447         dateToAdd = dateToAdd.addSecs( -1 );
01448       }
01449 
01450       incidenceEnd = dateToAdd;
01451     } else if ( event ) {
01452       dateToAdd = incDtStart;
01453       incidenceEnd = incDtEnd;
01454     }
01455 
01456     if ( incidence->doesFloat() ) {
01457       // so comparisons with < > actually work
01458       dateToAdd.setTime( QTime( 0, 0 ) );
01459       incidenceEnd.setTime( QTime( 23, 59, 59, 59 ) );
01460     }
01461 
01462     if ( dateToAdd <= lastVisibleDateTime && incidenceEnd > firstVisibleDateTime ) {
01463       dateTimeList += dateToAdd;
01464     }
01465   }
01466 
01467   // ToDo items shall be displayed today if they are already overdude
01468   QDateTime dateTimeToday = today;
01469   if ( todo &&
01470        todo->isOverdue() &&
01471        dateTimeToday >= firstVisibleDateTime &&
01472        dateTimeToday <= lastVisibleDateTime ) {
01473 
01474     bool doAdd = true;
01475 
01476     if ( todo->doesRecur() ) {
01477       /* If there's a recurring instance showing up today don't add "today" again
01478        * we don't want the event to appear duplicated */
01479       for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01480         if ( (*t).date() == today ) {
01481           doAdd = false;
01482           break;
01483        }
01484       }
01485     }
01486 
01487     if ( doAdd ) {
01488       dateTimeList += dateTimeToday;
01489     }
01490   }
01491 
01492   for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01493     insertIncidence( incidence, (*t).date() );
01494   }
01495 }
01496 
01497 void KOAgendaView::clearView()
01498 {
01499 //  kdDebug(5850) << "ClearView" << endl;
01500   mAllDayAgenda->clear();
01501   mAgenda->clear();
01502 }
01503 
01504 CalPrinterBase::PrintType KOAgendaView::printType()
01505 {
01506   if ( currentDateCount() == 1 ) return CalPrinterBase::Day;
01507   else return CalPrinterBase::Week;
01508 }
01509 
01510 void KOAgendaView::updateEventIndicatorTop( int newY )
01511 {
01512   uint i;
01513   for( i = 0; i < mMinY.size(); ++i ) {
01514     mEventIndicatorTop->enableColumn( i, newY > mMinY[i] );
01515   }
01516   mEventIndicatorTop->update();
01517 }
01518 
01519 void KOAgendaView::updateEventIndicatorBottom( int newY )
01520 {
01521   uint i;
01522   for( i = 0; i < mMaxY.size(); ++i ) {
01523     mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
01524   }
01525   mEventIndicatorBottom->update();
01526 }
01527 
01528 void KOAgendaView::slotTodoDropped( Todo *todo, const QPoint &gpos, bool allDay )
01529 {
01530   if ( gpos.x()<0 || gpos.y()<0 ) return;
01531   QDate day = mSelectedDates[gpos.x()];
01532   QTime time = mAgenda->gyToTime( gpos.y() );
01533   QDateTime newTime( day, time );
01534 
01535   if ( todo ) {
01536     Todo *existingTodo = calendar()->todo( todo->uid() );
01537     if ( existingTodo ) {
01538       kdDebug(5850) << "Drop existing Todo" << endl;
01539       Todo *oldTodo = existingTodo->clone();
01540       if ( mChanger &&
01541            mChanger->beginChange( existingTodo, resourceCalendar(), subResourceCalendar() ) ) {
01542         existingTodo->setDtDue( newTime );
01543         existingTodo->setFloats( allDay );
01544         existingTodo->setHasDueDate( true );
01545         mChanger->changeIncidence( oldTodo, existingTodo,
01546                                    KOGlobals::DATE_MODIFIED, this );
01547         mChanger->endChange( existingTodo, resourceCalendar(), subResourceCalendar() );
01548       } else {
01549         KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
01550                             "because it cannot be locked.") );
01551       }
01552       delete oldTodo;
01553     } else {
01554       kdDebug(5850) << "Drop new Todo" << endl;
01555       todo->setDtDue( newTime );
01556       todo->setFloats( allDay );
01557       todo->setHasDueDate( true );
01558       if ( !mChanger->addIncidence( todo, 0, QString(), this ) ) {
01559         KODialogManager::errorSaveIncidence( this, todo );
01560       }
01561     }
01562   }
01563 }
01564 
01565 void KOAgendaView::startDrag( Incidence *incidence )
01566 {
01567 #ifndef KORG_NODND
01568   DndFactory factory( calendar() );
01569   ICalDrag *vd = factory.createDrag( incidence, this );
01570   if ( vd->drag() ) {
01571     kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl;
01572   }
01573 #endif
01574 }
01575 
01576 void KOAgendaView::readSettings()
01577 {
01578   readSettings(KOGlobals::self()->config());
01579 }
01580 
01581 void KOAgendaView::readSettings(KConfig *config)
01582 {
01583 //  kdDebug(5850) << "KOAgendaView::readSettings()" << endl;
01584 
01585   config->setGroup("Views");
01586 
01587 #ifndef KORG_NOSPLITTER
01588   QValueList<int> sizes = config->readIntListEntry("Separator AgendaView");
01589   if (sizes.count() == 2) {
01590     mSplitterAgenda->setSizes(sizes);
01591   }
01592 #endif
01593 
01594   updateConfig();
01595 }
01596 
01597 void KOAgendaView::writeSettings(KConfig *config)
01598 {
01599 //  kdDebug(5850) << "KOAgendaView::writeSettings()" << endl;
01600 
01601   config->setGroup("Views");
01602 
01603 #ifndef KORG_NOSPLITTER
01604   QValueList<int> list = mSplitterAgenda->sizes();
01605   config->writeEntry("Separator AgendaView",list);
01606 #endif
01607 }
01608 
01609 void KOAgendaView::setHolidayMasks()
01610 {
01611   if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) {
01612     return;
01613   }
01614 
01615   mHolidayMask.resize( mSelectedDates.count() + 1 );
01616 
01617   for( uint i = 0; i < mSelectedDates.count(); ++i ) {
01618     mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
01619   }
01620 
01621   // Store the information about the day before the visible area (needed for
01622   // overnight working hours) in the last bit of the mask:
01623   bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
01624   mHolidayMask[ mSelectedDates.count() ] = showDay;
01625 
01626   mAgenda->setHolidayMask( &mHolidayMask );
01627   mAllDayAgenda->setHolidayMask( &mHolidayMask );
01628 }
01629 
01630 void KOAgendaView::setContentsPos( int y )
01631 {
01632   mAgenda->setContentsPos( 0, y );
01633 }
01634 
01635 void KOAgendaView::setExpandedButton( bool expanded )
01636 {
01637   if ( !mExpandButton ) return;
01638 
01639   if ( expanded ) {
01640     mExpandButton->setPixmap( mExpandedPixmap );
01641   } else {
01642     mExpandButton->setPixmap( mNotExpandedPixmap );
01643   }
01644 }
01645 
01646 void KOAgendaView::clearSelection()
01647 {
01648   mAgenda->deselectItem();
01649   mAllDayAgenda->deselectItem();
01650 }
01651 
01652 void KOAgendaView::newTimeSpanSelectedAllDay( const QPoint &start, const QPoint &end )
01653 {
01654   newTimeSpanSelected( start, end );
01655   mTimeSpanInAllDay = true;
01656 }
01657 
01658 void KOAgendaView::newTimeSpanSelected( const QPoint &start, const QPoint &end )
01659 {
01660   if (!mSelectedDates.count()) return;
01661 
01662   mTimeSpanInAllDay = false;
01663 
01664   QDate dayStart = mSelectedDates[ kClamp( start.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01665   QDate dayEnd = mSelectedDates[ kClamp( end.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01666 
01667   QTime timeStart = mAgenda->gyToTime(start.y());
01668   QTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
01669 
01670   QDateTime dtStart(dayStart,timeStart);
01671   QDateTime dtEnd(dayEnd,timeEnd);
01672 
01673   mTimeSpanBegin = dtStart;
01674   mTimeSpanEnd = dtEnd;
01675 }
01676 
01677 void KOAgendaView::deleteSelectedDateTime()
01678 {
01679   mTimeSpanBegin.setDate(QDate());
01680   mTimeSpanEnd.setDate(QDate());
01681   mTimeSpanInAllDay = false;
01682 }
01683 
01684 void KOAgendaView::setTypeAheadReceiver( QObject *o )
01685 {
01686   mAgenda->setTypeAheadReceiver( o );
01687   mAllDayAgenda->setTypeAheadReceiver( o );
01688 }
01689 
01690 void KOAgendaView::finishTypeAhead()
01691 {
01692   mAgenda->finishTypeAhead();
01693   mAllDayAgenda->finishTypeAhead();
01694 }
01695 
01696 void KOAgendaView::removeIncidence( Incidence *incidence )
01697 {
01698   mAgenda->removeIncidence( incidence );
01699   mAllDayAgenda->removeIncidence( incidence );
01700 }
01701 
01702 void KOAgendaView::updateEventIndicators()
01703 {
01704   mMinY = mAgenda->minContentsY();
01705   mMaxY = mAgenda->maxContentsY();
01706 
01707   mAgenda->checkScrollBoundaries();
01708   updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
01709   updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
01710 }
01711 
01712 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
01713 {
01714   mChanger = changer;
01715   mAgenda->setIncidenceChanger( changer );
01716   mAllDayAgenda->setIncidenceChanger( changer );
01717 }
01718 
01719 void KOAgendaView::clearTimeSpanSelection()
01720 {
01721   mAgenda->clearSelection();
01722   mAllDayAgenda->clearSelection();
01723   deleteSelectedDateTime();
01724 }
01725 
01726 bool KOAgendaView::filterByResource( Incidence *incidence )
01727 {
01728   // Special handling for groupware to-dos that are in Task folders.
01729   // Put them in the top-level "Calendar" folder for lack of a better
01730   // place since we never show Task type folders even in the
01731   // multiagenda view.
01732   if ( resourceCalendar() && incidence->type() == "Todo" ) {
01733     QString subRes = resourceCalendar()->subresourceIdentifier( incidence );
01734     if ( resourceCalendar()->subresourceType( subRes ) == "todo" ) {
01735       QString calmatch = "/.INBOX.directory/Calendar";
01736       QString i18nmatch = "/.INBOX.directory/" + i18n( "Calendar" );
01737       if ( subResourceCalendar().contains( calmatch ) ||
01738            subResourceCalendar().contains( i18nmatch ) ) {
01739         return true;
01740       }
01741     }
01742   }
01743 
01744   // Normal handling
01745   if ( !resourceCalendar() )
01746     return true;
01747   CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() );
01748   if ( !calRes )
01749     return true;
01750   if ( calRes->resource( incidence ) != resourceCalendar() )
01751     return false;
01752   if ( !subResourceCalendar().isEmpty() ) {
01753     if ( resourceCalendar()->subresourceIdentifier( incidence ) != subResourceCalendar() )
01754       return false;
01755   }
01756   return true;
01757 }
01758 
01759 void KOAgendaView::resourcesChanged()
01760 {
01761   mPendingChanges = true;
01762 }
01763 
01764 void KOAgendaView::calendarIncidenceAdded(Incidence * incidence)
01765 {
01766   Q_UNUSED( incidence );
01767   mPendingChanges = true;
01768 }
01769 
01770 void KOAgendaView::calendarIncidenceChanged(Incidence * incidence)
01771 {
01772   Q_UNUSED( incidence );
01773   mPendingChanges = true;
01774 }
01775 
01776 void KOAgendaView::calendarIncidenceDeleted(Incidence * incidence)
01777 {
01778   Q_UNUSED( incidence );
01779   mPendingChanges = true;
01780 }
KDE Home | KDE Accessibility Home | Description of Access Keys