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