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 }
00372 
00373 
00374 KOAgendaView::~KOAgendaView()
00375 {
00376   if ( calendar() )
00377     calendar()->unregisterObserver( this );
00378   delete mAgendaPopup;
00379   delete mAllDayAgendaPopup;
00380 }
00381 
00382 void KOAgendaView::connectAgenda( KOAgenda *agenda, QPopupMenu *popup,
00383                                   KOAgenda *otherAgenda )
00384 {
00385   connect( agenda, SIGNAL( showIncidencePopupSignal( Incidence *, const QDate & ) ),
00386            popup, SLOT( showIncidencePopup( Incidence *, const QDate & ) ) );
00387 
00388   connect( agenda, SIGNAL( showNewEventPopupSignal() ),
00389            SLOT( showNewEventPopup() ) );
00390 
00391   agenda->setCalendar( calendar() );
00392 
00393   // Create/Show/Edit/Delete Event
00394   connect( agenda, SIGNAL( newEventSignal() ), SIGNAL( newEventSignal() ) );
00395 
00396   connect( agenda, SIGNAL( newStartSelectSignal() ),
00397            otherAgenda, SLOT( clearSelection() ) );
00398   connect( agenda, SIGNAL( newStartSelectSignal() ),
00399            SIGNAL( timeSpanSelectionChanged()) );
00400 
00401   connect( agenda, SIGNAL( editIncidenceSignal( Incidence * ) ),
00402                    SIGNAL( editIncidenceSignal( Incidence * ) ) );
00403   connect( agenda, SIGNAL( showIncidenceSignal( Incidence * ) ),
00404                    SIGNAL( showIncidenceSignal( Incidence * ) ) );
00405   connect( agenda, SIGNAL( deleteIncidenceSignal( Incidence * ) ),
00406                    SIGNAL( deleteIncidenceSignal( Incidence * ) ) );
00407 
00408   connect( agenda, SIGNAL( startMultiModify( const QString & ) ),
00409                    SIGNAL( startMultiModify( const QString & ) ) );
00410   connect( agenda, SIGNAL( endMultiModify() ),
00411                    SIGNAL( endMultiModify() ) );
00412 
00413   connect( agenda, SIGNAL( itemModified( KOAgendaItem * ) ),
00414                    SLOT( updateEventDates( KOAgendaItem * ) ) );
00415   connect( agenda, SIGNAL( enableAgendaUpdate( bool ) ),
00416                    SLOT( enableAgendaUpdate( bool ) ) );
00417 
00418   // drag signals
00419   connect( agenda, SIGNAL( startDragSignal( Incidence * ) ),
00420            SLOT( startDrag( Incidence * ) ) );
00421 
00422   // synchronize selections
00423   connect( agenda, SIGNAL( incidenceSelected( Incidence * ) ),
00424            otherAgenda, SLOT( deselectItem() ) );
00425   connect( agenda, SIGNAL( incidenceSelected( Incidence * ) ),
00426            SIGNAL( incidenceSelected( Incidence * ) ) );
00427 
00428   // rescheduling of todos by d'n'd
00429   connect( agenda, SIGNAL( droppedToDo( Todo *, const QPoint &, bool ) ),
00430            SLOT( slotTodoDropped( Todo *, const QPoint &, bool ) ) );
00431 
00432 }
00433 
00434 void KOAgendaView::zoomInVertically( )
00435 {
00436   if ( !mIsSideBySide )
00437     KOPrefs::instance()->mHourSize++;
00438   mAgenda->updateConfig();
00439   mAgenda->checkScrollBoundaries();
00440 
00441   mTimeLabels->updateConfig();
00442   mTimeLabels->positionChanged();
00443   mTimeLabels->repaint();
00444 
00445   updateView();
00446 }
00447 
00448 void KOAgendaView::zoomOutVertically( )
00449 {
00450 
00451   if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) {
00452 
00453     if ( !mIsSideBySide )
00454       KOPrefs::instance()->mHourSize--;
00455     mAgenda->updateConfig();
00456     mAgenda->checkScrollBoundaries();
00457 
00458     mTimeLabels->updateConfig();
00459     mTimeLabels->positionChanged();
00460     mTimeLabels->repaint();
00461 
00462     updateView();
00463   }
00464 }
00465 
00466 void KOAgendaView::zoomInHorizontally( const QDate &date)
00467 {
00468   QDate begin;
00469   QDate newBegin;
00470   QDate dateToZoom = date;
00471   int ndays,count;
00472 
00473   begin = mSelectedDates.first();
00474   ndays = begin.daysTo( mSelectedDates.last() );
00475 
00476   // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it.
00477   if ( ! dateToZoom.isValid () )
00478     dateToZoom=mAgenda->selectedIncidenceDate();
00479 
00480   if( !dateToZoom.isValid() ) {
00481     if ( ndays > 1 ) {
00482       newBegin=begin.addDays(1);
00483       count = ndays-1;
00484       emit zoomViewHorizontally ( newBegin , count );
00485     }
00486   } else {
00487     if ( ndays <= 2 ) {
00488       newBegin = dateToZoom;
00489       count = 1;
00490     } else  {
00491       newBegin = dateToZoom.addDays( -ndays/2 +1  );
00492       count = ndays -1 ;
00493     }
00494     emit zoomViewHorizontally ( newBegin , count );
00495   }
00496 }
00497 
00498 void KOAgendaView::zoomOutHorizontally( const QDate &date )
00499 {
00500   QDate begin;
00501   QDate newBegin;
00502   QDate dateToZoom = date;
00503   int ndays,count;
00504 
00505   begin = mSelectedDates.first();
00506   ndays = begin.daysTo( mSelectedDates.last() );
00507 
00508   // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it.
00509   if ( ! dateToZoom.isValid () )
00510     dateToZoom=mAgenda->selectedIncidenceDate();
00511 
00512   if ( !dateToZoom.isValid() ) {
00513     newBegin = begin.addDays(-1);
00514     count = ndays+3 ;
00515   } else {
00516     newBegin = dateToZoom.addDays( -ndays/2-1 );
00517     count = ndays+3;
00518   }
00519 
00520   if ( abs( count ) >= 31 )
00521     kdDebug(5850) << "change to the mounth view?"<<endl;
00522   else
00523     //We want to center the date
00524     emit zoomViewHorizontally( newBegin, count );
00525 }
00526 
00527 void KOAgendaView::zoomView( const int delta, const QPoint &pos,
00528   const Qt::Orientation orient )
00529 {
00530   static QDate zoomDate;
00531   static QTimer *t = new QTimer( this );
00532 
00533 
00534   //Zoom to the selected incidence, on the other way
00535   // zoom to the date on screen after the first mousewheel move.
00536   if ( orient == Qt::Horizontal ) {
00537     QDate date=mAgenda->selectedIncidenceDate();
00538     if ( date.isValid() )
00539       zoomDate=date;
00540     else{
00541       if ( !t->isActive() ) {
00542         zoomDate= mSelectedDates[pos.x()];
00543       }
00544       t->start ( 1000,true );
00545     }
00546     if ( delta > 0 )
00547       zoomOutHorizontally( zoomDate );
00548     else
00549       zoomInHorizontally( zoomDate );
00550   } else {
00551     // Vertical zoom
00552     QPoint posConstentsOld = mAgenda->gridToContents(pos);
00553     if ( delta > 0 ) {
00554       zoomOutVertically();
00555     } else {
00556       zoomInVertically();
00557     }
00558     QPoint posConstentsNew = mAgenda->gridToContents(pos);
00559     mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() );
00560   }
00561 }
00562 
00563 void KOAgendaView::createDayLabels()
00564 {
00565 //  kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl;
00566 
00567   // ### Before deleting and recreating we could check if mSelectedDates changed...
00568   // It would remove some flickering and gain speed (since this is called by
00569   // each updateView() call)
00570   delete mDayLabels;
00571 
00572   mDayLabels = new QFrame (mDayLabelsFrame);
00573   mLayoutDayLabels = new QHBoxLayout(mDayLabels);
00574   if ( !mIsSideBySide )
00575     mLayoutDayLabels->addSpacing(mTimeLabels->width());
00576 
00577   const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
00578 
00579   DateList::ConstIterator dit;
00580   for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
00581     QDate date = *dit;
00582     QBoxLayout *dayLayout = new QVBoxLayout(mLayoutDayLabels);
00583     mLayoutDayLabels->setStretchFactor(dayLayout, 1);
00584 //    dayLayout->setMinimumWidth(1);
00585 
00586     int dW = calsys->dayOfWeek(date);
00587     QString veryLongStr = KGlobal::locale()->formatDate( date );
00588     QString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" )
00589         .arg( calsys->weekDayName( dW, true ) )
00590         .arg( calsys->day(date) );
00591     QString shortstr = QString::number(calsys->day(date));
00592 
00593     KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr,
00594       longstr, veryLongStr, mDayLabels);
00595     dayLabel->setMinimumWidth(1);
00596     dayLabel->setAlignment(QLabel::AlignHCenter);
00597     if (date == QDate::currentDate()) {
00598       QFont font = dayLabel->font();
00599       font.setBold(true);
00600       dayLabel->setFont(font);
00601     }
00602     dayLayout->addWidget(dayLabel);
00603 
00604     // if a holiday region is selected, show the holiday name
00605     QStringList texts = KOGlobals::self()->holiday( date );
00606     QStringList::ConstIterator textit = texts.begin();
00607     for ( ; textit != texts.end(); ++textit ) {
00608       // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00609       KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), QString::null, mDayLabels );
00610       label->setMinimumWidth(1);
00611       label->setAlignment(AlignCenter);
00612       dayLayout->addWidget(label);
00613     }
00614 
00615 #ifndef KORG_NOPLUGINS
00616     CalendarDecoration::List cds = KOCore::self()->calendarDecorations();
00617     CalendarDecoration *it;
00618     for(it = cds.first(); it; it = cds.next()) {
00619       QString text = it->shortText( date );
00620       if ( !text.isEmpty() ) {
00621         // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00622         KOAlternateLabel*label = new KOAlternateLabel( text, text, QString::null, mDayLabels );
00623         label->setMinimumWidth(1);
00624         label->setAlignment(AlignCenter);
00625         dayLayout->addWidget(label);
00626       }
00627     }
00628 
00629     for(it = cds.first(); it; it = cds.next()) {
00630       QWidget *wid = it->smallWidget(mDayLabels,date);
00631       if ( wid ) {
00632 //      wid->setHeight(20);
00633         dayLayout->addWidget(wid);
00634       }
00635     }
00636 #endif
00637   }
00638 
00639   if ( !mIsSideBySide )
00640     mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width());
00641   mDayLabels->show();
00642 }
00643 
00644 void KOAgendaView::enableAgendaUpdate( bool enable )
00645 {
00646   mAllowAgendaUpdate = enable;
00647 }
00648 
00649 int KOAgendaView::maxDatesHint()
00650 {
00651   // Not sure about the max number of events, so return 0 for now.
00652   return 0;
00653 }
00654 
00655 int KOAgendaView::currentDateCount()
00656 {
00657   return mSelectedDates.count();
00658 }
00659 
00660 Incidence::List KOAgendaView::selectedIncidences()
00661 {
00662   Incidence::List selected;
00663   Incidence *incidence;
00664 
00665   incidence = mAgenda->selectedIncidence();
00666   if (incidence) selected.append(incidence);
00667 
00668   incidence = mAllDayAgenda->selectedIncidence();
00669   if (incidence) selected.append(incidence);
00670 
00671   return selected;
00672 }
00673 
00674 DateList KOAgendaView::selectedDates()
00675 {
00676   DateList selected;
00677   QDate qd;
00678 
00679   qd = mAgenda->selectedIncidenceDate();
00680   if (qd.isValid()) selected.append(qd);
00681 
00682   qd = mAllDayAgenda->selectedIncidenceDate();
00683   if (qd.isValid()) selected.append(qd);
00684 
00685   return selected;
00686 }
00687 
00688 bool KOAgendaView::eventDurationHint( QDateTime &startDt, QDateTime &endDt,
00689                                       bool &allDay )
00690 {
00691   if ( selectionStart().isValid() ) {
00692     QDateTime start = selectionStart();
00693     QDateTime end = selectionEnd();
00694 
00695     if ( start.secsTo( end ) == 15*60 ) {
00696       // One cell in the agenda view selected, e.g.
00697       // because of a double-click, => Use the default duration
00698       QTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() );
00699       int addSecs = ( defaultDuration.hour()*3600 ) +
00700                     ( defaultDuration.minute()*60 );
00701       end = start.addSecs( addSecs );
00702     }
00703 
00704     startDt = start;
00705     endDt = end;
00706     allDay = selectedIsAllDay();
00707     return true;
00708   }
00709   return false;
00710 }
00711 
00713 bool KOAgendaView::selectedIsSingleCell()
00714 {
00715   if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false;
00716 
00717   if (selectedIsAllDay()) {
00718     int days = selectionStart().daysTo(selectionEnd());
00719     return ( days < 1 );
00720   } else {
00721     int secs = selectionStart().secsTo(selectionEnd());
00722     return ( secs <= 24*60*60/mAgenda->rows() );
00723   }
00724 }
00725 
00726 
00727 void KOAgendaView::updateView()
00728 {
00729 //  kdDebug(5850) << "KOAgendaView::updateView()" << endl;
00730   fillAgenda();
00731 }
00732 
00733 
00734 /*
00735   Update configuration settings for the agenda view. This method is not
00736   complete.
00737 */
00738 void KOAgendaView::updateConfig()
00739 {
00740 //  kdDebug(5850) << "KOAgendaView::updateConfig()" << endl;
00741 
00742   // update config for children
00743   mTimeLabels->updateConfig();
00744   mAgenda->updateConfig();
00745   mAllDayAgenda->updateConfig();
00746 
00747   // widget synchronization
00748   // FIXME: find a better way, maybe signal/slot
00749   mTimeLabels->positionChanged();
00750 
00751   // for some reason, this needs to be called explicitly
00752   mTimeLabels->repaint();
00753 
00754   updateTimeBarWidth();
00755 
00756   // ToolTips displaying summary of events
00757   KOAgendaItem::toolTipGroup()->setEnabled(KOPrefs::instance()
00758                                            ->mEnableToolTips);
00759 
00760   setHolidayMasks();
00761 
00762   createDayLabels();
00763 
00764   updateView();
00765 }
00766 
00767 void KOAgendaView::updateTimeBarWidth()
00768 {
00769   int width;
00770 
00771   width = mDummyAllDayLeft->fontMetrics().width( i18n("All Day") );
00772   width = QMAX( width, mTimeLabels->width() );
00773 
00774   mDummyAllDayLeft->setFixedWidth( width );
00775   mTimeLabels->setFixedWidth( width );
00776 }
00777 
00778 
00779 void KOAgendaView::updateEventDates( KOAgendaItem *item )
00780 {
00781   kdDebug(5850) << "KOAgendaView::updateEventDates(): " << item->text() << endl;
00782 
00783   QDateTime startDt,endDt;
00784 
00785   // Start date of this incidence, calculate the offset from it (so recurring and
00786   // non-recurring items can be treated exactly the same, we never need to check
00787   // for doesRecur(), because we only move the start day by the number of days the
00788   // agenda item was really moved. Smart, isn't it?)
00789   QDate thisDate;
00790   if ( item->cellXLeft() < 0 ) {
00791     thisDate = ( mSelectedDates.first() ).addDays( item->cellXLeft() );
00792   } else {
00793     thisDate = mSelectedDates[ item->cellXLeft() ];
00794   }
00795   QDate oldThisDate( item->itemDate() );
00796   int daysOffset = oldThisDate.daysTo( thisDate );
00797   int daysLength = 0;
00798 
00799 //  startDt.setDate( startDate );
00800 
00801   Incidence *incidence = item->incidence();
00802   if ( !incidence ) return;
00803   if ( !mChanger || !mChanger->beginChange(incidence) ) return;
00804   Incidence *oldIncidence = incidence->clone();
00805 
00806   QTime startTime(0,0,0), endTime(0,0,0);
00807   if ( incidence->doesFloat() ) {
00808     daysLength = item->cellWidth() - 1;
00809   } else {
00810     startTime = mAgenda->gyToTime( item->cellYTop() );
00811     if ( item->lastMultiItem() ) {
00812       endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 );
00813       daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft();
00814     } else {
00815       endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
00816     }
00817   }
00818 
00819 //  kdDebug(5850) << "KOAgendaView::updateEventDates(): now setting dates" << endl;
00820   // FIXME: use a visitor here
00821   if ( incidence->type() == "Event" ) {
00822     startDt = incidence->dtStart();
00823     startDt = startDt.addDays( daysOffset );
00824     startDt.setTime( startTime );
00825     endDt = startDt.addDays( daysLength );
00826     endDt.setTime( endTime );
00827     Event*ev = static_cast<Event*>(incidence);
00828     if( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) {
00829       // No change
00830       delete oldIncidence;
00831       return;
00832     }
00833     incidence->setDtStart( startDt );
00834     ev->setDtEnd( endDt );
00835   } else if ( incidence->type() == "Todo" ) {
00836     Todo *td = static_cast<Todo*>(incidence);
00837     startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
00838     startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) );
00839     startDt.setTime( startTime );
00840     endDt.setDate( thisDate );
00841     endDt.setTime( endTime );
00842 
00843     if( td->dtDue() == endDt ) {
00844       // No change
00845       delete oldIncidence;
00846       return;
00847     }
00848   }
00849   // FIXME: Adjusting the recurrence should really go to CalendarView so this
00850   // functionality will also be available in other views!
00851   // TODO_Recurrence: This does not belong here, and I'm not really sure
00852   // how it's supposed to work anyway.
00853   Recurrence *recur = incidence->recurrence();
00854 /*  if ( recur->doesRecur() && daysOffset != 0 ) {
00855     switch ( recur->recurrenceType() ) {
00856       case Recurrence::rYearlyPos: {
00857         int freq = recur->frequency();
00858         int duration = recur->duration();
00859         QDate endDt( recur->endDate() );
00860         bool negative = false;
00861 
00862         QPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() );
00863         if ( monthPos.first() ) {
00864           negative = monthPos.first()->negative;
00865         }
00866         QBitArray days( 7 );
00867         int pos = 0;
00868         days.fill( false );
00869         days.setBit( thisDate.dayOfWeek() - 1 );
00870         if ( negative ) {
00871           pos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
00872         } else {
00873           pos =  ( thisDate.day()-1 ) / 7 + 1;
00874         }
00875         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00876         recur->unsetRecurs();
00877         if ( duration != 0 ) {
00878           recur->setYearly( Recurrence::rYearlyPos, freq, duration );
00879         } else {
00880           recur->setYearly( Recurrence::rYearlyPos, freq, endDt );
00881         }
00882         recur->addYearlyMonthPos( pos, days );
00883         recur->addYearlyNum( thisDate.month() );
00884 
00885         break; }
00886         case Recurrence::rYearlyDay: {
00887           int freq = recur->frequency();
00888           int duration = recur->duration();
00889           QDate endDt( recur->endDate() );
00890         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00891           recur->unsetRecurs();
00892           if ( duration == 0 ) { // end by date
00893             recur->setYearly( Recurrence::rYearlyDay, freq, endDt );
00894           } else {
00895             recur->setYearly( Recurrence::rYearlyDay, freq, duration );
00896           }
00897           recur->addYearlyNum( thisDate.dayOfYear() );
00898           break; }
00899           case Recurrence::rYearlyMonth: {
00900             int freq = recur->frequency();
00901             int duration = recur->duration();
00902             QDate endDt( recur->endDate() );
00903         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00904             recur->unsetRecurs();
00905             if ( duration != 0 ) {
00906               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration );
00907             } else {
00908               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt );
00909             }
00910             recur->addYearlyNum( thisDate.month() );
00911             break; }
00912             case Recurrence::rMonthlyPos: {
00913               int freq = recur->frequency();
00914               int duration = recur->duration();
00915               QDate endDt( recur->endDate() );
00916               QPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() );
00917               if ( !monthPos.isEmpty() ) {
00918           // FIXME: How shall I adapt the day x of week Y if we move the date across month borders???
00919           // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
00920           // That's fine for korganizer, but might mess up other organizers.
00921                 QBitArray rDays( 7 );
00922                 rDays = monthPos.first()->rDays;
00923                 bool negative = monthPos.first()->negative;
00924                 int newPos;
00925                 rDays.fill( false );
00926                 rDays.setBit( thisDate.dayOfWeek() - 1 );
00927                 if ( negative ) {
00928                   newPos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
00929                 } else {
00930                   newPos =  ( thisDate.day()-1 ) / 7 + 1;
00931                 }
00932 
00933           // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00934                 recur->unsetRecurs();
00935                 if ( duration == 0 ) { // end by date
00936                   recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt );
00937                 } else {
00938                   recur->setMonthly( Recurrence::rMonthlyPos, freq, duration );
00939                 }
00940                 recur->addMonthlyPos( newPos, rDays );
00941               }
00942               break;}
00943               case Recurrence::rMonthlyDay: {
00944                 int freq = recur->frequency();
00945                 int duration = recur->duration();
00946                 QDate endDt( recur->endDate() );
00947                 QPtrList<int> monthDays( recur->monthDays() );
00948         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00949                 recur->unsetRecurs();
00950                 if ( duration == 0 ) { // end by date
00951                   recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt );
00952                 } else {
00953                   recur->setMonthly( Recurrence::rMonthlyDay, freq, duration );
00954                 }
00955         // FIXME: How shall I adapt the n-th day if we move the date across month borders???
00956         // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
00957         // That's fine for korganizer, but might mess up other organizers.
00958                 recur->addMonthlyDay( thisDate.day() );
00959 
00960                 break;}
00961                 case Recurrence::rWeekly: {
00962                   QBitArray days(7), oldDays( recur->days() );
00963                   int offset = daysOffset % 7;
00964                   if ( offset<0 ) offset = (offset+7) % 7;
00965         // rotate the days
00966                   for (int d=0; d<7; d++ ) {
00967                     days.setBit( (d+offset) % 7, oldDays.at(d) );
00968                   }
00969                   if ( recur->duration() == 0 ) { // end by date
00970                     recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() );
00971                   } else { // duration or no end
00972                     recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() );
00973                   }
00974                   break;}
00975       // nothing to be done for the following:
00976       case Recurrence::rDaily:
00977       case Recurrence::rHourly:
00978       case Recurrence::rMinutely:
00979       case Recurrence::rNone:
00980       default:
00981         break;
00982     }
00983     if ( recur->duration()==0 ) { // end by date
00984       recur->setEndDate( recur->endDate().addDays( daysOffset ) );
00985     }
00986     KMessageBox::information( this, i18n("A recurring calendar item was moved "
00987                               "to a different day. The recurrence settings "
00988                               "have been updated with that move. Please check "
00989                               "them in the editor."),
00990                               i18n("Recurrence Moved"),
00991                               "RecurrenceMoveInAgendaWarning" );
00992   }*/
00993 
00994   // FIXME: use a visitor here
00995   if ( incidence->type() == "Event" ) {
00996     incidence->setDtStart( startDt );
00997     (static_cast<Event*>( incidence ) )->setDtEnd( endDt );
00998   } else if ( incidence->type() == "Todo" ) {
00999     Todo *td = static_cast<Todo*>( incidence );
01000     if ( td->hasStartDate() )
01001       td->setDtStart( startDt );
01002     td->setDtDue( endDt );
01003   }
01004 
01005   item->setItemDate( startDt.date() );
01006 
01007   KOIncidenceToolTip::remove( item );
01008   KOIncidenceToolTip::add( item, incidence, KOAgendaItem::toolTipGroup() );
01009 
01010   const bool result = mChanger->changeIncidence( oldIncidence, incidence );
01011   mChanger->endChange(incidence);
01012   delete oldIncidence;
01013 
01014   if ( !result ) {
01015     mPendingChanges = true;
01016     QTimer::singleShot( 0, this, SLOT(updateView()) );
01017     return;
01018   }
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