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 
00976   QTime startTime(0,0,0), endTime(0,0,0);
00977   if ( incidence->doesFloat() ) {
00978     daysLength = item->cellWidth() - 1;
00979   } else {
00980     startTime = mAgenda->gyToTime( item->cellYTop() );
00981     if ( item->lastMultiItem() ) {
00982       endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 );
00983       daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft();
00984     } else {
00985       endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
00986     }
00987   }
00988 
00989 //  kdDebug(5850) << "KOAgendaView::updateEventDates(): now setting dates" << endl;
00990   // FIXME: use a visitor here
00991   if ( incidence->type() == "Event" ) {
00992     startDt = incidence->dtStart();
00993     startDt = startDt.addDays( daysOffset );
00994     startDt.setTime( startTime );
00995     endDt = startDt.addDays( daysLength );
00996     endDt.setTime( endTime );
00997     Event*ev = static_cast<Event*>(incidence);
00998     if( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) {
00999       // No change
01000       return;
01001     }
01002     incidence->setDtStart( startDt );
01003     ev->setDtEnd( endDt );
01004   } else if ( incidence->type() == "Todo" ) {
01005     Todo *td = static_cast<Todo*>(incidence);
01006     startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
01007     startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) );
01008     startDt.setTime( startTime );
01009     endDt.setDate( thisDate );
01010     endDt.setTime( endTime );
01011 
01012     if( td->dtDue() == endDt ) {
01013       // No change
01014       return;
01015     }
01016   }
01017   // FIXME: Adjusting the recurrence should really go to CalendarView so this
01018   // functionality will also be available in other views!
01019   // TODO_Recurrence: This does not belong here, and I'm not really sure
01020   // how it's supposed to work anyway.
01021   Recurrence *recur = incidence->recurrence();
01022 /*  if ( recur->doesRecur() && daysOffset != 0 ) {
01023     switch ( recur->recurrenceType() ) {
01024       case Recurrence::rYearlyPos: {
01025         int freq = recur->frequency();
01026         int duration = recur->duration();
01027         QDate endDt( recur->endDate() );
01028         bool negative = false;
01029 
01030         QPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() );
01031         if ( monthPos.first() ) {
01032           negative = monthPos.first()->negative;
01033         }
01034         QBitArray days( 7 );
01035         int pos = 0;
01036         days.fill( false );
01037         days.setBit( thisDate.dayOfWeek() - 1 );
01038         if ( negative ) {
01039           pos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
01040         } else {
01041           pos =  ( thisDate.day()-1 ) / 7 + 1;
01042         }
01043         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01044         recur->unsetRecurs();
01045         if ( duration != 0 ) {
01046           recur->setYearly( Recurrence::rYearlyPos, freq, duration );
01047         } else {
01048           recur->setYearly( Recurrence::rYearlyPos, freq, endDt );
01049         }
01050         recur->addYearlyMonthPos( pos, days );
01051         recur->addYearlyNum( thisDate.month() );
01052 
01053         break; }
01054         case Recurrence::rYearlyDay: {
01055           int freq = recur->frequency();
01056           int duration = recur->duration();
01057           QDate endDt( recur->endDate() );
01058         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01059           recur->unsetRecurs();
01060           if ( duration == 0 ) { // end by date
01061             recur->setYearly( Recurrence::rYearlyDay, freq, endDt );
01062           } else {
01063             recur->setYearly( Recurrence::rYearlyDay, freq, duration );
01064           }
01065           recur->addYearlyNum( thisDate.dayOfYear() );
01066           break; }
01067           case Recurrence::rYearlyMonth: {
01068             int freq = recur->frequency();
01069             int duration = recur->duration();
01070             QDate endDt( recur->endDate() );
01071         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01072             recur->unsetRecurs();
01073             if ( duration != 0 ) {
01074               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration );
01075             } else {
01076               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt );
01077             }
01078             recur->addYearlyNum( thisDate.month() );
01079             break; }
01080             case Recurrence::rMonthlyPos: {
01081               int freq = recur->frequency();
01082               int duration = recur->duration();
01083               QDate endDt( recur->endDate() );
01084               QPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() );
01085               if ( !monthPos.isEmpty() ) {
01086           // FIXME: How shall I adapt the day x of week Y if we move the date across month borders???
01087           // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
01088           // That's fine for korganizer, but might mess up other organizers.
01089                 QBitArray rDays( 7 );
01090                 rDays = monthPos.first()->rDays;
01091                 bool negative = monthPos.first()->negative;
01092                 int newPos;
01093                 rDays.fill( false );
01094                 rDays.setBit( thisDate.dayOfWeek() - 1 );
01095                 if ( negative ) {
01096                   newPos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
01097                 } else {
01098                   newPos =  ( thisDate.day()-1 ) / 7 + 1;
01099                 }
01100 
01101           // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01102                 recur->unsetRecurs();
01103                 if ( duration == 0 ) { // end by date
01104                   recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt );
01105                 } else {
01106                   recur->setMonthly( Recurrence::rMonthlyPos, freq, duration );
01107                 }
01108                 recur->addMonthlyPos( newPos, rDays );
01109               }
01110               break;}
01111               case Recurrence::rMonthlyDay: {
01112                 int freq = recur->frequency();
01113                 int duration = recur->duration();
01114                 QDate endDt( recur->endDate() );
01115                 QPtrList<int> monthDays( recur->monthDays() );
01116         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01117                 recur->unsetRecurs();
01118                 if ( duration == 0 ) { // end by date
01119                   recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt );
01120                 } else {
01121                   recur->setMonthly( Recurrence::rMonthlyDay, freq, duration );
01122                 }
01123         // FIXME: How shall I adapt the n-th day if we move the date across month borders???
01124         // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
01125         // That's fine for korganizer, but might mess up other organizers.
01126                 recur->addMonthlyDay( thisDate.day() );
01127 
01128                 break;}
01129                 case Recurrence::rWeekly: {
01130                   QBitArray days(7), oldDays( recur->days() );
01131                   int offset = daysOffset % 7;
01132                   if ( offset<0 ) offset = (offset+7) % 7;
01133         // rotate the days
01134                   for (int d=0; d<7; d++ ) {
01135                     days.setBit( (d+offset) % 7, oldDays.at(d) );
01136                   }
01137                   if ( recur->duration() == 0 ) { // end by date
01138                     recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() );
01139                   } else { // duration or no end
01140                     recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() );
01141                   }
01142                   break;}
01143       // nothing to be done for the following:
01144       case Recurrence::rDaily:
01145       case Recurrence::rHourly:
01146       case Recurrence::rMinutely:
01147       case Recurrence::rNone:
01148       default:
01149         break;
01150     }
01151     if ( recur->duration()==0 ) { // end by date
01152       recur->setEndDate( recur->endDate().addDays( daysOffset ) );
01153     }
01154     KMessageBox::information( this, i18n("A recurring calendar item was moved "
01155                               "to a different day. The recurrence settings "
01156                               "have been updated with that move. Please check "
01157                               "them in the editor."),
01158                               i18n("Recurrence Moved"),
01159                               "RecurrenceMoveInAgendaWarning" );
01160   }*/
01161 
01162   // FIXME: use a visitor here
01163   if ( incidence->type() == "Event" ) {
01164     incidence->setDtStart( startDt );
01165     (static_cast<Event*>( incidence ) )->setDtEnd( endDt );
01166   } else if ( incidence->type() == "Todo" ) {
01167     Todo *td = static_cast<Todo*>( incidence );
01168     if ( td->hasStartDate() )
01169       td->setDtStart( startDt );
01170     td->setDtDue( endDt );
01171   }
01172 
01173   item->setItemDate( startDt.date() );
01174 
01175   KOIncidenceToolTip::remove( item );
01176   KOIncidenceToolTip::add( item, incidence, KOAgendaItem::toolTipGroup() );
01177 
01178   // don't update the agenda as the item already has the correct coordinates.
01179   // an update would delete the current item and recreate it, but we are still
01180   // using a pointer to that item! => CRASH
01181   enableAgendaUpdate( false );
01182   // We need to do this in a timer to make sure we are not deleting the item
01183   // we are currently working on, which would lead to crashes
01184   // Only the actually moved agenda item is already at the correct position and mustn't be
01185   // recreated. All others have to!!!
01186   if ( incidence->doesRecur() ) {
01187     mUpdateItem = incidence;
01188     QTimer::singleShot( 0, this, SLOT( doUpdateItem() ) );
01189   }
01190 
01191     enableAgendaUpdate( true );
01192 
01193 //  kdDebug(5850) << "KOAgendaView::updateEventDates() done " << endl;
01194 }
01195 
01196 void KOAgendaView::doUpdateItem()
01197 {
01198   if ( mUpdateItem ) {
01199     changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED );
01200     mUpdateItem = 0;
01201   }
01202 }
01203 
01204 
01205 
01206 void KOAgendaView::showDates( const QDate &start, const QDate &end )
01207 {
01208 //  kdDebug(5850) << "KOAgendaView::selectDates" << endl;
01209 
01210   mSelectedDates.clear();
01211 
01212   QDate d = start;
01213   while (d <= end) {
01214     mSelectedDates.append(d);
01215     d = d.addDays( 1 );
01216   }
01217 
01218   // and update the view
01219   fillAgenda();
01220 }
01221 
01222 
01223 void KOAgendaView::showIncidences( const Incidence::List & )
01224 {
01225   kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl;
01226 }
01227 
01228 void KOAgendaView::insertIncidence( Incidence *incidence, const QDate &curDate,
01229                                     int curCol )
01230 {
01231   // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's
01232   Event *event = dynamic_cast<Event *>( incidence );
01233   Todo  *todo  = dynamic_cast<Todo  *>( incidence );
01234 
01235   if ( curCol < 0 ) {
01236     curCol = mSelectedDates.findIndex( curDate );
01237   }
01238   // The date for the event is not displayed, just ignore it
01239   if ( curCol < 0 || curCol > int( mSelectedDates.size() ) )
01240     return;
01241 
01242   int beginX;
01243   int endX;
01244   if ( event ) {
01245     beginX = curDate.daysTo( incidence->dtStart().date() ) + curCol;
01246     endX = curDate.daysTo( event->dateEnd() ) + curCol;
01247   } else if ( todo ) {
01248     if ( ! todo->hasDueDate() ) return;  // todo shall not be displayed if it has no date
01249     beginX = curDate.daysTo( todo->dtDue().date() ) + curCol;
01250     endX = beginX;
01251   } else {
01252     return;
01253   }
01254 
01255   if ( todo && todo->isOverdue() ) {
01256     mAllDayAgenda->insertAllDayItem( incidence, curDate, curCol, curCol );
01257   } else if ( incidence->doesFloat() ) {
01258 // FIXME: This breaks with recurring multi-day events!
01259     if ( incidence->recurrence()->doesRecur() ) {
01260       mAllDayAgenda->insertAllDayItem( incidence, curDate, curCol, curCol );
01261     } else {
01262       // Insert multi-day events only on the first day, otherwise it will
01263       // appear multiple times
01264       if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) {
01265         mAllDayAgenda->insertAllDayItem( incidence, curDate, beginX, endX );
01266       }
01267     }
01268   } else if ( event && event->isMultiDay() ) {
01269     int startY = mAgenda->timeToY( event->dtStart().time() );
01270     QTime endtime( event->dtEnd().time() );
01271     if ( endtime == QTime( 0, 0, 0 ) ) endtime = QTime( 23, 59, 59 );
01272     int endY = mAgenda->timeToY( endtime ) - 1;
01273     if ( (beginX <= 0 && curCol == 0) || beginX == curCol ) {
01274       mAgenda->insertMultiItem( event, curDate, beginX, endX, startY, endY );
01275     }
01276     if ( beginX == curCol ) {
01277       mMaxY[curCol] = mAgenda->timeToY( QTime(23,59) );
01278       if ( startY < mMinY[curCol] ) mMinY[curCol] = startY;
01279     } else if ( endX == curCol ) {
01280       mMinY[curCol] = mAgenda->timeToY( QTime(0,0) );
01281       if ( endY > mMaxY[curCol] ) mMaxY[curCol] = endY;
01282     } else {
01283       mMinY[curCol] = mAgenda->timeToY( QTime(0,0) );
01284       mMaxY[curCol] = mAgenda->timeToY( QTime(23,59) );
01285     }
01286   } else {
01287     int startY = 0, endY = 0;
01288     if ( event ) {
01289       startY = mAgenda->timeToY( incidence->dtStart().time() );
01290       QTime endtime( event->dtEnd().time() );
01291       if ( endtime == QTime( 0, 0, 0 ) ) endtime = QTime( 23, 59, 59 );
01292       endY = mAgenda->timeToY( endtime ) - 1;
01293     }
01294     if ( todo ) {
01295       QTime t = todo->dtDue().time();
01296       endY = mAgenda->timeToY( t ) - 1;
01297       startY = mAgenda->timeToY( t.addSecs( -1800 ) );
01298     }
01299     if ( endY < startY ) endY = startY;
01300     mAgenda->insertItem( incidence, curDate, curCol, startY, endY );
01301     if ( startY < mMinY[curCol] ) mMinY[curCol] = startY;
01302     if ( endY > mMaxY[curCol] ) mMaxY[curCol] = endY;
01303   }
01304 }
01305 
01306 void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence )
01307 {
01308   Todo *todo = dynamic_cast<Todo *>(incidence);
01309   CalFilter *filter = calendar()->filter();
01310   if ( filter && !filter->filterIncidence( incidence ) ||
01311      ( todo && !KOPrefs::instance()->showAllDayTodo() ) )
01312     return;
01313 
01314   QDate f = mSelectedDates.first();
01315   QDate l = mSelectedDates.last();
01316   QDate startDt = incidence->dtStart().date();
01317 
01318   if ( incidence->doesRecur() ) {
01319     DateList::ConstIterator dit;
01320     QDate curDate;
01321     for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
01322       curDate = *dit;
01323 // FIXME: This breaks with recurring multi-day events!
01324       if ( incidence->recursOn( curDate ) ) {
01325         insertIncidence( incidence, curDate );
01326       }
01327     }
01328     return;
01329   }
01330 
01331   QDate endDt;
01332   if ( incidence->type() == "Event" )
01333     endDt = (static_cast<Event *>(incidence))->dateEnd();
01334   if ( todo ) {
01335     endDt = todo->isOverdue() ? QDate::currentDate()
01336                               : todo->dtDue().date();
01337 
01338     if ( endDt >= f && endDt <= l ) {
01339       insertIncidence( incidence, endDt );
01340       return;
01341     }
01342   }
01343 
01344   if ( startDt >= f && startDt <= l ) {
01345     insertIncidence( incidence, startDt );
01346   }
01347 }
01348 
01349 void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode )
01350 {
01351   switch ( mode ) {
01352     case KOGlobals::INCIDENCEADDED: {
01353         //  Add an event. No need to recreate the whole view!
01354         // recreating everything even causes troubles: dropping to the day matrix
01355         // recreates the agenda items, but the evaluation is still in an agendaItems' code,
01356         // which was deleted in the mean time. Thus KOrg crashes...
01357       if ( mAllowAgendaUpdate )
01358         changeIncidenceDisplayAdded( incidence );
01359       break;
01360     }
01361     case KOGlobals::INCIDENCEEDITED: {
01362       if ( !mAllowAgendaUpdate ) {
01363         updateEventIndicators();
01364       } else {
01365         removeIncidence( incidence );
01366         updateEventIndicators();
01367         changeIncidenceDisplayAdded( incidence );
01368       }
01369       break;
01370     }
01371     case KOGlobals::INCIDENCEDELETED: {
01372       mAgenda->removeIncidence( incidence );
01373       mAllDayAgenda->removeIncidence( incidence );
01374       updateEventIndicators();
01375       break;
01376     }
01377     default:
01378       updateView();
01379   }
01380 }
01381 
01382 void KOAgendaView::fillAgenda( const QDate & )
01383 {
01384   fillAgenda();
01385 }
01386 
01387 void KOAgendaView::fillAgenda()
01388 {
01389   /* Remember the uids of the selected items. In case one of the
01390    * items was deleted and re-added, we want to reselect it. */
01391   const QString &selectedAgendaUid = mAgenda->lastSelectedUid();
01392   const QString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
01393 
01394   enableAgendaUpdate( true );
01395   clearView();
01396 
01397   mAllDayAgenda->changeColumns(mSelectedDates.count());
01398   mAgenda->changeColumns(mSelectedDates.count());
01399   mEventIndicatorTop->changeColumns(mSelectedDates.count());
01400   mEventIndicatorBottom->changeColumns(mSelectedDates.count());
01401 
01402   createDayLabels();
01403   setHolidayMasks();
01404 
01405   mMinY.resize(mSelectedDates.count());
01406   mMaxY.resize(mSelectedDates.count());
01407 
01408   Event::List dayEvents;
01409 
01410   // ToDo items shall be displayed for the day they are due, but only shown today if they are already overdue.
01411   // Therefore, get all of them.
01412   Todo::List todos  = calendar()->todos();
01413 
01414   mAgenda->setDateList(mSelectedDates);
01415 
01416   QDate today = QDate::currentDate();
01417 
01418   bool somethingReselected = false;
01419   DateList::ConstIterator dit;
01420   int curCol = 0;
01421   for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
01422     QDate currentDate = *dit;
01423 //    kdDebug(5850) << "KOAgendaView::fillAgenda(): " << currentDate.toString()
01424 //              << endl;
01425 
01426     dayEvents = calendar()->events(currentDate,
01427                                    EventSortStartDate,
01428                                    SortDirectionAscending);
01429 
01430     // Default values, which can never be reached
01431     mMinY[curCol] = mAgenda->timeToY(QTime(23,59)) + 1;
01432     mMaxY[curCol] = mAgenda->timeToY(QTime(0,0)) - 1;
01433 
01434     unsigned int numEvent;
01435     for(numEvent=0;numEvent<dayEvents.count();++numEvent) {
01436       Event *event = *dayEvents.at(numEvent);
01437 //      kdDebug(5850) << " Event: " << event->summary() << endl;
01438       insertIncidence( event, currentDate, curCol );
01439       if( event->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
01440         mAgenda->selectItemByUID( event->uid() );
01441         somethingReselected = true;
01442       }
01443       if( event->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
01444         mAllDayAgenda->selectItemByUID( event->uid() );
01445         somethingReselected = true;
01446       }
01447 
01448     }
01449 //    if (numEvent == 0) kdDebug(5850) << " No events" << endl;
01450 
01451 
01452     // ---------- [display Todos --------------
01453     if ( KOPrefs::instance()->showAllDayTodo() ) {
01454       unsigned int numTodo;
01455       for (numTodo = 0; numTodo < todos.count(); ++numTodo) {
01456         Todo *todo = *todos.at(numTodo);
01457 
01458         if ( ! todo->hasDueDate() ) continue;  // todo shall not be displayed if it has no date
01459 
01460         // ToDo items shall be displayed for the day they are due, but only showed today if they are already overdue.
01461         // Already completed items can be displayed on their original due date
01462         bool overdue = todo->isOverdue();
01463 
01464         if ( (( todo->dtDue().date() == currentDate) && !overdue) ||
01465              (( currentDate == today) && overdue) ||
01466              ( todo->recursOn( currentDate ) ) ) {
01467           if ( todo->doesFloat() || overdue ) {  // Todo has no due-time set or is already overdue
01468             //kdDebug(5850) << "todo without time:" << todo->dtDueDateStr() << ";" << todo->summary() << endl;
01469 
01470             mAllDayAgenda->insertAllDayItem(todo, currentDate, curCol, curCol);
01471           } else {
01472             //kdDebug(5850) << "todo with time:" << todo->dtDueStr() << ";" << todo->summary() << endl;
01473 
01474             int endY = mAgenda->timeToY(todo->dtDue().time()) - 1;
01475             int startY = endY - 1;
01476 
01477             mAgenda->insertItem(todo,currentDate,curCol,startY,endY);
01478 
01479             if (startY < mMinY[curCol]) mMinY[curCol] = startY;
01480             if (endY > mMaxY[curCol]) mMaxY[curCol] = endY;
01481           }
01482         }
01483       }
01484     }
01485     // ---------- display Todos] --------------
01486 
01487     ++curCol;
01488   }
01489 
01490   mAgenda->checkScrollBoundaries();
01491   updateEventIndicators();
01492 
01493 //  mAgenda->viewport()->update();
01494 //  mAllDayAgenda->viewport()->update();
01495 
01496 // make invalid
01497   deleteSelectedDateTime();
01498 
01499   if( !somethingReselected ) {
01500     emit incidenceSelected( 0 );
01501   }
01502 
01503 //  kdDebug(5850) << "Fill Agenda done" << endl;
01504 }
01505 
01506 void KOAgendaView::clearView()
01507 {
01508 //  kdDebug(5850) << "ClearView" << endl;
01509   mAllDayAgenda->clear();
01510   mAgenda->clear();
01511 }
01512 
01513 CalPrinter::PrintType KOAgendaView::printType()
01514 {
01515   if ( currentDateCount() == 1 ) return CalPrinter::Day;
01516   else return CalPrinter::Week;
01517 }
01518 
01519 void KOAgendaView::updateEventIndicatorTop( int newY )
01520 {
01521   uint i;
01522   for( i = 0; i < mMinY.size(); ++i ) {
01523     mEventIndicatorTop->enableColumn( i, newY >= mMinY[i] );
01524   }
01525   mEventIndicatorTop->update();
01526 }
01527 
01528 void KOAgendaView::updateEventIndicatorBottom( int newY )
01529 {
01530   uint i;
01531   for( i = 0; i < mMaxY.size(); ++i ) {
01532     mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
01533   }
01534   mEventIndicatorBottom->update();
01535 }
01536 
01537 void KOAgendaView::slotTodoDropped( Todo *todo, const QPoint &gpos, bool allDay )
01538 {
01539   if ( gpos.x()<0 || gpos.y()<0 ) return;
01540   QDate day = mSelectedDates[gpos.x()];
01541   QTime time = mAgenda->gyToTime( gpos.y() );
01542   QDateTime newTime( day, time );
01543 
01544   if ( todo ) {
01545     Todo *existingTodo = calendar()->todo( todo->uid() );
01546     if ( existingTodo ) {
01547       kdDebug(5850) << "Drop existing Todo" << endl;
01548       Todo *oldTodo = existingTodo->clone();
01549       if ( mChanger && mChanger->beginChange( existingTodo ) ) {
01550         existingTodo->setDtDue( newTime );
01551         existingTodo->setFloats( allDay );
01552         existingTodo->setHasDueDate( true );
01553         mChanger->changeIncidence( oldTodo, existingTodo );
01554         mChanger->endChange( existingTodo );
01555       } else {
01556         KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
01557                             "because it cannot be locked.") );
01558       }
01559       delete oldTodo;
01560     } else {
01561       kdDebug(5850) << "Drop new Todo" << endl;
01562       todo->setDtDue( newTime );
01563       todo->setFloats( allDay );
01564       todo->setHasDueDate( true );
01565       if ( !mChanger->addIncidence( todo ) ) {
01566         KODialogManager::errorSaveIncidence( this, todo );
01567       }
01568     }
01569   }
01570 }
01571 
01572 void KOAgendaView::startDrag( Incidence *incidence )
01573 {
01574 #ifndef KORG_NODND
01575   DndFactory factory( calendar() );
01576   ICalDrag *vd = factory.createDrag( incidence, this );
01577   if ( vd->drag() ) {
01578     kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl;
01579   }
01580 #endif
01581 }
01582 
01583 void KOAgendaView::readSettings()
01584 {
01585   readSettings(KOGlobals::self()->config());
01586 }
01587 
01588 void KOAgendaView::readSettings(KConfig *config)
01589 {
01590 //  kdDebug(5850) << "KOAgendaView::readSettings()" << endl;
01591 
01592   config->setGroup("Views");
01593 
01594 #ifndef KORG_NOSPLITTER
01595   QValueList<int> sizes = config->readIntListEntry("Separator AgendaView");
01596   if (sizes.count() == 2) {
01597     mSplitterAgenda->setSizes(sizes);
01598   }
01599 #endif
01600 
01601   updateConfig();
01602 }
01603 
01604 void KOAgendaView::writeSettings(KConfig *config)
01605 {
01606 //  kdDebug(5850) << "KOAgendaView::writeSettings()" << endl;
01607 
01608   config->setGroup("Views");
01609 
01610 #ifndef KORG_NOSPLITTER
01611   QValueList<int> list = mSplitterAgenda->sizes();
01612   config->writeEntry("Separator AgendaView",list);
01613 #endif
01614 }
01615 
01616 void KOAgendaView::setHolidayMasks()
01617 {
01618   mHolidayMask.resize( mSelectedDates.count() + 1 );
01619 
01620   for( uint i = 0; i < mSelectedDates.count(); ++i ) {
01621     mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
01622   }
01623 
01624   // Store the information about the day before the visible area (needed for
01625   // overnight working hours) in the last bit of the mask:
01626   bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
01627   mHolidayMask[ mSelectedDates.count() ] = showDay;
01628 
01629   mAgenda->setHolidayMask( &mHolidayMask );
01630   mAllDayAgenda->setHolidayMask( &mHolidayMask );
01631 }
01632 
01633 void KOAgendaView::setContentsPos( int y )
01634 {
01635   mAgenda->setContentsPos( 0, y );
01636 }
01637 
01638 void KOAgendaView::setExpandedButton( bool expanded )
01639 {
01640   if ( !mExpandButton ) return;
01641 
01642   if ( expanded ) {
01643     mExpandButton->setPixmap( mExpandedPixmap );
01644   } else {
01645     mExpandButton->setPixmap( mNotExpandedPixmap );
01646   }
01647 }
01648 
01649 void KOAgendaView::clearSelection()
01650 {
01651   mAgenda->deselectItem();
01652   mAllDayAgenda->deselectItem();
01653 }
01654 
01655 void KOAgendaView::newTimeSpanSelectedAllDay( const QPoint &start, const QPoint &end )
01656 {
01657   newTimeSpanSelected( start, end );
01658   mTimeSpanInAllDay = true;
01659 }
01660 
01661 void KOAgendaView::newTimeSpanSelected( const QPoint &start, const QPoint &end )
01662 {
01663   if (!mSelectedDates.count()) return;
01664 
01665   mTimeSpanInAllDay = false;
01666 
01667   QDate dayStart = mSelectedDates[start.x()];
01668   QDate dayEnd = mSelectedDates[end.x()];
01669 
01670   QTime timeStart = mAgenda->gyToTime(start.y());
01671   QTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
01672 
01673   QDateTime dtStart(dayStart,timeStart);
01674   QDateTime dtEnd(dayEnd,timeEnd);
01675 
01676   mTimeSpanBegin = dtStart;
01677   mTimeSpanEnd = dtEnd;
01678 }
01679 
01680 void KOAgendaView::deleteSelectedDateTime()
01681 {
01682   mTimeSpanBegin.setDate(QDate());
01683   mTimeSpanEnd.setDate(QDate());
01684   mTimeSpanInAllDay = false;
01685 }
01686 
01687 void KOAgendaView::setTypeAheadReceiver( QObject *o )
01688 {
01689   mAgenda->setTypeAheadReceiver( o );
01690   mAllDayAgenda->setTypeAheadReceiver( o );
01691 }
01692 
01693 void KOAgendaView::finishTypeAhead()
01694 {
01695   mAgenda->finishTypeAhead();
01696   mAllDayAgenda->finishTypeAhead();
01697 }
01698 
01699 void KOAgendaView::removeIncidence( Incidence *incidence )
01700 {
01701   mAgenda->removeIncidence( incidence );
01702   mAllDayAgenda->removeIncidence( incidence );
01703 }
01704 
01705 void KOAgendaView::updateEventIndicators()
01706 {
01707   mMinY = mAgenda->minContentsY();
01708   mMaxY = mAgenda->maxContentsY();
01709 
01710   mAgenda->checkScrollBoundaries();
01711   updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
01712   updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
01713 }
01714 
01715 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
01716 {
01717   mChanger = changer;
01718   mAgenda->setIncidenceChanger( changer );
01719   mAllDayAgenda->setIncidenceChanger( changer );
01720 }
KDE Home | KDE Accessibility Home | Description of Access Keys