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