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