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