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