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