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