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