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