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