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