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 TQt, and distribute the resulting executable,
00022     without including the source code for TQt in the source distribution.
00023 */
00024 
00025 #include <tqhbox.h>
00026 #include <tqvbox.h>
00027 #include <tqlabel.h>
00028 #include <tqframe.h>
00029 #include <tqlayout.h>
00030 #ifndef KORG_NOSPLITTER
00031 #include <tqsplitter.h>
00032 #endif
00033 #include <tqfont.h>
00034 #include <tqfontmetrics.h>
00035 #include <tqpopupmenu.h>
00036 #include <tqtooltip.h>
00037 #include <tqpainter.h>
00038 #include <tqpushbutton.h>
00039 #include <tqcursor.h>
00040 #include <tqbitarray.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,TQWidget *parent,const char *name)
00080   : TQFrame(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(TQPainter *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(TQPoint(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 TQString &shortlabel, const TQString &longlabel,
00136     const TQString &extensivelabel, TQWidget *parent, const char *name )
00137   : TQLabel(parent, name), mTextTypeFixed(false), mShortText(shortlabel),
00138     mLongText(longlabel), mExtensiveText(extensivelabel)
00139 {
00140   setSizePolicy(TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::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   TQLabel::setText( mShortText );
00153   TQToolTip::remove( this );
00154   TQToolTip::add( this, mExtensiveText );
00155   update(); // for kolab/issue4350
00156 }
00157 
00158 void KOAlternateLabel::useLongText()
00159 {
00160   mTextTypeFixed = true;
00161   TQLabel::setText( mLongText );
00162   TQToolTip::remove( this );
00163   TQToolTip::add( this, mExtensiveText );
00164   update(); // for kolab/issue4350
00165 }
00166 
00167 void KOAlternateLabel::useExtensiveText()
00168 {
00169   mTextTypeFixed = true;
00170   TQLabel::setText( mExtensiveText );
00171   TQToolTip::remove( this );
00172   TQToolTip::add( this, "" );
00173   update(); // for kolab/issue4350
00174 }
00175 
00176 void KOAlternateLabel::useDefaultText()
00177 {
00178   mTextTypeFixed = false;
00179   squeezeTextToLabel();
00180 }
00181 
00182 KOAlternateLabel::TextType KOAlternateLabel::largestFittingTextType() const
00183 {
00184   TQFontMetrics fm( fontMetrics() );
00185   const int labelWidth = size().width();
00186   const int longTextWidth = fm.width( mLongText );
00187   const int extensiveTextWidth = fm.width( mExtensiveText );
00188   if ( extensiveTextWidth <= labelWidth )
00189     return Extensive;
00190   else if ( longTextWidth <= labelWidth )
00191     return Long;
00192   else
00193     return Short;
00194 }
00195 
00196 void KOAlternateLabel::setFixedType( TextType type )
00197 {
00198   switch ( type )
00199   {
00200     case Extensive: useExtensiveText(); break;
00201     case Long: useLongText(); break;
00202     case Short: useShortText(); break;
00203   }
00204 }
00205 
00206 void KOAlternateLabel::squeezeTextToLabel()
00207 {
00208   if ( mTextTypeFixed )
00209     return;
00210 
00211   const TextType type = largestFittingTextType();
00212   switch ( type )
00213   {
00214     case Extensive:
00215       TQLabel::setText( mExtensiveText );
00216       TQToolTip::remove( this );
00217       TQToolTip::add( this, "" );
00218       break;
00219     case Long:
00220       TQLabel::setText( mLongText );
00221       TQToolTip::remove( this );
00222       TQToolTip::add( this, mExtensiveText );
00223       break;
00224     case Short:
00225       TQLabel::setText( mShortText );
00226       TQToolTip::remove( this );
00227       TQToolTip::add( this, mExtensiveText );
00228       break;
00229   }
00230   update(); // for kolab/issue4350
00231 }
00232 
00233 void KOAlternateLabel::resizeEvent( TQResizeEvent * )
00234 {
00235   squeezeTextToLabel();
00236 }
00237 
00238 TQSize KOAlternateLabel::minimumSizeHint() const
00239 {
00240   TQSize sh = TQLabel::minimumSizeHint();
00241   sh.setWidth(-1);
00242   return sh;
00243 }
00244 
00248 
00249 KOAgendaView::KOAgendaView( Calendar *cal,
00250                             CalendarView *calendarView,
00251                             TQWidget *parent,
00252                             const char *name,
00253                             bool isSideBySide ) :
00254   KOrg::AgendaView (cal, parent,name), mExpandButton( 0 ),
00255   mAllowAgendaUpdate( true ),
00256   mUpdateItem( 0 ),
00257   mIsSideBySide( isSideBySide ),
00258   mPendingChanges( true ),
00259   mAreDatesInitialized( false )
00260 {
00261   mSelectedDates.append(TQDate::currentDate());
00262 
00263   mLayoutDayLabels = 0;
00264   mDayLabelsFrame = 0;
00265   mDayLabels = 0;
00266 
00267   bool isRTL = KOGlobals::self()->reverseLayout();
00268 
00269   if ( KOPrefs::instance()->compactDialogs() ) {
00270     if ( KOPrefs::instance()->mVerticalScreen ) {
00271       mExpandedPixmap = KOGlobals::self()->smallIcon( "1downarrow" );
00272       mNotExpandedPixmap = KOGlobals::self()->smallIcon( "1uparrow" );
00273     } else {
00274       mExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1leftarrow" : "1rightarrow" );
00275       mNotExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1rightarrow" : "1leftarrow" );
00276     }
00277   }
00278 
00279   TQBoxLayout *topLayout = new TQVBoxLayout(this);
00280 
00281   // Create day name labels for agenda columns
00282   mDayLabelsFrame = new TQHBox(this);
00283   topLayout->addWidget(mDayLabelsFrame);
00284 
00285   // Create agenda splitter
00286 #ifndef KORG_NOSPLITTER
00287   mSplitterAgenda = new TQSplitter(Qt::Vertical,this);
00288   topLayout->addWidget(mSplitterAgenda);
00289 
00290 #if KDE_IS_VERSION( 3, 1, 93 )
00291   mSplitterAgenda->setOpaqueResize( KGlobalSettings::opaqueResize() );
00292 #else
00293   mSplitterAgenda->setOpaqueResize();
00294 #endif
00295 
00296   mAllDayFrame = new TQHBox(mSplitterAgenda);
00297 
00298   TQWidget *agendaFrame = new TQWidget(mSplitterAgenda);
00299 #else
00300   TQVBox *mainBox = new TQVBox( this );
00301   topLayout->addWidget( mainBox );
00302 
00303   mAllDayFrame = new TQHBox(mainBox);
00304 
00305   TQWidget *agendaFrame = new TQWidget(mainBox);
00306 #endif
00307 
00308   // Create all-day agenda widget
00309   mDummyAllDayLeft = new TQVBox( mAllDayFrame );
00310   if ( isSideBySide )
00311     mDummyAllDayLeft->hide();
00312 
00313   if ( KOPrefs::instance()->compactDialogs() ) {
00314     mExpandButton = new TQPushButton(mDummyAllDayLeft);
00315     mExpandButton->setPixmap( mNotExpandedPixmap );
00316     mExpandButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed,
00317                                   TQSizePolicy::Fixed ) );
00318     connect( mExpandButton, TQT_SIGNAL( clicked() ), TQT_SIGNAL( toggleExpand() ) );
00319   } else {
00320     TQLabel *label = new TQLabel( i18n("All Day"), mDummyAllDayLeft );
00321     label->setAlignment( TQt::AlignRight | TQt::AlignVCenter | TQt::WordBreak );
00322   }
00323 
00324   mAllDayAgenda = new KOAgenda( 1, calendarView, mAllDayFrame );
00325   mAllDayAgenda->setCalendar( calendar() );
00326   TQWidget *dummyAllDayRight = new TQWidget(mAllDayFrame);
00327 
00328   // Create agenda frame
00329   TQGridLayout *agendaLayout = new TQGridLayout(agendaFrame,3,3);
00330 //  TQHBox *agendaFrame = new TQHBox(splitterAgenda);
00331 
00332   // create event indicator bars
00333   mEventIndicatorTop = new EventIndicator(EventIndicator::Top,agendaFrame);
00334   agendaLayout->addWidget(mEventIndicatorTop,0,1);
00335   mEventIndicatorBottom = new EventIndicator(EventIndicator::Bottom,
00336                                              agendaFrame);
00337   agendaLayout->addWidget(mEventIndicatorBottom,2,1);
00338   TQWidget *dummyAgendaRight = new TQWidget(agendaFrame);
00339   agendaLayout->addWidget(dummyAgendaRight,0,2);
00340 
00341   // Create time labels
00342   mTimeLabels = new TimeLabels(24,agendaFrame);
00343   agendaLayout->addWidget(mTimeLabels,1,0);
00344 
00345   // Create agenda
00346   mAgenda = new KOAgenda( 1, 96, KOPrefs::instance()->mHourSize, calendarView, agendaFrame );
00347   mAgenda->setCalendar( calendar() );
00348   agendaLayout->addMultiCellWidget(mAgenda,1,1,1,2);
00349   agendaLayout->setColStretch(1,1);
00350 
00351   // Create event context menu for agenda
00352   mAgendaPopup = eventPopup();
00353 
00354   // Create event context menu for all day agenda
00355   mAllDayAgendaPopup = eventPopup();
00356 
00357   // make connections between dependent widgets
00358   mTimeLabels->setAgenda(mAgenda);
00359   if ( isSideBySide )
00360     mTimeLabels->hide();
00361 
00362   // Update widgets to reflect user preferences
00363 //  updateConfig();
00364 
00365   createDayLabels( true );
00366 
00367   if ( !isSideBySide ) {
00368     // these blank widgets make the All Day Event box line up with the agenda
00369     dummyAllDayRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
00370     dummyAgendaRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
00371   }
00372 
00373   updateTimeBarWidth();
00374 
00375   // Scrolling
00376   connect(mAgenda->verticalScrollBar(),TQT_SIGNAL(valueChanged(int)),
00377           mTimeLabels, TQT_SLOT(positionChanged()));
00378 
00379   connect( mAgenda,
00380     TQT_SIGNAL( zoomView( const int, const TQPoint & ,const Qt::Orientation ) ),
00381     TQT_SLOT( zoomView( const int, const TQPoint &, const Qt::Orientation ) ) );
00382 
00383   connect(mTimeLabels->verticalScrollBar(),TQT_SIGNAL(valueChanged(int)),
00384           TQT_SLOT(setContentsPos(int)));
00385 
00386   // Create Events, depends on type of agenda
00387   connect( mAgenda, TQT_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)),
00388                     TQT_SLOT(newTimeSpanSelected(const TQPoint &, const TQPoint &)));
00389   connect( mAllDayAgenda, TQT_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)),
00390                           TQT_SLOT(newTimeSpanSelectedAllDay(const TQPoint &, const TQPoint &)));
00391 
00392   // event indicator update
00393   connect( mAgenda, TQT_SIGNAL(lowerYChanged(int)),
00394                     TQT_SLOT(updateEventIndicatorTop(int)));
00395   connect( mAgenda, TQT_SIGNAL(upperYChanged(int)),
00396                     TQT_SLOT(updateEventIndicatorBottom(int)));
00397 
00398   if ( !readOnly() ) {
00399     connectAgenda( mAgenda, mAgendaPopup, mAllDayAgenda );
00400     connectAgenda( mAllDayAgenda, mAllDayAgendaPopup, mAgenda);
00401   }
00402 
00403   if ( cal ) {
00404     cal->registerObserver( this );
00405   }
00406 }
00407 
00408 
00409 KOAgendaView::~KOAgendaView()
00410 {
00411   if ( calendar() )
00412     calendar()->unregisterObserver( this );
00413   delete mAgendaPopup;
00414   delete mAllDayAgendaPopup;
00415 }
00416 
00417 void KOAgendaView::connectAgenda( KOAgenda *agenda, TQPopupMenu *popup,
00418                                   KOAgenda *otherAgenda )
00419 {
00420   connect( agenda, TQT_SIGNAL(showIncidencePopupSignal(Calendar *,Incidence *,const TQDate &)),
00421            popup, TQT_SLOT(showIncidencePopup(Calendar *,Incidence *,const TQDate &)) );
00422 
00423   connect( agenda, TQT_SIGNAL(showNewEventPopupSignal()),
00424            TQT_SLOT(showNewEventPopup()) );
00425 
00426 
00427   // Create/Show/Edit/Delete Event
00428   connect( agenda, TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)),
00429            TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)) );
00430 
00431   connect( agenda, TQT_SIGNAL(newStartSelectSignal()),
00432            otherAgenda, TQT_SLOT(clearSelection()) );
00433   connect( agenda, TQT_SIGNAL(newStartSelectSignal()),
00434            TQT_SIGNAL(timeSpanSelectionChanged()) );
00435 
00436   connect( agenda, TQT_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)),
00437            TQT_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)) );
00438   connect( agenda, TQT_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)),
00439            TQT_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)) );
00440   connect( agenda, TQT_SIGNAL(deleteIncidenceSignal(Incidence *)),
00441            TQT_SIGNAL(deleteIncidenceSignal(Incidence *)) );
00442 
00443   connect( agenda, TQT_SIGNAL(startMultiModify(const TQString &)),
00444            TQT_SIGNAL(startMultiModify(const TQString &)) );
00445   connect( agenda, TQT_SIGNAL(endMultiModify()),
00446            TQT_SIGNAL(endMultiModify()) );
00447 
00448   connect( agenda, TQT_SIGNAL(itemModified(KOAgendaItem *)),
00449            TQT_SLOT(updateEventDates(KOAgendaItem *)) );
00450 
00451   connect( agenda, TQT_SIGNAL(enableAgendaUpdate(bool)),
00452            TQT_SLOT(enableAgendaUpdate(bool)) );
00453 
00454   // drag signals
00455   connect( agenda, TQT_SIGNAL(startDragSignal(Incidence *)),
00456            TQT_SLOT(startDrag(Incidence *)) );
00457 
00458   // synchronize selections
00459   connect( agenda, TQT_SIGNAL(incidenceSelected(Incidence *,const TQDate &)),
00460            otherAgenda, TQT_SLOT(deselectItem()) );
00461   connect( agenda, TQT_SIGNAL(incidenceSelected(Incidence *,const TQDate &)),
00462            TQT_SIGNAL(incidenceSelected(Incidence *,const TQDate &)) );
00463 
00464   // rescheduling of todos by d'n'd
00465   connect( agenda, TQT_SIGNAL(droppedToDo(Todo *,const TQPoint &,bool)),
00466            TQT_SLOT(slotTodoDropped(Todo *,const TQPoint &,bool)) );
00467 
00468 }
00469 
00470 void KOAgendaView::zoomInVertically( )
00471 {
00472   if ( !mIsSideBySide )
00473     KOPrefs::instance()->mHourSize++;
00474   mAgenda->updateConfig();
00475   mAgenda->checkScrollBoundaries();
00476 
00477   mTimeLabels->updateConfig();
00478   mTimeLabels->positionChanged();
00479   mTimeLabels->repaint();
00480 
00481   updateView();
00482 }
00483 
00484 void KOAgendaView::zoomOutVertically( )
00485 {
00486 
00487   if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) {
00488 
00489     if ( !mIsSideBySide )
00490       KOPrefs::instance()->mHourSize--;
00491     mAgenda->updateConfig();
00492     mAgenda->checkScrollBoundaries();
00493 
00494     mTimeLabels->updateConfig();
00495     mTimeLabels->positionChanged();
00496     mTimeLabels->repaint();
00497 
00498     updateView();
00499   }
00500 }
00501 
00502 void KOAgendaView::zoomInHorizontally( const TQDate &date)
00503 {
00504   TQDate begin;
00505   TQDate newBegin;
00506   TQDate dateToZoom = date;
00507   int ndays,count;
00508 
00509   begin = mSelectedDates.first();
00510   ndays = begin.daysTo( mSelectedDates.last() );
00511 
00512   // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it.
00513   if ( ! dateToZoom.isValid () )
00514     dateToZoom=mAgenda->selectedIncidenceDate();
00515 
00516   if( !dateToZoom.isValid() ) {
00517     if ( ndays > 1 ) {
00518       newBegin=begin.addDays(1);
00519       count = ndays-1;
00520       emit zoomViewHorizontally ( newBegin , count );
00521     }
00522   } else {
00523     if ( ndays <= 2 ) {
00524       newBegin = dateToZoom;
00525       count = 1;
00526     } else  {
00527       newBegin = dateToZoom.addDays( -ndays/2 +1  );
00528       count = ndays -1 ;
00529     }
00530     emit zoomViewHorizontally ( newBegin , count );
00531   }
00532 }
00533 
00534 void KOAgendaView::zoomOutHorizontally( const TQDate &date )
00535 {
00536   TQDate begin;
00537   TQDate newBegin;
00538   TQDate dateToZoom = date;
00539   int ndays,count;
00540 
00541   begin = mSelectedDates.first();
00542   ndays = begin.daysTo( mSelectedDates.last() );
00543 
00544   // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it.
00545   if ( ! dateToZoom.isValid () )
00546     dateToZoom=mAgenda->selectedIncidenceDate();
00547 
00548   if ( !dateToZoom.isValid() ) {
00549     newBegin = begin.addDays(-1);
00550     count = ndays+3 ;
00551   } else {
00552     newBegin = dateToZoom.addDays( -ndays/2-1 );
00553     count = ndays+3;
00554   }
00555 
00556   if ( abs( count ) >= 31 )
00557     kdDebug(5850) << "change to the mounth view?"<<endl;
00558   else
00559     //We want to center the date
00560     emit zoomViewHorizontally( newBegin, count );
00561 }
00562 
00563 void KOAgendaView::zoomView( const int delta, const TQPoint &pos,
00564   const Qt::Orientation orient )
00565 {
00566   static TQDate zoomDate;
00567   static TQTimer *t = new TQTimer( this );
00568 
00569 
00570   //Zoom to the selected incidence, on the other way
00571   // zoom to the date on screen after the first mousewheel move.
00572   if ( orient == Qt::Horizontal ) {
00573     TQDate date=mAgenda->selectedIncidenceDate();
00574     if ( date.isValid() )
00575       zoomDate=date;
00576     else{
00577       if ( !t->isActive() ) {
00578         zoomDate= mSelectedDates[pos.x()];
00579       }
00580       t->start ( 1000,true );
00581     }
00582     if ( delta > 0 )
00583       zoomOutHorizontally( zoomDate );
00584     else
00585       zoomInHorizontally( zoomDate );
00586   } else {
00587     // Qt::Vertical zoom
00588     TQPoint posConstentsOld = mAgenda->gridToContents(pos);
00589     if ( delta > 0 ) {
00590       zoomOutVertically();
00591     } else {
00592       zoomInVertically();
00593     }
00594     TQPoint posConstentsNew = mAgenda->gridToContents(pos);
00595     mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() );
00596   }
00597 }
00598 
00599 void KOAgendaView::createDayLabels( bool force )
00600 {
00601 //  kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl;
00602 
00603   // Check if mSelectedDates has changed, if not just return
00604   // Removes some flickering and gains speed (since this is called by each updateView())
00605   if ( !force && mSaveSelectedDates == mSelectedDates ) {
00606     return;
00607   }
00608   mSaveSelectedDates = mSelectedDates;
00609 
00610   delete mDayLabels;
00611   mDateDayLabels.clear();
00612 
00613   mDayLabels = new TQFrame (mDayLabelsFrame);
00614   mLayoutDayLabels = new TQHBoxLayout(mDayLabels);
00615   if ( !mIsSideBySide )
00616     mLayoutDayLabels->addSpacing(mTimeLabels->width());
00617 
00618   const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
00619 
00620   DateList::ConstIterator dit;
00621   for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
00622     TQDate date = *dit;
00623     TQBoxLayout *dayLayout = new TQVBoxLayout(mLayoutDayLabels);
00624     mLayoutDayLabels->setStretchFactor(dayLayout, 1);
00625 //    dayLayout->setMinimumWidth(1);
00626 
00627     int dW = calsys->dayOfWeek(date);
00628     TQString veryLongStr = KGlobal::locale()->formatDate( date );
00629     TQString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" )
00630         .arg( calsys->weekDayName( dW, true ) )
00631         .arg( calsys->day(date) );
00632     TQString shortstr = TQString::number(calsys->day(date));
00633 
00634     KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr,
00635                                                       longstr, veryLongStr, mDayLabels);
00636     dayLabel->useShortText(); // will be recalculated in updateDayLabelSizes() anyway
00637     dayLabel->setMinimumWidth(1);
00638     dayLabel->setAlignment(TQLabel::AlignHCenter);
00639     if (date == TQDate::currentDate()) {
00640       TQFont font = dayLabel->font();
00641       font.setBold(true);
00642       dayLabel->setFont(font);
00643     }
00644     dayLayout->addWidget(dayLabel);
00645     mDateDayLabels.append( dayLabel );
00646 
00647     // if a holiday region is selected, show the holiday name
00648     TQStringList texts = KOGlobals::self()->holiday( date );
00649     TQStringList::ConstIterator textit = texts.begin();
00650     for ( ; textit != texts.end(); ++textit ) {
00651       // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00652       KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), TQString(), mDayLabels );
00653       label->setMinimumWidth(1);
00654       label->setAlignment(AlignCenter);
00655       dayLayout->addWidget(label);
00656     }
00657 
00658 #ifndef KORG_NOPLUGINS
00659     CalendarDecoration::List cds = KOCore::self()->calendarDecorations();
00660     CalendarDecoration *it;
00661     for(it = cds.first(); it; it = cds.next()) {
00662       TQString text = it->shortText( date );
00663       if ( !text.isEmpty() ) {
00664         // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00665         KOAlternateLabel*label = new KOAlternateLabel( text, text, TQString(), mDayLabels );
00666         label->setMinimumWidth(1);
00667         label->setAlignment(AlignCenter);
00668         dayLayout->addWidget(label);
00669       }
00670     }
00671 
00672     for(it = cds.first(); it; it = cds.next()) {
00673       TQWidget *wid = it->smallWidget(mDayLabels,date);
00674       if ( wid ) {
00675 //      wid->setHeight(20);
00676         dayLayout->addWidget(wid);
00677       }
00678     }
00679 #endif
00680   }
00681 
00682   if ( !mIsSideBySide )
00683     mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width());
00684   mDayLabels->show();
00685   TQTimer::singleShot( 0, this, TQT_SLOT( updateDayLabelSizes() ) );
00686 }
00687 
00688 void KOAgendaView::enableAgendaUpdate( bool enable )
00689 {
00690   mAllowAgendaUpdate = enable;
00691 }
00692 
00693 int KOAgendaView::maxDatesHint()
00694 {
00695   // Not sure about the max number of events, so return 0 for now.
00696   return 0;
00697 }
00698 
00699 int KOAgendaView::currentDateCount()
00700 {
00701   return mSelectedDates.count();
00702 }
00703 
00704 Incidence::List KOAgendaView::selectedIncidences()
00705 {
00706   Incidence::List selected;
00707   Incidence *incidence;
00708 
00709   incidence = mAgenda->selectedIncidence();
00710   if (incidence) selected.append(incidence);
00711 
00712   incidence = mAllDayAgenda->selectedIncidence();
00713   if (incidence) selected.append(incidence);
00714 
00715   return selected;
00716 }
00717 
00718 DateList KOAgendaView::selectedIncidenceDates()
00719 {
00720   DateList selected;
00721   TQDate qd;
00722 
00723   qd = mAgenda->selectedIncidenceDate();
00724   if (qd.isValid()) selected.append(qd);
00725 
00726   qd = mAllDayAgenda->selectedIncidenceDate();
00727   if (qd.isValid()) selected.append(qd);
00728 
00729   return selected;
00730 }
00731 
00732 bool KOAgendaView::eventDurationHint( TQDateTime &startDt, TQDateTime &endDt,
00733                                       bool &allDay )
00734 {
00735   if ( selectionStart().isValid() ) {
00736     TQDateTime start = selectionStart();
00737     TQDateTime end = selectionEnd();
00738 
00739     if ( start.secsTo( end ) == 15*60 ) {
00740       // One cell in the agenda view selected, e.g.
00741       // because of a double-click, => Use the default duration
00742       TQTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() );
00743       int addSecs = ( defaultDuration.hour()*3600 ) +
00744                     ( defaultDuration.minute()*60 );
00745       end = start.addSecs( addSecs );
00746     }
00747 
00748     startDt = start;
00749     endDt = end;
00750     allDay = selectedIsAllDay();
00751     return true;
00752   }
00753   return false;
00754 }
00755 
00757 bool KOAgendaView::selectedIsSingleCell()
00758 {
00759   if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false;
00760 
00761   if (selectedIsAllDay()) {
00762     int days = selectionStart().daysTo(selectionEnd());
00763     return ( days < 1 );
00764   } else {
00765     int secs = selectionStart().secsTo(selectionEnd());
00766     return ( secs <= 24*60*60/mAgenda->rows() );
00767   }
00768 }
00769 
00770 
00771 void KOAgendaView::updateView()
00772 {
00773 //  kdDebug(5850) << "KOAgendaView::updateView()" << endl;
00774   fillAgenda();
00775 }
00776 
00777 
00778 /*
00779   Update configuration settings for the agenda view. This method is not
00780   complete.
00781 */
00782 void KOAgendaView::updateConfig()
00783 {
00784 //  kdDebug(5850) << "KOAgendaView::updateConfig()" << endl;
00785 
00786   // update config for children
00787   mTimeLabels->updateConfig();
00788   mAgenda->updateConfig();
00789   mAllDayAgenda->updateConfig();
00790 
00791   // widget synchronization
00792   // FIXME: find a better way, maybe signal/slot
00793   mTimeLabels->positionChanged();
00794 
00795   // for some reason, this needs to be called explicitly
00796   mTimeLabels->repaint();
00797 
00798   updateTimeBarWidth();
00799 
00800   // ToolTips displaying summary of events
00801   KOAgendaItem::toolTipGroup()->setEnabled(KOPrefs::instance()
00802                                            ->mEnableToolTips);
00803 
00804   setHolidayMasks();
00805 
00806   createDayLabels( true );
00807 
00808   updateView();
00809 }
00810 
00811 void KOAgendaView::updateTimeBarWidth()
00812 {
00813   int width;
00814 
00815   width = mDummyAllDayLeft->fontMetrics().width( i18n("All Day") );
00816   width = TQMAX( width, mTimeLabels->width() );
00817 
00818   mDummyAllDayLeft->setFixedWidth( width );
00819   mTimeLabels->setFixedWidth( width );
00820 }
00821 
00822 void KOAgendaView::updateDayLabelSizes()
00823 {
00824   // First, calculate the maximum text type that fits for all labels
00825   KOAlternateLabel::TextType overallType = KOAlternateLabel::Extensive;
00826   TQPtrList<KOAlternateLabel>::const_iterator it = mDateDayLabels.constBegin();
00827   for( ; it != mDateDayLabels.constEnd(); it++ ) {
00828     KOAlternateLabel::TextType type = (*it)->largestFittingTextType();
00829     if ( type < overallType )
00830       overallType = type;
00831   }
00832 
00833   // Then, set that maximum text type to all the labels
00834   it = mDateDayLabels.constBegin();
00835   for( ; it != mDateDayLabels.constEnd(); it++ ) {
00836     (*it)->setFixedType( overallType );
00837   }
00838 }
00839 
00840 void KOAgendaView::resizeEvent( TQResizeEvent *resizeEvent )
00841 {
00842   updateDayLabelSizes();
00843   KOrg::AgendaView::resizeEvent( resizeEvent );
00844 }
00845 
00846 void KOAgendaView::updateEventDates( KOAgendaItem *item )
00847 {
00848   kdDebug(5850) << "KOAgendaView::updateEventDates(): " << item->text()
00849                 << "; item->cellXLeft(): " << item->cellXLeft()
00850                 << "; item->cellYTop(): " << item->cellYTop()
00851                 << "; item->lastMultiItem(): " << item->lastMultiItem()
00852                 << "; item->itemPos(): " << item->itemPos()
00853                 << "; item->itemCount(): " << item->itemCount()
00854                 << endl;
00855 
00856   TQDateTime startDt, endDt;
00857 
00858   // Start date of this incidence, calculate the offset from it (so recurring and
00859   // non-recurring items can be treated exactly the same, we never need to check
00860   // for doesRecur(), because we only move the start day by the number of days the
00861   // agenda item was really moved. Smart, isn't it?)
00862   TQDate thisDate;
00863   if ( item->cellXLeft() < 0 ) {
00864     thisDate = ( mSelectedDates.first() ).addDays( item->cellXLeft() );
00865   } else {
00866     thisDate = mSelectedDates[ item->cellXLeft() ];
00867   }
00868   TQDate oldThisDate( item->itemDate() );
00869   const int daysOffset = oldThisDate.daysTo( thisDate );
00870   int daysLength = 0;
00871 
00872   // startDt.setDate( startDate );
00873 
00874   Incidence *incidence = item->incidence();
00875   if ( !incidence ) {
00876     return;
00877   }
00878   if ( !mChanger ||
00879        !mChanger->beginChange( incidence, resourceCalendar(), subResourceCalendar() ) ) {
00880     return;
00881   }
00882   Incidence *oldIncidence = incidence->clone();
00883 
00884   TQTime startTime( 0, 0, 0 ), endTime( 0, 0, 0 );
00885   if ( incidence->doesFloat() ) {
00886     daysLength = item->cellWidth() - 1;
00887   } else {
00888     startTime = mAgenda->gyToTime( item->cellYTop() );
00889     if ( item->lastMultiItem() ) {
00890       endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 );
00891       daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft();
00892       kdDebug(5850) << "item->lastMultiItem()->cellXLeft(): " << item->lastMultiItem()->cellXLeft()
00893                     << endl;
00894     } else if ( item->itemPos() == item->itemCount() && item->itemCount() > 1 ) {
00895       /* multiitem handling in agenda assumes two things:
00896          - The start (first KOAgendaItem) is always visible.
00897          - The first KOAgendaItem of the incidence has a non-null item->lastMultiItem()
00898              pointing to the last KOagendaItem.
00899 
00900         But those aren't always met, for example when in day-view.
00901         kolab/issue4417
00902        */
00903 
00904       // Cornercase 1: - Resizing the end of the event but the start isn't visible
00905       endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
00906       daysLength = item->itemCount() - 1;
00907       startTime = incidence->dtStart().time();
00908     } else if ( item->itemPos() == 1 && item->itemCount() > 1 ) {
00909       // Cornercase 2: - Resizing the start of the event but the end isn't visible
00910       endTime = incidence->dtEnd().time();
00911       daysLength = item->itemCount() - 1;
00912     } else {
00913       endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
00914     }
00915   }
00916 
00917   kdDebug(5850) << "daysLength: " << daysLength << "; startTime: " << startTime
00918                 << "; endTime: " << endTime << "; thisDate: " << thisDate
00919                 << "; incidence->dtStart(): " << incidence->dtStart() << endl;
00920 
00921   // FIXME: use a visitor here
00922   if ( incidence->type() == "Event" ) {
00923     startDt = incidence->dtStart();
00924     startDt = startDt.addDays( daysOffset );
00925     startDt.setTime( startTime );
00926     endDt = startDt.addDays( daysLength );
00927     endDt.setTime( endTime );
00928     Event* ev = static_cast<Event*>( incidence );
00929     if ( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) {
00930       // No change
00931       delete oldIncidence;
00932       return;
00933     }
00934     incidence->setDtStart( startDt );
00935     ev->setDtEnd( endDt );
00936   } else if ( incidence->type() == "Todo" ) {
00937     Todo *td = static_cast<Todo*>( incidence );
00938     startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
00939     startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) );
00940     startDt.setTime( startTime );
00941     endDt.setDate( thisDate );
00942     endDt.setTime( endTime );
00943 
00944     if( td->dtDue() == endDt ) {
00945       // No change
00946       delete oldIncidence;
00947       return;
00948     }
00949   }
00950   // FIXME: Adjusting the recurrence should really go to CalendarView so this
00951   // functionality will also be available in other views!
00952   // TODO_Recurrence: This does not belong here, and I'm not really sure
00953   // how it's supposed to work anyway.
00954 /*
00955     Recurrence *recur = incidence->recurrence();
00956     if ( recur->doesRecur() && daysOffset != 0 ) {
00957     switch ( recur->recurrenceType() ) {
00958       case Recurrence::rYearlyPos: {
00959         int freq = recur->frequency();
00960         int duration = recur->duration();
00961         TQDate endDt( recur->endDate() );
00962         bool negative = false;
00963 
00964         TQPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() );
00965         if ( monthPos.first() ) {
00966           negative = monthPos.first()->negative;
00967         }
00968         TQBitArray days( 7 );
00969         int pos = 0;
00970         days.fill( false );
00971         days.setBit( thisDate.dayOfWeek() - 1 );
00972         if ( negative ) {
00973           pos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
00974         } else {
00975           pos =  ( thisDate.day()-1 ) / 7 + 1;
00976         }
00977         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00978         recur->unsetRecurs();
00979         if ( duration != 0 ) {
00980           recur->setYearly( Recurrence::rYearlyPos, freq, duration );
00981         } else {
00982           recur->setYearly( Recurrence::rYearlyPos, freq, endDt );
00983         }
00984         recur->addYearlyMonthPos( pos, days );
00985         recur->addYearlyNum( thisDate.month() );
00986 
00987         break; }
00988         case Recurrence::rYearlyDay: {
00989           int freq = recur->frequency();
00990           int duration = recur->duration();
00991           TQDate endDt( recur->endDate() );
00992         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
00993           recur->unsetRecurs();
00994           if ( duration == 0 ) { // end by date
00995             recur->setYearly( Recurrence::rYearlyDay, freq, endDt );
00996           } else {
00997             recur->setYearly( Recurrence::rYearlyDay, freq, duration );
00998           }
00999           recur->addYearlyNum( thisDate.dayOfYear() );
01000           break; }
01001           case Recurrence::rYearlyMonth: {
01002             int freq = recur->frequency();
01003             int duration = recur->duration();
01004             TQDate endDt( recur->endDate() );
01005         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01006             recur->unsetRecurs();
01007             if ( duration != 0 ) {
01008               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration );
01009             } else {
01010               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt );
01011             }
01012             recur->addYearlyNum( thisDate.month() );
01013             break; }
01014             case Recurrence::rMonthlyPos: {
01015               int freq = recur->frequency();
01016               int duration = recur->duration();
01017               TQDate endDt( recur->endDate() );
01018               TQPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() );
01019               if ( !monthPos.isEmpty() ) {
01020           // FIXME: How shall I adapt the day x of week Y if we move the date across month borders???
01021           // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
01022           // That's fine for korganizer, but might mess up other organizers.
01023                 TQBitArray rDays( 7 );
01024                 rDays = monthPos.first()->rDays;
01025                 bool negative = monthPos.first()->negative;
01026                 int newPos;
01027                 rDays.fill( false );
01028                 rDays.setBit( thisDate.dayOfWeek() - 1 );
01029                 if ( negative ) {
01030                   newPos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
01031                 } else {
01032                   newPos =  ( thisDate.day()-1 ) / 7 + 1;
01033                 }
01034 
01035           // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01036                 recur->unsetRecurs();
01037                 if ( duration == 0 ) { // end by date
01038                   recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt );
01039                 } else {
01040                   recur->setMonthly( Recurrence::rMonthlyPos, freq, duration );
01041                 }
01042                 recur->addMonthlyPos( newPos, rDays );
01043               }
01044               break;}
01045               case Recurrence::rMonthlyDay: {
01046                 int freq = recur->frequency();
01047                 int duration = recur->duration();
01048                 TQDate endDt( recur->endDate() );
01049                 TQPtrList<int> monthDays( recur->monthDays() );
01050         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01051                 recur->unsetRecurs();
01052                 if ( duration == 0 ) { // end by date
01053                   recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt );
01054                 } else {
01055                   recur->setMonthly( Recurrence::rMonthlyDay, freq, duration );
01056                 }
01057         // FIXME: How shall I adapt the n-th day if we move the date across month borders???
01058         // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
01059         // That's fine for korganizer, but might mess up other organizers.
01060                 recur->addMonthlyDay( thisDate.day() );
01061 
01062                 break;}
01063                 case Recurrence::rWeekly: {
01064                   TQBitArray days(7), oldDays( recur->days() );
01065                   int offset = daysOffset % 7;
01066                   if ( offset<0 ) offset = (offset+7) % 7;
01067         // rotate the days
01068                   for (int d=0; d<7; d++ ) {
01069                     days.setBit( (d+offset) % 7, oldDays.at(d) );
01070                   }
01071                   if ( recur->duration() == 0 ) { // end by date
01072                     recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() );
01073                   } else { // duration or no end
01074                     recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() );
01075                   }
01076                   break;}
01077       // nothing to be done for the following:
01078       case Recurrence::rDaily:
01079       case Recurrence::rHourly:
01080       case Recurrence::rMinutely:
01081       case Recurrence::rNone:
01082       default:
01083         break;
01084     }
01085     if ( recur->duration()==0 ) { // end by date
01086       recur->setEndDate( recur->endDate().addDays( daysOffset ) );
01087     }
01088     KMessageBox::information( this, i18n("A recurring calendar item was moved "
01089                               "to a different day. The recurrence settings "
01090                               "have been updated with that move. Please check "
01091                               "them in the editor."),
01092                               i18n("Recurrence Moved"),
01093                               "RecurrenceMoveInAgendaWarning" );
01094   }*/
01095 
01096   // FIXME: use a visitor here
01097   if ( incidence->type() == "Event" ) {
01098     incidence->setDtStart( startDt );
01099     static_cast<Event*>( incidence )->setDtEnd( endDt );
01100   } else if ( incidence->type() == "Todo" ) {
01101     Todo *td = static_cast<Todo*>( incidence );
01102     if ( td->hasStartDate() ) {
01103       td->setDtStart( startDt );
01104     }
01105     td->setDtDue( endDt );
01106   }
01107 
01108   item->setItemDate( startDt.date() );
01109 
01110   KOIncidenceToolTip::remove( item );
01111   KOIncidenceToolTip::add( item, calendar(), incidence, thisDate, KOAgendaItem::toolTipGroup() );
01112 
01113   const bool result = mChanger->changeIncidence( oldIncidence, incidence,
01114                                                  KOGlobals::DATE_MODIFIED, this );
01115   mChanger->endChange( incidence, resourceCalendar(), subResourceCalendar() );
01116   delete oldIncidence;
01117 
01118   if ( !result ) {
01119     mPendingChanges = true;
01120     TQTimer::singleShot( 0, this, TQT_SLOT(updateView()) );
01121     return;
01122   }
01123 
01124   // don't update the agenda as the item already has the correct coordinates.
01125   // an update would delete the current item and recreate it, but we are still
01126   // using a pointer to that item! => CRASH
01127   enableAgendaUpdate( false );
01128   // We need to do this in a timer to make sure we are not deleting the item
01129   // we are currently working on, which would lead to crashes
01130   // Only the actually moved agenda item is already at the correct position and mustn't be
01131   // recreated. All others have to!!!
01132   if ( incidence->doesRecur() ) {
01133     mUpdateItem = incidence;
01134     TQTimer::singleShot( 0, this, TQT_SLOT( doUpdateItem() ) );
01135   }
01136 
01137     enableAgendaUpdate( true );
01138 
01139 //  kdDebug(5850) << "KOAgendaView::updateEventDates() done " << endl;
01140 }
01141 
01142 void KOAgendaView::doUpdateItem()
01143 {
01144   if ( mUpdateItem ) {
01145     changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED );
01146     mUpdateItem = 0;
01147   }
01148 }
01149 
01150 
01151 
01152 void KOAgendaView::showDates( const TQDate &start, const TQDate &end )
01153 {
01154 //  kdDebug(5850) << "KOAgendaView::selectDates" << endl;
01155   if ( !mSelectedDates.isEmpty() && mSelectedDates.first() == start
01156         && mSelectedDates.last() == end && !mPendingChanges )
01157     return;
01158 
01159   mSelectedDates.clear();
01160 
01161   TQDate d = start;
01162   while ( d <= end ) {
01163     mSelectedDates.append( d );
01164     d = d.addDays( 1 );
01165   }
01166 
01167   mAreDatesInitialized = true;
01168 
01169   // and update the view
01170   fillAgenda();
01171 }
01172 
01173 
01174 void KOAgendaView::showIncidences( const Incidence::List &, const TQDate & )
01175 {
01176   kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl;
01177 }
01178 
01179 void KOAgendaView::insertIncidence( Incidence *incidence, const TQDate &curDate )
01180 {
01181   if ( !filterByResource( incidence ) ) {
01182     return;
01183   }
01184 
01185   // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's
01186   Event *event = dynamic_cast<Event *>( incidence );
01187   Todo  *todo  = dynamic_cast<Todo  *>( incidence );
01188 
01189   int curCol = mSelectedDates.first().daysTo( curDate );
01190 
01191   // In case incidence->dtStart() isn't visible (crosses bounderies)
01192   if ( curCol < 0 ) {
01193     curCol = 0;
01194   }
01195 
01196   // The date for the event is not displayed, just ignore it
01197   if ( curCol >= int( mSelectedDates.count() ) ) {
01198     return;
01199   }
01200 
01201   // Default values, which can never be reached
01202   mMinY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) ) + 1;
01203   mMaxY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) ) - 1;
01204 
01205   int beginX;
01206   int endX;
01207   TQDate columnDate;
01208   if ( event ) {
01209     TQDate firstVisibleDate = mSelectedDates.first();
01210     // its crossing bounderies, lets calculate beginX and endX
01211     if ( curDate < firstVisibleDate ) {
01212       beginX = curCol + firstVisibleDate.daysTo( curDate );
01213       endX   = beginX + event->dtStart().daysTo( event->dtEnd() );
01214       columnDate = firstVisibleDate;
01215     } else {
01216       beginX = curCol;
01217       endX   = beginX + event->dtStart().daysTo( event->dtEnd() );
01218       columnDate = curDate;
01219     }
01220   } else if ( todo ) {
01221     if ( !todo->hasDueDate() ) {
01222       return;  // todo shall not be displayed if it has no date
01223     }
01224     columnDate = curDate;
01225     beginX = endX = curCol;
01226 
01227   } else {
01228     return;
01229   }
01230   if ( todo && todo->isOverdue() ) {
01231     mAllDayAgenda->insertAllDayItem( incidence, columnDate, curCol, curCol );
01232   } else if ( incidence->doesFloat() ||
01233               ( todo &&
01234                   !todo->dtDue().isValid() ) ) {
01235       mAllDayAgenda->insertAllDayItem( incidence, columnDate, beginX, endX );
01236   } else if ( event && event->isMultiDay() ) {
01237     int startY = mAgenda->timeToY( event->dtStart().time() );
01238     TQTime endtime = event->dtEnd().time();
01239     if ( endtime == TQTime( 0, 0, 0 ) ) {
01240       endtime = TQTime( 23, 59, 59 );
01241     }
01242     int endY = mAgenda->timeToY( endtime ) - 1;
01243     if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) {
01244       mAgenda->insertMultiItem( event, columnDate, beginX, endX, startY, endY );
01245 
01246     }
01247     if ( beginX == curCol ) {
01248       mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) );
01249       if ( startY < mMinY[curCol] ) {
01250         mMinY[curCol] = startY;
01251       }
01252     } else if ( endX == curCol ) {
01253       mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) );
01254       if ( endY > mMaxY[curCol] ) {
01255         mMaxY[curCol] = endY;
01256       }
01257     } else {
01258       mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) );
01259       mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) );
01260     }
01261   } else {
01262     int startY = 0, endY = 0;
01263     if ( event ) {
01264       startY = mAgenda->timeToY( incidence->dtStart().time() );
01265       TQTime endtime = event->dtEnd().time();
01266       if ( endtime == TQTime( 0, 0, 0 ) ) {
01267         endtime = TQTime( 23, 59, 59 );
01268       }
01269       endY = mAgenda->timeToY( endtime ) - 1;
01270     }
01271     if ( todo ) {
01272       TQTime t = todo->dtDue().time();
01273 
01274       if ( t == TQTime( 0, 0 ) ) {
01275         t = TQTime( 23, 59 );
01276       }
01277 
01278       int halfHour = 1800;
01279       if ( t.addSecs( -halfHour ) < t ) {
01280         startY = mAgenda->timeToY( t.addSecs( -halfHour ) );
01281         endY   = mAgenda->timeToY( t ) - 1;
01282       } else {
01283         startY = 0;
01284         endY   = mAgenda->timeToY( t.addSecs( halfHour ) ) - 1;
01285       }
01286     }
01287     if ( endY < startY ) {
01288       endY = startY;
01289     }
01290     mAgenda->insertItem( incidence, columnDate, curCol, startY, endY, 1, 1 );
01291     if ( startY < mMinY[curCol] ) {
01292       mMinY[curCol] = startY;
01293     }
01294     if ( endY > mMaxY[curCol] ) {
01295       mMaxY[curCol] = endY;
01296     }
01297   }
01298 }
01299 
01300 void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence )
01301 {
01302   Todo *todo = dynamic_cast<Todo *>(incidence);
01303   CalFilter *filter = calendar()->filter();
01304   if ( ( filter && !filter->filterIncidence( incidence ) ) ||
01305        ( ( todo && !KOPrefs::instance()->showAllDayTodo() ) ) ) {
01306     return;
01307   }
01308 
01309   displayIncidence( incidence );
01310 }
01311 
01312 void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode )
01313 {
01314   switch ( mode ) {
01315     case KOGlobals::INCIDENCEADDED:
01316     {
01317       // Add an event. No need to recreate the whole view!
01318       // recreating everything even causes troubles: dropping to the
01319       // day matrix recreates the agenda items, but the evaluation is
01320       // still in an agendaItems' code, which was deleted in the mean time.
01321       // Thus KOrg crashes...
01322       changeIncidenceDisplayAdded( incidence );
01323       updateEventIndicators();
01324       break;
01325     }
01326     case KOGlobals::INCIDENCEEDITED:
01327     {
01328       if ( mAllowAgendaUpdate ) {
01329         removeIncidence( incidence );
01330         changeIncidenceDisplayAdded( incidence );
01331       }
01332       updateEventIndicators();
01333       break;
01334     }
01335     case KOGlobals::INCIDENCEDELETED:
01336     {
01337       removeIncidence( incidence );
01338       updateEventIndicators();
01339       break;
01340     }
01341     default:
01342       return;
01343   }
01344 
01345   // HACK: Update the view if the all-day agenda has been modified.
01346   // Do this because there are some layout problems in the
01347   // all-day agenda that are not easily solved, but clearing
01348   // and redrawing works ok.
01349   if ( incidence->doesFloat() ) {
01350     updateView();
01351   }
01352 }
01353 
01354 void KOAgendaView::fillAgenda( const TQDate & )
01355 {
01356   fillAgenda();
01357 }
01358 
01359 void KOAgendaView::fillAgenda()
01360 {
01361   if ( !mAreDatesInitialized ) {
01362     return;
01363   }
01364 
01365   mPendingChanges = false;
01366 
01367   /* Remember the uids of the selected items. In case one of the
01368    * items was deleted and re-added, we want to reselect it. */
01369   const TQString &selectedAgendaUid = mAgenda->lastSelectedUid();
01370   const TQString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
01371 
01372   enableAgendaUpdate( true );
01373   clearView();
01374 
01375   mAllDayAgenda->changeColumns( mSelectedDates.count() );
01376   mAgenda->changeColumns( mSelectedDates.count() );
01377   mEventIndicatorTop->changeColumns( mSelectedDates.count() );
01378   mEventIndicatorBottom->changeColumns( mSelectedDates.count() );
01379 
01380   createDayLabels( false );
01381   setHolidayMasks();
01382 
01383   mMinY.resize( mSelectedDates.count() );
01384   mMaxY.resize( mSelectedDates.count() );
01385 
01386   mAgenda->setDateList( mSelectedDates );
01387 
01388   bool somethingReselected = false;
01389   Incidence::List incidences = calendar()->incidences();
01390 
01391   for ( Incidence::List::ConstIterator it = incidences.begin(); it!=incidences.constEnd(); ++it ) {
01392     Incidence *incidence = (*it);
01393     displayIncidence( incidence );
01394 
01395     if( incidence->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
01396       mAgenda->selectItemByUID( incidence->uid() );
01397       somethingReselected = true;
01398     }
01399 
01400     if( incidence->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
01401       mAllDayAgenda->selectItemByUID( incidence->uid() );
01402       somethingReselected = true;
01403     }
01404 
01405   }
01406 
01407   mAgenda->checkScrollBoundaries();
01408   updateEventIndicators();
01409 
01410   //  mAgenda->viewport()->update();
01411   //  mAllDayAgenda->viewport()->update();
01412 
01413   // make invalid
01414   deleteSelectedDateTime();
01415 
01416   if( !somethingReselected ) {
01417     emit incidenceSelected( 0, TQDate() );
01418   }
01419 }
01420 
01421 void KOAgendaView::displayIncidence( Incidence *incidence )
01422 {
01423   TQDate today = TQDate::currentDate();
01424   DateTimeList::iterator t;
01425 
01426   // FIXME: use a visitor here
01427   Todo *todo = dynamic_cast<Todo *>( incidence );
01428   Event *event = dynamic_cast<Event *>( incidence );
01429 
01430   TQDateTime firstVisibleDateTime = mSelectedDates.first();
01431   TQDateTime lastVisibleDateTime = mSelectedDates.last();
01432 
01433   lastVisibleDateTime.setTime( TQTime( 23, 59, 59, 59 ) );
01434   firstVisibleDateTime.setTime( TQTime( 0, 0 ) );
01435   DateTimeList dateTimeList;
01436 
01437   TQDateTime incDtStart = incidence->dtStart();
01438   TQDateTime incDtEnd   = incidence->dtEnd();
01439 
01440   if ( todo &&
01441        ( !KOPrefs::instance()->showAllDayTodo() || !todo->hasDueDate() ) ) {
01442     return;
01443   }
01444 
01445   if ( incidence->doesRecur() ) {
01446     int eventDuration = event ? incDtStart.daysTo( incDtEnd ) : 0;
01447 
01448     // if there's a multiday event that starts before firstVisibleDateTime but ends after
01449     // lets include it. timesInInterval() ignores incidences that aren't totaly inside
01450     // the range
01451     TQDateTime startDateTimeWithOffset = firstVisibleDateTime.addDays( -eventDuration );
01452     dateTimeList =
01453       incidence->recurrence()->timesInInterval( startDateTimeWithOffset,
01454                                                 lastVisibleDateTime );
01455   } else {
01456     TQDateTime dateToAdd; // date to add to our date list
01457     TQDateTime incidenceStart;
01458     TQDateTime incidenceEnd;
01459 
01460     if ( todo && todo->hasDueDate() && !todo->isOverdue() ) {
01461       // If it's not overdue it will be shown at the original date (not today)
01462       dateToAdd = todo->dtDue();
01463 
01464       // To-dos are drawn with the bottom of the rectangle at dtDue
01465       // if dtDue is at 00:00, then it should be displayed in the previous day, at 23:59
01466       if ( !todo->doesFloat() && dateToAdd.time() == TQTime( 0, 0 ) ) {
01467         dateToAdd = dateToAdd.addSecs( -1 );
01468       }
01469 
01470       incidenceEnd = dateToAdd;
01471     } else if ( event ) {
01472       dateToAdd = incDtStart;
01473       incidenceEnd = incDtEnd;
01474     }
01475 
01476     if ( incidence->doesFloat() ) {
01477       // so comparisons with < > actually work
01478       dateToAdd.setTime( TQTime( 0, 0 ) );
01479       incidenceEnd.setTime( TQTime( 23, 59, 59, 59 ) );
01480     }
01481 
01482     if ( dateToAdd <= lastVisibleDateTime && incidenceEnd > firstVisibleDateTime ) {
01483       dateTimeList += dateToAdd;
01484     }
01485   }
01486 
01487   // ToDo items shall be displayed today if they are already overdude
01488   TQDateTime dateTimeToday = today;
01489   if ( todo &&
01490        todo->isOverdue() &&
01491        dateTimeToday >= firstVisibleDateTime &&
01492        dateTimeToday <= lastVisibleDateTime ) {
01493 
01494     bool doAdd = true;
01495 
01496     if ( todo->doesRecur() ) {
01497       /* If there's a recurring instance showing up today don't add "today" again
01498        * we don't want the event to appear duplicated */
01499       for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01500         if ( (*t).date() == today ) {
01501           doAdd = false;
01502           break;
01503        }
01504       }
01505     }
01506 
01507     if ( doAdd ) {
01508       dateTimeList += dateTimeToday;
01509     }
01510   }
01511 
01512   for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
01513     insertIncidence( incidence, (*t).date() );
01514   }
01515 }
01516 
01517 void KOAgendaView::clearView()
01518 {
01519 //  kdDebug(5850) << "ClearView" << endl;
01520   mAllDayAgenda->clear();
01521   mAgenda->clear();
01522 }
01523 
01524 CalPrinterBase::PrintType KOAgendaView::printType()
01525 {
01526   if ( currentDateCount() == 1 ) return CalPrinterBase::Day;
01527   else return CalPrinterBase::Week;
01528 }
01529 
01530 void KOAgendaView::updateEventIndicatorTop( int newY )
01531 {
01532   uint i;
01533   for( i = 0; i < mMinY.size(); ++i ) {
01534     mEventIndicatorTop->enableColumn( i, newY > mMinY[i] );
01535   }
01536   mEventIndicatorTop->update();
01537 }
01538 
01539 void KOAgendaView::updateEventIndicatorBottom( int newY )
01540 {
01541   uint i;
01542   for( i = 0; i < mMaxY.size(); ++i ) {
01543     mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
01544   }
01545   mEventIndicatorBottom->update();
01546 }
01547 
01548 void KOAgendaView::slotTodoDropped( Todo *todo, const TQPoint &gpos, bool allDay )
01549 {
01550   if ( gpos.x()<0 || gpos.y()<0 ) return;
01551   TQDate day = mSelectedDates[gpos.x()];
01552   TQTime time = mAgenda->gyToTime( gpos.y() );
01553   TQDateTime newTime( day, time );
01554 
01555   if ( todo ) {
01556     Todo *existingTodo = calendar()->todo( todo->uid() );
01557     if ( existingTodo ) {
01558       kdDebug(5850) << "Drop existing Todo" << endl;
01559       Todo *oldTodo = existingTodo->clone();
01560       if ( mChanger &&
01561            mChanger->beginChange( existingTodo, resourceCalendar(), subResourceCalendar() ) ) {
01562         existingTodo->setDtDue( newTime );
01563         existingTodo->setFloats( allDay );
01564         existingTodo->setHasDueDate( true );
01565         mChanger->changeIncidence( oldTodo, existingTodo,
01566                                    KOGlobals::DATE_MODIFIED, this );
01567         mChanger->endChange( existingTodo, resourceCalendar(), subResourceCalendar() );
01568       } else {
01569         KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
01570                             "because it cannot be locked.") );
01571       }
01572       delete oldTodo;
01573     } else {
01574       kdDebug(5850) << "Drop new Todo" << endl;
01575       todo->setDtDue( newTime );
01576       todo->setFloats( allDay );
01577       todo->setHasDueDate( true );
01578       if ( !mChanger->addIncidence( todo, 0, TQString(), this ) ) {
01579         KODialogManager::errorSaveIncidence( this, todo );
01580       }
01581     }
01582   }
01583 }
01584 
01585 void KOAgendaView::startDrag( Incidence *incidence )
01586 {
01587 #ifndef KORG_NODND
01588   DndFactory factory( calendar() );
01589   ICalDrag *vd = factory.createDrag( incidence, this );
01590   if ( vd->drag() ) {
01591     kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl;
01592   }
01593 #endif
01594 }
01595 
01596 void KOAgendaView::readSettings()
01597 {
01598   readSettings(KOGlobals::self()->config());
01599 }
01600 
01601 void KOAgendaView::readSettings(KConfig *config)
01602 {
01603 //  kdDebug(5850) << "KOAgendaView::readSettings()" << endl;
01604 
01605   config->setGroup("Views");
01606 
01607 #ifndef KORG_NOSPLITTER
01608   TQValueList<int> sizes = config->readIntListEntry("Separator AgendaView");
01609   if (sizes.count() == 2) {
01610     mSplitterAgenda->setSizes(sizes);
01611   }
01612 #endif
01613 
01614   updateConfig();
01615 }
01616 
01617 void KOAgendaView::writeSettings(KConfig *config)
01618 {
01619 //  kdDebug(5850) << "KOAgendaView::writeSettings()" << endl;
01620 
01621   config->setGroup("Views");
01622 
01623 #ifndef KORG_NOSPLITTER
01624   TQValueList<int> list = mSplitterAgenda->sizes();
01625   config->writeEntry("Separator AgendaView",list);
01626 #endif
01627 }
01628 
01629 void KOAgendaView::setHolidayMasks()
01630 {
01631   if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) {
01632     return;
01633   }
01634 
01635   mHolidayMask.resize( mSelectedDates.count() + 1 );
01636 
01637   for( uint i = 0; i < mSelectedDates.count(); ++i ) {
01638     mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
01639   }
01640 
01641   // Store the information about the day before the visible area (needed for
01642   // overnight working hours) in the last bit of the mask:
01643   bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
01644   mHolidayMask[ mSelectedDates.count() ] = showDay;
01645 
01646   mAgenda->setHolidayMask( &mHolidayMask );
01647   mAllDayAgenda->setHolidayMask( &mHolidayMask );
01648 }
01649 
01650 void KOAgendaView::setContentsPos( int y )
01651 {
01652   mAgenda->setContentsPos( 0, y );
01653 }
01654 
01655 void KOAgendaView::setExpandedButton( bool expanded )
01656 {
01657   if ( !mExpandButton ) return;
01658 
01659   if ( expanded ) {
01660     mExpandButton->setPixmap( mExpandedPixmap );
01661   } else {
01662     mExpandButton->setPixmap( mNotExpandedPixmap );
01663   }
01664 }
01665 
01666 void KOAgendaView::clearSelection()
01667 {
01668   mAgenda->deselectItem();
01669   mAllDayAgenda->deselectItem();
01670 }
01671 
01672 void KOAgendaView::newTimeSpanSelectedAllDay( const TQPoint &start, const TQPoint &end )
01673 {
01674   newTimeSpanSelected( start, end );
01675   mTimeSpanInAllDay = true;
01676 }
01677 
01678 void KOAgendaView::newTimeSpanSelected( const TQPoint &start, const TQPoint &end )
01679 {
01680   if (!mSelectedDates.count()) return;
01681 
01682   mTimeSpanInAllDay = false;
01683 
01684   TQDate dayStart = mSelectedDates[ kClamp( start.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01685   TQDate dayEnd = mSelectedDates[ kClamp( end.x(), 0, (int)mSelectedDates.size() - 1 ) ];
01686 
01687   TQTime timeStart = mAgenda->gyToTime(start.y());
01688   TQTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
01689 
01690   TQDateTime dtStart(dayStart,timeStart);
01691   TQDateTime dtEnd(dayEnd,timeEnd);
01692 
01693   mTimeSpanBegin = dtStart;
01694   mTimeSpanEnd = dtEnd;
01695 }
01696 
01697 void KOAgendaView::deleteSelectedDateTime()
01698 {
01699   mTimeSpanBegin.setDate(TQDate());
01700   mTimeSpanEnd.setDate(TQDate());
01701   mTimeSpanInAllDay = false;
01702 }
01703 
01704 void KOAgendaView::setTypeAheadReceiver( TQObject *o )
01705 {
01706   mAgenda->setTypeAheadReceiver( o );
01707   mAllDayAgenda->setTypeAheadReceiver( o );
01708 }
01709 
01710 void KOAgendaView::finishTypeAhead()
01711 {
01712   mAgenda->finishTypeAhead();
01713   mAllDayAgenda->finishTypeAhead();
01714 }
01715 
01716 void KOAgendaView::removeIncidence( Incidence *incidence )
01717 {
01718   mAgenda->removeIncidence( incidence );
01719   mAllDayAgenda->removeIncidence( incidence );
01720 }
01721 
01722 void KOAgendaView::updateEventIndicators()
01723 {
01724   mMinY = mAgenda->minContentsY();
01725   mMaxY = mAgenda->maxContentsY();
01726 
01727   mAgenda->checkScrollBoundaries();
01728   updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
01729   updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
01730 }
01731 
01732 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
01733 {
01734   mChanger = changer;
01735   mAgenda->setIncidenceChanger( changer );
01736   mAllDayAgenda->setIncidenceChanger( changer );
01737 }
01738 
01739 void KOAgendaView::clearTimeSpanSelection()
01740 {
01741   mAgenda->clearSelection();
01742   mAllDayAgenda->clearSelection();
01743   deleteSelectedDateTime();
01744 }
01745 
01746 bool KOAgendaView::filterByResource( Incidence *incidence )
01747 {
01748   // Special handling for groupware to-dos that are in Task folders.
01749   // Put them in the top-level "Calendar" folder for lack of a better
01750   // place since we never show Task type folders even in the
01751   // multiagenda view.
01752   if ( resourceCalendar() && incidence->type() == "Todo" ) {
01753     TQString subRes = resourceCalendar()->subresourceIdentifier( incidence );
01754     if ( resourceCalendar()->subresourceType( subRes ) == "todo" ) {
01755       TQString calmatch = "/.INBOX.directory/Calendar";
01756       TQString i18nmatch = "/.INBOX.directory/" + i18n( "Calendar" );
01757       if ( subResourceCalendar().contains( calmatch ) ||
01758            subResourceCalendar().contains( i18nmatch ) ) {
01759         return true;
01760       }
01761     }
01762   }
01763 
01764   // Normal handling
01765   if ( !resourceCalendar() )
01766     return true;
01767   CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() );
01768   if ( !calRes )
01769     return true;
01770   if ( calRes->resource( incidence ) != resourceCalendar() )
01771     return false;
01772   if ( !subResourceCalendar().isEmpty() ) {
01773     if ( resourceCalendar()->subresourceIdentifier( incidence ) != subResourceCalendar() )
01774       return false;
01775   }
01776   return true;
01777 }
01778 
01779 void KOAgendaView::resourcesChanged()
01780 {
01781   mPendingChanges = true;
01782 }
01783 
01784 void KOAgendaView::calendarIncidenceAdded(Incidence * incidence)
01785 {
01786   Q_UNUSED( incidence );
01787   mPendingChanges = true;
01788 }
01789 
01790 void KOAgendaView::calendarIncidenceChanged(Incidence * incidence)
01791 {
01792   Q_UNUSED( incidence );
01793   mPendingChanges = true;
01794 }
01795 
01796 void KOAgendaView::calendarIncidenceDeleted(Incidence * incidence)
01797 {
01798   Q_UNUSED( incidence );
01799   mPendingChanges = true;
01800 }