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(int 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 }
00255 
00256 
00258 void TimeLabels::paintEvent(QPaintEvent*)
00259 {
00260 //  kdDebug(5850) << "paintevent..." << endl;
00261   // this is another hack!
00262 //  QPainter painter(this);
00263   //QString c
00264   repaintContents(contentsX(), contentsY(), visibleWidth(), visibleHeight());
00265 }
00266 
00268 
00269 EventIndicator::EventIndicator(Location loc,QWidget *parent,const char *name)
00270   : QFrame(parent,name)
00271 {
00272   mColumns = 1;
00273   mTopBox = 0;
00274   mLocation = loc;
00275   mTopLayout = 0;
00276 
00277   if (mLocation == Top) mPixmap = KOGlobals::self()->smallIcon("upindicator");
00278   else mPixmap = KOGlobals::self()->smallIcon("downindicator");
00279 
00280   setMinimumHeight(mPixmap.height());
00281 }
00282 
00283 EventIndicator::~EventIndicator()
00284 {
00285 }
00286 
00287 void EventIndicator::drawContents(QPainter *p)
00288 {
00289 //  kdDebug(5850) << "======== top: " << contentsRect().top() << "  bottom "
00290 //         << contentsRect().bottom() << "  left " << contentsRect().left()
00291 //         << "  right " << contentsRect().right() << endl;
00292 
00293   int i;
00294   for(i=0;i<mColumns;++i) {
00295     if (mEnabled[i]) {
00296       int cellWidth = contentsRect().right()/mColumns;
00297       int xOffset = KOGlobals::self()->reverseLayout() ?
00298                (mColumns - 1 - i)*cellWidth + cellWidth/2 -mPixmap.width()/2 :
00299                i*cellWidth + cellWidth/2 -mPixmap.width()/2;
00300       p->drawPixmap(QPoint(xOffset,0),mPixmap);
00301     }
00302   }
00303 }
00304 
00305 void EventIndicator::changeColumns(int columns)
00306 {
00307   mColumns = columns;
00308   mEnabled.resize(mColumns);
00309 
00310   update();
00311 }
00312 
00313 void EventIndicator::enableColumn(int column, bool enable)
00314 {
00315   mEnabled[column] = enable;
00316 }
00317 
00318 
00319 #include <libkcal/incidence.h>
00320 
00324 
00325 
00326 KOAlternateLabel::KOAlternateLabel(const QString &shortlabel, const QString &longlabel,
00327     const QString &extensivelabel, QWidget *parent, const char *name )
00328   : QLabel(parent, name), mTextTypeFixed(false), mShortText(shortlabel),
00329     mLongText(longlabel), mExtensiveText(extensivelabel)
00330 {
00331   setSizePolicy(QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ));
00332   if (mExtensiveText.isEmpty()) mExtensiveText = mLongText;
00333   squeezeTextToLabel();
00334 }
00335 
00336 KOAlternateLabel::~KOAlternateLabel()
00337 {
00338 }
00339 
00340 void KOAlternateLabel::useShortText()
00341 {
00342   mTextTypeFixed = true;
00343   QLabel::setText( mShortText );
00344   QToolTip::remove( this );
00345   QToolTip::add( this, mExtensiveText );
00346 }
00347 
00348 void KOAlternateLabel::useLongText()
00349 {
00350   mTextTypeFixed = true;
00351   QLabel::setText( mLongText );
00352   QToolTip::remove( this );
00353   QToolTip::add( this, mExtensiveText );
00354 }
00355 
00356 void KOAlternateLabel::useExtensiveText()
00357 {
00358   mTextTypeFixed = true;
00359   QLabel::setText( mExtensiveText );
00360   QToolTip::remove( this );
00361   QToolTip::hide();
00362 }
00363 
00364 void KOAlternateLabel::useDefaultText()
00365 {
00366   mTextTypeFixed = false;
00367   squeezeTextToLabel();
00368 }
00369 
00370 void KOAlternateLabel::squeezeTextToLabel()
00371 {
00372   if (mTextTypeFixed) return;
00373 
00374   QFontMetrics fm(fontMetrics());
00375   int labelWidth = size().width();
00376   int textWidth = fm.width(mLongText);
00377   int longTextWidth = fm.width(mExtensiveText);
00378   if (longTextWidth <= labelWidth) {
00379     QLabel::setText( mExtensiveText );
00380     QToolTip::remove( this );
00381     QToolTip::hide();
00382   } else if (textWidth <= labelWidth) {
00383     QLabel::setText( mLongText );
00384     QToolTip::remove( this );
00385     QToolTip::add( this, mExtensiveText );
00386   } else {
00387     QLabel::setText( mShortText );
00388     QToolTip::remove( this );
00389     QToolTip::add( this, mExtensiveText );
00390   }
00391 }
00392 
00393 void KOAlternateLabel::resizeEvent( QResizeEvent * )
00394 {
00395   squeezeTextToLabel();
00396 }
00397 
00398 QSize KOAlternateLabel::minimumSizeHint() const
00399 {
00400   QSize sh = QLabel::minimumSizeHint();
00401   sh.setWidth(-1);
00402   return sh;
00403 }
00404 
00405 void KOAlternateLabel::setText( const QString &text ) {
00406   mLongText = text;
00407   squeezeTextToLabel();
00408 }
00409 
00410 
00414 
00415 KOAgendaView::KOAgendaView(Calendar *cal,QWidget *parent,const char *name) :
00416   KOEventView (cal,parent,name), mExpandButton( 0 ), mAllowAgendaUpdate( true ),
00417   mUpdateItem( 0 )
00418 {
00419   mSelectedDates.append(QDate::currentDate());
00420 
00421   mLayoutDayLabels = 0;
00422   mDayLabelsFrame = 0;
00423   mDayLabels = 0;
00424 
00425   bool isRTL = KOGlobals::self()->reverseLayout();
00426 
00427   if ( KOPrefs::instance()->compactDialogs() ) {
00428     if ( KOPrefs::instance()->mVerticalScreen ) {
00429       mExpandedPixmap = KOGlobals::self()->smallIcon( "1downarrow" );
00430       mNotExpandedPixmap = KOGlobals::self()->smallIcon( "1uparrow" );
00431     } else {
00432       mExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1leftarrow" : "1rightarrow" );
00433       mNotExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1rightarrow" : "1leftarrow" );
00434     }
00435   }
00436 
00437   QBoxLayout *topLayout = new QVBoxLayout(this);
00438 
00439   // Create day name labels for agenda columns
00440   mDayLabelsFrame = new QHBox(this);
00441   topLayout->addWidget(mDayLabelsFrame);
00442 
00443   // Create agenda splitter
00444 #ifndef KORG_NOSPLITTER
00445   mSplitterAgenda = new QSplitter(Vertical,this);
00446   topLayout->addWidget(mSplitterAgenda);
00447 
00448 #if KDE_IS_VERSION( 3, 1, 93 )
00449   mSplitterAgenda->setOpaqueResize( KGlobalSettings::opaqueResize() );
00450 #else
00451   mSplitterAgenda->setOpaqueResize();
00452 #endif
00453 
00454   mAllDayFrame = new QHBox(mSplitterAgenda);
00455 
00456   QWidget *agendaFrame = new QWidget(mSplitterAgenda);
00457 #else
00458   QVBox *mainBox = new QVBox( this );
00459   topLayout->addWidget( mainBox );
00460 
00461   mAllDayFrame = new QHBox(mainBox);
00462 
00463   QWidget *agendaFrame = new QWidget(mainBox);
00464 #endif
00465 
00466   // Create all-day agenda widget
00467   mDummyAllDayLeft = new QVBox( mAllDayFrame );
00468 
00469   if ( KOPrefs::instance()->compactDialogs() ) {
00470     mExpandButton = new QPushButton(mDummyAllDayLeft);
00471     mExpandButton->setPixmap( mNotExpandedPixmap );
00472     mExpandButton->setSizePolicy( QSizePolicy( QSizePolicy::Fixed,
00473                                   QSizePolicy::Fixed ) );
00474     connect( mExpandButton, SIGNAL( clicked() ), SIGNAL( toggleExpand() ) );
00475   } else {
00476     QLabel *label = new QLabel( i18n("All Day"), mDummyAllDayLeft );
00477     label->setAlignment( Qt::AlignRight | Qt::AlignVCenter | Qt::WordBreak );
00478   }
00479 
00480   mAllDayAgenda = new KOAgenda(1,mAllDayFrame);
00481   QWidget *dummyAllDayRight = new QWidget(mAllDayFrame);
00482 
00483   // Create agenda frame
00484   QGridLayout *agendaLayout = new QGridLayout(agendaFrame,3,3);
00485 //  QHBox *agendaFrame = new QHBox(splitterAgenda);
00486 
00487   // create event indicator bars
00488   mEventIndicatorTop = new EventIndicator(EventIndicator::Top,agendaFrame);
00489   agendaLayout->addWidget(mEventIndicatorTop,0,1);
00490   mEventIndicatorBottom = new EventIndicator(EventIndicator::Bottom,
00491                                              agendaFrame);
00492   agendaLayout->addWidget(mEventIndicatorBottom,2,1);
00493   QWidget *dummyAgendaRight = new QWidget(agendaFrame);
00494   agendaLayout->addWidget(dummyAgendaRight,0,2);
00495 
00496   // Create time labels
00497   mTimeLabels = new TimeLabels(24,agendaFrame);
00498   agendaLayout->addWidget(mTimeLabels,1,0);
00499 
00500   // Create agenda
00501   mAgenda = new KOAgenda(1,96,KOPrefs::instance()->mHourSize,agendaFrame);
00502   agendaLayout->addMultiCellWidget(mAgenda,1,1,1,2);
00503   agendaLayout->setColStretch(1,1);
00504 
00505   // Create event context menu for agenda
00506   mAgendaPopup = eventPopup();
00507 
00508   // Create event context menu for all day agenda
00509   mAllDayAgendaPopup = eventPopup();
00510 
00511   // make connections between dependent widgets
00512   mTimeLabels->setAgenda(mAgenda);
00513 
00514   // Update widgets to reflect user preferences
00515 //  updateConfig();
00516 
00517   createDayLabels();
00518 
00519   // these blank widgets make the All Day Event box line up with the agenda
00520   dummyAllDayRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
00521   dummyAgendaRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
00522 
00523   updateTimeBarWidth();
00524 
00525   // Scrolling
00526   connect(mAgenda->verticalScrollBar(),SIGNAL(valueChanged(int)),
00527           mTimeLabels, SLOT(positionChanged()));
00528 
00529   connect( mAgenda,
00530     SIGNAL( zoomView( const int, const QPoint & ,const Qt::Orientation ) ),
00531     SLOT( zoomView( const int, const QPoint &, const Qt::Orientation ) ) );
00532 
00533   connect(mTimeLabels->verticalScrollBar(),SIGNAL(valueChanged(int)),
00534           SLOT(setContentsPos(int)));
00535 
00536   // Create Events, depends on type of agenda
00537   connect( mAgenda, SIGNAL(newTimeSpanSignal(const QPoint &, const QPoint &)),
00538                     SLOT(newTimeSpanSelected(const QPoint &, const QPoint &)));
00539   connect( mAllDayAgenda, SIGNAL(newTimeSpanSignal(const QPoint &, const QPoint &)),
00540                           SLOT(newTimeSpanSelectedAllDay(const QPoint &, const QPoint &)));
00541 
00542   // event indicator update
00543   connect( mAgenda, SIGNAL(lowerYChanged(int)),
00544                     SLOT(updateEventIndicatorTop(int)));
00545   connect( mAgenda, SIGNAL(upperYChanged(int)),
00546                     SLOT(updateEventIndicatorBottom(int)));
00547 
00548   connectAgenda( mAgenda, mAgendaPopup, mAllDayAgenda );
00549   connectAgenda( mAllDayAgenda, mAllDayAgendaPopup, mAgenda);
00550 }
00551 
00552 
00553 KOAgendaView::~KOAgendaView()
00554 {
00555   delete mAgendaPopup;
00556   delete mAllDayAgendaPopup;
00557 }
00558 
00559 void KOAgendaView::connectAgenda( KOAgenda *agenda, QPopupMenu *popup,
00560                                   KOAgenda *otherAgenda )
00561 {
00562   connect( agenda, SIGNAL( showIncidencePopupSignal( Incidence *, const QDate & ) ),
00563            popup, SLOT( showIncidencePopup( Incidence *, const QDate & ) ) );
00564 
00565   connect( agenda, SIGNAL( showNewEventPopupSignal() ),
00566            SLOT( showNewEventPopup() ) );
00567 
00568   agenda->setCalendar( calendar() );
00569 
00570   // Create/Show/Edit/Delete Event
00571   connect( agenda, SIGNAL( newEventSignal() ), SIGNAL( newEventSignal() ) );
00572 
00573   connect( agenda, SIGNAL( newStartSelectSignal() ),
00574            otherAgenda, SLOT( clearSelection() ) );
00575 
00576   connect( agenda, SIGNAL( editIncidenceSignal( Incidence * ) ),
00577                    SIGNAL( editIncidenceSignal( Incidence * ) ) );
00578   connect( agenda, SIGNAL( showIncidenceSignal( Incidence * ) ),
00579                    SIGNAL( showIncidenceSignal( Incidence * ) ) );
00580   connect( agenda, SIGNAL( deleteIncidenceSignal( Incidence * ) ),
00581                    SIGNAL( deleteIncidenceSignal( Incidence * ) ) );
00582 
00583   connect( agenda, SIGNAL( startMultiModify( const QString & ) ),
00584                    SIGNAL( startMultiModify( const QString & ) ) );
00585   connect( agenda, SIGNAL( endMultiModify() ),
00586                    SIGNAL( endMultiModify() ) );
00587 
00588   connect( agenda, SIGNAL( itemModified( KOAgendaItem * ) ),
00589                    SLOT( updateEventDates( KOAgendaItem * ) ) );
00590   connect( agenda, SIGNAL( enableAgendaUpdate( bool ) ),
00591                    SLOT( enableAgendaUpdate( bool ) ) );
00592 
00593   // drag signals
00594   connect( agenda, SIGNAL( startDragSignal( Incidence * ) ),
00595            SLOT( startDrag( Incidence * ) ) );
00596 
00597   // synchronize selections
00598   connect( agenda, SIGNAL( incidenceSelected( Incidence * ) ),
00599            otherAgenda, SLOT( deselectItem() ) );
00600   connect( agenda, SIGNAL( incidenceSelected( Incidence * ) ),
00601            SIGNAL( incidenceSelected( Incidence * ) ) );
00602 
00603   // rescheduling of todos by d'n'd
00604   connect( agenda, SIGNAL( droppedToDo( Todo *, const QPoint &, bool ) ),
00605            SLOT( slotTodoDropped( Todo *, const QPoint &, bool ) ) );
00606 
00607 }
00608 
00609 void KOAgendaView::zoomInVertically( )
00610 {
00611   KOPrefs::instance()->mHourSize++;
00612   mAgenda->updateConfig();
00613   mAgenda->checkScrollBoundaries();
00614 
00615   mTimeLabels->updateConfig();
00616   mTimeLabels->positionChanged();
00617   mTimeLabels->repaint();
00618 
00619   updateView();
00620 }
00621 
00622 void KOAgendaView::zoomOutVertically( )
00623 {
00624 
00625   if ( KOPrefs::instance()->mHourSize > 4 ) {
00626 
00627     KOPrefs::instance()->mHourSize--;
00628     mAgenda->updateConfig();
00629     mAgenda->checkScrollBoundaries();
00630 
00631     mTimeLabels->updateConfig();
00632     mTimeLabels->positionChanged();
00633     mTimeLabels->repaint();
00634 
00635     updateView();
00636   }
00637 }
00638 
00639 void KOAgendaView::zoomInHorizontally( const QDate &date)
00640 {
00641   QDate begin;
00642   QDate newBegin;
00643   QDate dateToZoom = date;
00644   int ndays,count;
00645 
00646   begin = mSelectedDates.first();
00647   ndays = begin.daysTo( mSelectedDates.last() );
00648 
00649   // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it.
00650   if ( ! dateToZoom.isValid () )
00651     dateToZoom=mAgenda->selectedIncidenceDate();
00652 
00653   if( !dateToZoom.isValid() ) {
00654     if ( ndays > 1 ) {
00655       newBegin=begin.addDays(1);
00656       count = ndays-1;
00657       emit zoomViewHorizontally ( newBegin , count );
00658     }
00659   } else {
00660     if ( ndays <= 2 ) {
00661       newBegin = dateToZoom;
00662       count = 1;
00663     } else  {
00664       newBegin = dateToZoom.addDays( -ndays/2 +1  );
00665       count = ndays -1 ;
00666     }
00667     emit zoomViewHorizontally ( newBegin , count );
00668   }
00669 }
00670 
00671 void KOAgendaView::zoomOutHorizontally( const QDate &date )
00672 {
00673   QDate begin;
00674   QDate newBegin;
00675   QDate dateToZoom = date;
00676   int ndays,count;
00677 
00678   begin = mSelectedDates.first();
00679   ndays = begin.daysTo( mSelectedDates.last() );
00680 
00681   // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it.
00682   if ( ! dateToZoom.isValid () )
00683     dateToZoom=mAgenda->selectedIncidenceDate();
00684 
00685   if ( !dateToZoom.isValid() ) {
00686     newBegin = begin.addDays(-1);
00687     count = ndays+3 ;
00688   } else {
00689     newBegin = dateToZoom.addDays( -ndays/2-1 );
00690     count = ndays+3;
00691   }
00692 
00693   if ( abs( count ) >= 31 )
00694     kdDebug(5850) << "change to the mounth view?"<<endl;
00695   else
00696     //We want to center the date
00697     emit zoomViewHorizontally( newBegin, count );
00698 }
00699 
00700 void KOAgendaView::zoomView( const int delta, const QPoint &pos,
00701   const Qt::Orientation orient )
00702 {
00703   static QDate zoomDate;
00704   static QTimer t( this );
00705 
00706 
00707   //Zoom to the selected incidence, on the other way
00708   // zoom to the date on screen after the first mousewheel move.
00709   if ( orient == Qt::Horizontal ) {
00710     QDate date=mAgenda->selectedIncidenceDate();
00711     if ( date.isValid() )
00712       zoomDate=date;
00713     else{
00714       if ( !t.isActive() ) {
00715         zoomDate= mSelectedDates[pos.x()];
00716       }
00717       t.start ( 1000,true );
00718     }
00719     if ( delta > 0 )
00720       zoomOutHorizontally( zoomDate );
00721     else
00722       zoomInHorizontally( zoomDate );
00723   } else {
00724     // Vertical zoom
00725     QPoint posConstentsOld = mAgenda->gridToContents(pos);
00726     if ( delta > 0 ) {
00727       zoomOutVertically();
00728     } else {
00729       zoomInVertically();
00730     }
00731     QPoint posConstentsNew = mAgenda->gridToContents(pos);
00732     mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() );
00733   }
00734 }
00735 
00736 void KOAgendaView::createDayLabels()
00737 {
00738 //  kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl;
00739 
00740   // ### Before deleting and recreating we could check if mSelectedDates changed...
00741   // It would remove some flickering and gain speed (since this is called by
00742   // each updateView() call)
00743   delete mDayLabels;
00744 
00745   mDayLabels = new QFrame (mDayLabelsFrame);
00746   mLayoutDayLabels = new QHBoxLayout(mDayLabels);
00747   mLayoutDayLabels->addSpacing(mTimeLabels->width());
00748 
00749   const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
00750 
00751   DateList::ConstIterator dit;
00752   for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
00753     QDate date = *dit;
00754     QBoxLayout *dayLayout = new QVBoxLayout(mLayoutDayLabels);
00755     mLayoutDayLabels->setStretchFactor(dayLayout, 1);
00756 //    dayLayout->setMinimumWidth(1);
00757 
00758     int dW = calsys->dayOfWeek(date);
00759     QString veryLongStr = KGlobal::locale()->formatDate( date );
00760     QString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" )
00761         .arg( calsys->weekDayName( dW, true ) )
00762         .arg( calsys->day(date) );
00763     QString shortstr = QString::number(calsys->day(date));
00764 
00765     KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr,
00766       longstr, veryLongStr, mDayLabels);
00767     dayLabel->setMinimumWidth(1);
00768     dayLabel->setAlignment(QLabel::AlignHCenter);
00769     if (date == QDate::currentDate()) {
00770       QFont font = dayLabel->font();
00771       font.setBold(true);
00772       dayLabel->setFont(font);
00773     }
00774     dayLayout->addWidget(dayLabel);
00775 
00776     // if a holiday region is selected, show the holiday name
00777     if ( KOGlobals::self()->holidays() ) {
00778       QString text = KOGlobals::self()->holidays()->shortText( date );
00779       if ( !text.isEmpty() ) {
00780         // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00781         KOAlternateLabel*label = new KOAlternateLabel( text, text, QString::null, mDayLabels );
00782         label->setMinimumWidth(1);
00783         label->setAlignment(AlignCenter);
00784         dayLayout->addWidget(label);
00785       }
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