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   KOrg::AgendaView (cal,parent,name), mExpandButton( 0 ), mAllowAgendaUpdate( true ),
00418   mUpdateItem( 0 ),
00419   mResource( 0 )
00420 {
00421   mSelectedDates.append(QDate::currentDate());
00422 
00423   mLayoutDayLabels = 0;
00424   mDayLabelsFrame = 0;
00425   mDayLabels = 0;
00426 
00427   bool isRTL = KOGlobals::self()->reverseLayout();
00428 
00429   if ( KOPrefs::instance()->compactDialogs() ) {
00430     if ( KOPrefs::instance()->mVerticalScreen ) {
00431       mExpandedPixmap = KOGlobals::self()->smallIcon( "1downarrow" );
00432       mNotExpandedPixmap = KOGlobals::self()->smallIcon( "1uparrow" );
00433     } else {
00434       mExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1leftarrow" : "1rightarrow" );
00435       mNotExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1rightarrow" : "1leftarrow" );
00436     }
00437   }
00438 
00439   QBoxLayout *topLayout = new QVBoxLayout(this);
00440 
00441   // Create day name labels for agenda columns
00442   mDayLabelsFrame = new QHBox(this);
00443   topLayout->addWidget(mDayLabelsFrame);
00444 
00445   // Create agenda splitter
00446 #ifndef KORG_NOSPLITTER
00447   mSplitterAgenda = new QSplitter(Vertical,this);
00448   topLayout->addWidget(mSplitterAgenda);
00449 
00450 #if KDE_IS_VERSION( 3, 1, 93 )
00451   mSplitterAgenda->setOpaqueResize( KGlobalSettings::opaqueResize() );
00452 #else
00453   mSplitterAgenda->setOpaqueResize();
00454 #endif
00455 
00456   mAllDayFrame = new QHBox(mSplitterAgenda);
00457 
00458   QWidget *agendaFrame = new QWidget(mSplitterAgenda);
00459 #else
00460   QVBox *mainBox = new QVBox( this );
00461   topLayout->addWidget( mainBox );
00462 
00463   mAllDayFrame = new QHBox(mainBox);
00464 
00465   QWidget *agendaFrame = new QWidget(mainBox);
00466 #endif
00467 
00468   // Create all-day agenda widget
00469   mDummyAllDayLeft = new QVBox( mAllDayFrame );
00470 
00471   if ( KOPrefs::instance()->compactDialogs() ) {
00472     mExpandButton = new QPushButton(mDummyAllDayLeft);
00473     mExpandButton->setPixmap( mNotExpandedPixmap );
00474     mExpandButton->setSizePolicy( QSizePolicy( QSizePolicy::Fixed,
00475                                   QSizePolicy::Fixed ) );
00476     connect( mExpandButton, SIGNAL( clicked() ), SIGNAL( toggleExpand() ) );
00477   } else {
00478     QLabel *label = new QLabel( i18n("All Day"), mDummyAllDayLeft );
00479     label->setAlignment( Qt::AlignRight | Qt::AlignVCenter | Qt::WordBreak );
00480   }
00481 
00482   mAllDayAgenda = new KOAgenda(1,mAllDayFrame);
00483   QWidget *dummyAllDayRight = new QWidget(mAllDayFrame);
00484 
00485   // Create agenda frame
00486   QGridLayout *agendaLayout = new QGridLayout(agendaFrame,3,3);
00487 //  QHBox *agendaFrame = new QHBox(splitterAgenda);
00488 
00489   // create event indicator bars
00490   mEventIndicatorTop = new EventIndicator(EventIndicator::Top,agendaFrame);
00491   agendaLayout->addWidget(mEventIndicatorTop,0,1);
00492   mEventIndicatorBottom = new EventIndicator(EventIndicator::Bottom,
00493                                              agendaFrame);
00494   agendaLayout->addWidget(mEventIndicatorBottom,2,1);
00495   QWidget *dummyAgendaRight = new QWidget(agendaFrame);
00496   agendaLayout->addWidget(dummyAgendaRight,0,2);
00497 
00498   // Create time labels
00499   mTimeLabels = new TimeLabels(24,agendaFrame);
00500   agendaLayout->addWidget(mTimeLabels,1,0);
00501 
00502   // Create agenda
00503   mAgenda = new KOAgenda(1,96,KOPrefs::instance()->mHourSize,agendaFrame);
00504   agendaLayout->addMultiCellWidget(mAgenda,1,1,1,2);
00505   agendaLayout->setColStretch(1,1);
00506 
00507   // Create event context menu for agenda
00508   mAgendaPopup = eventPopup();
00509 
00510   // Create event context menu for all day agenda
00511   mAllDayAgendaPopup = eventPopup();
00512 
00513   // make connections between dependent widgets
00514   mTimeLabels->setAgenda(mAgenda);
00515 
00516   // Update widgets to reflect user preferences
00517 //  updateConfig();
00518 
00519   createDayLabels();
00520 
00521   // these blank widgets make the All Day Event box line up with the agenda
00522   dummyAllDayRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
00523   dummyAgendaRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
00524 
00525   updateTimeBarWidth();
00526 
00527   // Scrolling
00528   connect(mAgenda->verticalScrollBar(),SIGNAL(valueChanged(int)),
00529           mTimeLabels, SLOT(positionChanged()));
00530 
00531   connect( mAgenda,
00532     SIGNAL( zoomView( const int, const QPoint & ,const Qt::Orientation ) ),
00533     SLOT( zoomView( const int, const QPoint &, const Qt::Orientation ) ) );
00534 
00535   connect(mTimeLabels->verticalScrollBar(),SIGNAL(valueChanged(int)),
00536           SLOT(setContentsPos(int)));
00537 
00538   // Create Events, depends on type of agenda
00539   connect( mAgenda, SIGNAL(newTimeSpanSignal(const QPoint &, const QPoint &)),
00540                     SLOT(newTimeSpanSelected(const QPoint &, const QPoint &)));
00541   connect( mAllDayAgenda, SIGNAL(newTimeSpanSignal(const QPoint &, const QPoint &)),
00542                           SLOT(newTimeSpanSelectedAllDay(const QPoint &, const QPoint &)));
00543 
00544   // event indicator update
00545   connect( mAgenda, SIGNAL(lowerYChanged(int)),
00546                     SLOT(updateEventIndicatorTop(int)));
00547   connect( mAgenda, SIGNAL(upperYChanged(int)),
00548                     SLOT(updateEventIndicatorBottom(int)));
00549 
00550   connectAgenda( mAgenda, mAgendaPopup, mAllDayAgenda );
00551   connectAgenda( mAllDayAgenda, mAllDayAgendaPopup, mAgenda);
00552 }
00553 
00554 
00555 KOAgendaView::~KOAgendaView()
00556 {
00557   delete mAgendaPopup;
00558   delete mAllDayAgendaPopup;
00559 }
00560 
00561 void KOAgendaView::connectAgenda( KOAgenda *agenda, QPopupMenu *popup,
00562                                   KOAgenda *otherAgenda )
00563 {
00564   connect( agenda, SIGNAL( showIncidencePopupSignal( Incidence *, const QDate & ) ),
00565            popup, SLOT( showIncidencePopup( Incidence *, const QDate & ) ) );
00566 
00567   connect( agenda, SIGNAL( showNewEventPopupSignal() ),
00568            SLOT( showNewEventPopup() ) );
00569 
00570   agenda->setCalendar( calendar() );
00571 
00572   // Create/Show/Edit/Delete Event
00573   connect( agenda, SIGNAL( newEventSignal() ), SIGNAL( newEventSignal() ) );
00574 
00575   connect( agenda, SIGNAL( newStartSelectSignal() ),
00576            otherAgenda, SLOT( clearSelection() ) );
00577   connect( agenda, SIGNAL( newStartSelectSignal() ),
00578            SIGNAL( timeSpanSelectionChanged()) );
00579 
00580   connect( agenda, SIGNAL( editIncidenceSignal( Incidence * ) ),
00581                    SIGNAL( editIncidenceSignal( Incidence * ) ) );
00582   connect( agenda, SIGNAL( showIncidenceSignal( Incidence * ) ),
00583                    SIGNAL( showIncidenceSignal( Incidence * ) ) );
00584   connect( agenda, SIGNAL( deleteIncidenceSignal( Incidence * ) ),
00585                    SIGNAL( deleteIncidenceSignal( Incidence * ) ) );
00586 
00587   connect( agenda, SIGNAL( startMultiModify( const QString & ) ),
00588                    SIGNAL( startMultiModify( const QString & ) ) );
00589   connect( agenda, SIGNAL( endMultiModify() ),
00590                    SIGNAL( endMultiModify() ) );
00591 
00592   connect( agenda, SIGNAL( itemModified( KOAgendaItem * ) ),
00593                    SLOT( updateEventDates( KOAgendaItem * ) ) );
00594   connect( agenda, SIGNAL( enableAgendaUpdate( bool ) ),
00595                    SLOT( enableAgendaUpdate( bool ) ) );
00596 
00597   // drag signals
00598   connect( agenda, SIGNAL( startDragSignal( Incidence * ) ),
00599            SLOT( startDrag( Incidence * ) ) );
00600 
00601   // synchronize selections
00602   connect( agenda, SIGNAL( incidenceSelected( Incidence * ) ),
00603            otherAgenda, SLOT( deselectItem() ) );
00604   connect( agenda, SIGNAL( incidenceSelected( Incidence * ) ),
00605            SIGNAL( incidenceSelected( Incidence * ) ) );
00606 
00607   // rescheduling of todos by d'n'd
00608   connect( agenda, SIGNAL( droppedToDo( Todo *, const QPoint &, bool ) ),
00609            SLOT( slotTodoDropped( Todo *, const QPoint &, bool ) ) );
00610 
00611 }
00612 
00613 void KOAgendaView::zoomInVertically( )
00614 {
00615   KOPrefs::instance()->mHourSize++;
00616   mAgenda->updateConfig();
00617   mAgenda->checkScrollBoundaries();
00618 
00619   mTimeLabels->updateConfig();
00620   mTimeLabels->positionChanged();
00621   mTimeLabels->repaint();
00622 
00623   updateView();
00624 }
00625 
00626 void KOAgendaView::zoomOutVertically( )
00627 {
00628 
00629   if ( KOPrefs::instance()->mHourSize > 4 ) {
00630 
00631     KOPrefs::instance()->mHourSize--;
00632     mAgenda->updateConfig();
00633     mAgenda->checkScrollBoundaries();
00634 
00635     mTimeLabels->updateConfig();
00636     mTimeLabels->positionChanged();
00637     mTimeLabels->repaint();
00638 
00639     updateView();
00640   }
00641 }
00642 
00643 void KOAgendaView::zoomInHorizontally( const QDate &date)
00644 {
00645   QDate begin;
00646   QDate newBegin;
00647   QDate dateToZoom = date;
00648   int ndays,count;
00649 
00650   begin = mSelectedDates.first();
00651   ndays = begin.daysTo( mSelectedDates.last() );
00652 
00653   // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it.
00654   if ( ! dateToZoom.isValid () )
00655     dateToZoom=mAgenda->selectedIncidenceDate();
00656 
00657   if( !dateToZoom.isValid() ) {
00658     if ( ndays > 1 ) {
00659       newBegin=begin.addDays(1);
00660       count = ndays-1;
00661       emit zoomViewHorizontally ( newBegin , count );
00662     }
00663   } else {
00664     if ( ndays <= 2 ) {
00665       newBegin = dateToZoom;
00666       count = 1;
00667     } else  {
00668       newBegin = dateToZoom.addDays( -ndays/2 +1  );
00669       count = ndays -1 ;
00670     }
00671     emit zoomViewHorizontally ( newBegin , count );
00672   }
00673 }
00674 
00675 void KOAgendaView::zoomOutHorizontally( const QDate &date )
00676 {
00677   QDate begin;
00678   QDate newBegin;
00679   QDate dateToZoom = date;
00680   int ndays,count;
00681 
00682   begin = mSelectedDates.first();
00683   ndays = begin.daysTo( mSelectedDates.last() );
00684 
00685   // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it.
00686   if ( ! dateToZoom.isValid () )
00687     dateToZoom=mAgenda->selectedIncidenceDate();
00688 
00689   if ( !dateToZoom.isValid() ) {
00690     newBegin = begin.addDays(-1);
00691     count = ndays+3 ;
00692   } else {
00693     newBegin = dateToZoom.addDays( -ndays/2-1 );
00694     count = ndays+3;
00695   }
00696 
00697   if ( abs( count ) >= 31 )
00698     kdDebug(5850) << "change to the mounth view?"<<endl;
00699   else
00700     //We want to center the date
00701     emit zoomViewHorizontally( newBegin, count );
00702 }
00703 
00704 void KOAgendaView::zoomView( const int delta, const QPoint &pos,
00705   const Qt::Orientation orient )
00706 {
00707   static QDate zoomDate;
00708   static QTimer t( this );
00709 
00710 
00711   //Zoom to the selected incidence, on the other way
00712   // zoom to the date on screen after the first mousewheel move.
00713   if ( orient == Qt::Horizontal ) {
00714     QDate date=mAgenda->selectedIncidenceDate();
00715     if ( date.isValid() )
00716       zoomDate=date;
00717     else{
00718       if ( !t.isActive() ) {
00719         zoomDate= mSelectedDates[pos.x()];
00720       }
00721       t.start ( 1000,true );
00722     }
00723     if ( delta > 0 )
00724       zoomOutHorizontally( zoomDate );
00725     else
00726       zoomInHorizontally( zoomDate );
00727   } else {
00728     // Vertical zoom
00729     QPoint posConstentsOld = mAgenda->gridToContents(pos);
00730     if ( delta > 0 ) {
00731       zoomOutVertically();
00732     } else {
00733       zoomInVertically();
00734     }
00735     QPoint posConstentsNew = mAgenda->gridToContents(pos);
00736     mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() );
00737   }
00738 }
00739 
00740 void KOAgendaView::createDayLabels()
00741 {
00742 //  kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl;
00743 
00744   // ### Before deleting and recreating we could check if mSelectedDates changed...
00745   // It would remove some flickering and gain speed (since this is called by
00746   // each updateView() call)
00747   delete mDayLabels;
00748 
00749   mDayLabels = new QFrame (mDayLabelsFrame);
00750   mLayoutDayLabels = new QHBoxLayout(mDayLabels);
00751   mLayoutDayLabels->addSpacing(mTimeLabels->width());
00752 
00753   const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
00754 
00755   DateList::ConstIterator dit;
00756   for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
00757     QDate date = *dit;
00758     QBoxLayout *dayLayout = new QVBoxLayout(mLayoutDayLabels);
00759     mLayoutDayLabels->setStretchFactor(dayLayout, 1);
00760 //    dayLayout->setMinimumWidth(1);
00761 
00762     int dW = calsys->dayOfWeek(date);
00763     QString veryLongStr = KGlobal::locale()->formatDate( date );
00764     QString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" )
00765         .arg( calsys->weekDayName( dW, true ) )
00766         .arg( calsys->day(date) );
00767     QString shortstr = QString::number(calsys->day(date));
00768 
00769     KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr,
00770       longstr, veryLongStr, mDayLabels);
00771     dayLabel->setMinimumWidth(1);
00772     dayLabel->setAlignment(QLabel::AlignHCenter);
00773     if (date == QDate::currentDate()) {
00774       QFont font = dayLabel->font();
00775       font.setBold(true);
00776       dayLabel->setFont(font);
00777     }
00778     dayLayout->addWidget(dayLabel);
00779 
00780     // if a holiday region is selected, show the holiday name
00781     QStringList texts = KOGlobals::self()->holiday( date );
00782     QStringList::ConstIterator textit = texts.begin();
00783     for ( ; textit != texts.end(); ++textit ) {
00784       // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00785       KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), QString::null, mDayLabels );
00786       label->setMinimumWidth(1);
00787       label->setAlignment(AlignCenter);
00788       dayLayout->addWidget(label);
00789     }
00790 
00791 #ifndef KORG_NOPLUGINS
00792     CalendarDecoration::List cds = KOCore::self()->calendarDecorations();
00793     CalendarDecoration *it;
00794     for(it = cds.first(); it; it = cds.next()) {
00795       QString text = it->shortText( date );
00796       if ( !text.isEmpty() ) {
00797         // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
00798         KOAlternateLabel*label = new KOAlternateLabel( text, text, QString::null, mDayLabels );
00799         label->setMinimumWidth(1);
00800         label->setAlignment(AlignCenter);
00801         dayLayout->addWidget(label);
00802       }
00803     }
00804 
00805     for(it = cds.first(); it; it = cds.next()) {
00806       QWidget *wid = it->smallWidget(mDayLabels,date);
00807       if ( wid ) {
00808 //      wid->setHeight(20);
00809         dayLayout->addWidget(wid);
00810       }
00811     }
00812 #endif
00813   }
00814 
00815   mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width());
00816   mDayLabels->show();
00817 }
00818 
00819 void KOAgendaView::enableAgendaUpdate( bool enable )
00820 {
00821   mAllowAgendaUpdate = enable;
00822 }
00823 
00824 int KOAgendaView::maxDatesHint()
00825 {
00826   // Not sure about the max number of events, so return 0 for now.
00827   return 0;
00828 }
00829 
00830 int KOAgendaView::currentDateCount()
00831 {
00832   return mSelectedDates.count();
00833 }
00834 
00835 Incidence::List KOAgendaView::selectedIncidences()
00836 {
00837   Incidence::List selected;
00838   Incidence *incidence;
00839 
00840   incidence = mAgenda->selectedIncidence();
00841   if (incidence) selected.append(incidence);
00842 
00843   incidence = mAllDayAgenda->selectedIncidence();
00844   if (incidence) selected.append(incidence);
00845 
00846   return selected;
00847 }
00848 
00849 DateList KOAgendaView::selectedDates()
00850 {
00851   DateList selected;
00852   QDate qd;
00853 
00854   qd = mAgenda->selectedIncidenceDate();
00855   if (qd.isValid()) selected.append(qd);
00856 
00857   qd = mAllDayAgenda->selectedIncidenceDate();
00858   if (qd.isValid()) selected.append(qd);
00859 
00860   return selected;
00861 }
00862 
00863 bool KOAgendaView::eventDurationHint( QDateTime &startDt, QDateTime &endDt,
00864                                       bool &allDay )
00865 {
00866   if ( selectionStart().isValid() ) {
00867     QDateTime start = selectionStart();
00868     QDateTime end = selectionEnd();
00869 
00870     if ( start.secsTo( end ) == 15*60 ) {
00871       // One cell in the agenda view selected, e.g.
00872       // because of a double-click, => Use the default duration
00873       QTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() );
00874       int addSecs = ( defaultDuration.hour()*3600 ) +
00875                     ( defaultDuration.minute()*60 );
00876       end = start.addSecs( addSecs );
00877     }
00878 
00879     startDt = start;
00880     endDt = end;
00881     allDay = selectedIsAllDay();
00882     return true;
00883   }
00884   return false;
00885 }
00886 
00888 bool KOAgendaView::selectedIsSingleCell()
00889 {
00890   if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false;
00891 
00892   if (selectedIsAllDay()) {
00893     int days = selectionStart().daysTo(selectionEnd());
00894     return ( days < 1 );
00895   } else {
00896     int secs = selectionStart().secsTo(selectionEnd());
00897     return ( secs <= 24*60*60/mAgenda->rows() );
00898   }
00899 }
00900 
00901 
00902 void KOAgendaView::updateView()
00903 {
00904 //  kdDebug(5850) << "KOAgendaView::updateView()" << endl;
00905   fillAgenda();
00906 }
00907 
00908 
00909 /*
00910   Update configuration settings for the agenda view. This method is not
00911   complete.
00912 */
00913 void KOAgendaView::updateConfig()
00914 {
00915 //  kdDebug(5850) << "KOAgendaView::updateConfig()" << endl;
00916 
00917   // update config for children
00918   mTimeLabels->updateConfig();
00919   mAgenda->updateConfig();
00920   mAllDayAgenda->updateConfig();
00921 
00922   // widget synchronization
00923   // FIXME: find a better way, maybe signal/slot
00924   mTimeLabels->positionChanged();
00925 
00926   // for some reason, this needs to be called explicitly
00927   mTimeLabels->repaint();
00928 
00929   updateTimeBarWidth();
00930 
00931   // ToolTips displaying summary of events
00932   KOAgendaItem::toolTipGroup()->setEnabled(KOPrefs::instance()
00933                                            ->mEnableToolTips);
00934 
00935   setHolidayMasks();
00936 
00937   createDayLabels();
00938 
00939   updateView();
00940 }
00941 
00942 void KOAgendaView::updateTimeBarWidth()
00943 {
00944   int width;
00945 
00946   width = mDummyAllDayLeft->fontMetrics().width( i18n("All Day") );
00947   width = QMAX( width, mTimeLabels->width() );
00948 
00949   mDummyAllDayLeft->setFixedWidth( width );
00950   mTimeLabels->setFixedWidth( width );
00951 }
00952 
00953 
00954 void KOAgendaView::updateEventDates( KOAgendaItem *item )
00955 {
00956   kdDebug(5850) << "KOAgendaView::updateEventDates(): " << item->text() << endl;
00957 
00958   QDateTime startDt,endDt;
00959 
00960   // Start date of this incidence, calculate the offset from it (so recurring and
00961   // non-recurring items can be treated exactly the same, we never need to check
00962   // for doesRecur(), because we only move the start day by the number of days the
00963   // agenda item was really moved. Smart, isn't it?)
00964   QDate thisDate;
00965   if ( item->cellXLeft() < 0 ) {
00966     thisDate = ( mSelectedDates.first() ).addDays( item->cellXLeft() );
00967   } else {
00968     thisDate = mSelectedDates[ item->cellXLeft() ];
00969   }
00970   QDate oldThisDate( item->itemDate() );
00971   int daysOffset = oldThisDate.daysTo( thisDate );
00972   int daysLength = 0;
00973 
00974 //  startDt.setDate( startDate );
00975 
00976   Incidence *incidence = item->incidence();
00977   if ( !incidence ) return;
00978   if ( !mChanger || !mChanger->beginChange(incidence) ) return;
00979   Incidence *oldIncidence = incidence->clone();
00980 
00981   QTime startTime(0,0,0), endTime(0,0,0);
00982   if ( incidence->doesFloat() ) {
00983     daysLength = item->cellWidth() - 1;
00984   } else {
00985     startTime = mAgenda->gyToTime( item->cellYTop() );
00986     if ( item->lastMultiItem() ) {
00987       endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 );
00988       daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft();
00989     } else {
00990       endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
00991     }
00992   }
00993 
00994 //  kdDebug(5850) << "KOAgendaView::updateEventDates(): now setting dates" << endl;
00995   // FIXME: use a visitor here
00996   if ( incidence->type() == "Event" ) {
00997     startDt = incidence->dtStart();
00998     startDt = startDt.addDays( daysOffset );
00999     startDt.setTime( startTime );
01000     endDt = startDt.addDays( daysLength );
01001     endDt.setTime( endTime );
01002     Event*ev = static_cast<Event*>(incidence);
01003     if( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) {
01004       // No change
01005       delete oldIncidence;
01006       return;
01007     }
01008     incidence->setDtStart( startDt );
01009     ev->setDtEnd( endDt );
01010   } else if ( incidence->type() == "Todo" ) {
01011     Todo *td = static_cast<Todo*>(incidence);
01012     startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
01013     startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) );
01014     startDt.setTime( startTime );
01015     endDt.setDate( thisDate );
01016     endDt.setTime( endTime );
01017 
01018     if( td->dtDue() == endDt ) {
01019       // No change
01020       delete oldIncidence;
01021       return;
01022     }
01023   }
01024   // FIXME: Adjusting the recurrence should really go to CalendarView so this
01025   // functionality will also be available in other views!
01026   // TODO_Recurrence: This does not belong here, and I'm not really sure
01027   // how it's supposed to work anyway.
01028   Recurrence *recur = incidence->recurrence();
01029 /*  if ( recur->doesRecur() && daysOffset != 0 ) {
01030     switch ( recur->recurrenceType() ) {
01031       case Recurrence::rYearlyPos: {
01032         int freq = recur->frequency();
01033         int duration = recur->duration();
01034         QDate endDt( recur->endDate() );
01035         bool negative = false;
01036 
01037         QPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() );
01038         if ( monthPos.first() ) {
01039           negative = monthPos.first()->negative;
01040         }
01041         QBitArray days( 7 );
01042         int pos = 0;
01043         days.fill( false );
01044         days.setBit( thisDate.dayOfWeek() - 1 );
01045         if ( negative ) {
01046           pos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
01047         } else {
01048           pos =  ( thisDate.day()-1 ) / 7 + 1;
01049         }
01050         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01051         recur->unsetRecurs();
01052         if ( duration != 0 ) {
01053           recur->setYearly( Recurrence::rYearlyPos, freq, duration );
01054         } else {
01055           recur->setYearly( Recurrence::rYearlyPos, freq, endDt );
01056         }
01057         recur->addYearlyMonthPos( pos, days );
01058         recur->addYearlyNum( thisDate.month() );
01059 
01060         break; }
01061         case Recurrence::rYearlyDay: {
01062           int freq = recur->frequency();
01063           int duration = recur->duration();
01064           QDate endDt( recur->endDate() );
01065         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01066           recur->unsetRecurs();
01067           if ( duration == 0 ) { // end by date
01068             recur->setYearly( Recurrence::rYearlyDay, freq, endDt );
01069           } else {
01070             recur->setYearly( Recurrence::rYearlyDay, freq, duration );
01071           }
01072           recur->addYearlyNum( thisDate.dayOfYear() );
01073           break; }
01074           case Recurrence::rYearlyMonth: {
01075             int freq = recur->frequency();
01076             int duration = recur->duration();
01077             QDate endDt( recur->endDate() );
01078         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01079             recur->unsetRecurs();
01080             if ( duration != 0 ) {
01081               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration );
01082             } else {
01083               recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt );
01084             }
01085             recur->addYearlyNum( thisDate.month() );
01086             break; }
01087             case Recurrence::rMonthlyPos: {
01088               int freq = recur->frequency();
01089               int duration = recur->duration();
01090               QDate endDt( recur->endDate() );
01091               QPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() );
01092               if ( !monthPos.isEmpty() ) {
01093           // FIXME: How shall I adapt the day x of week Y if we move the date across month borders???
01094           // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
01095           // That's fine for korganizer, but might mess up other organizers.
01096                 QBitArray rDays( 7 );
01097                 rDays = monthPos.first()->rDays;
01098                 bool negative = monthPos.first()->negative;
01099                 int newPos;
01100                 rDays.fill( false );
01101                 rDays.setBit( thisDate.dayOfWeek() - 1 );
01102                 if ( negative ) {
01103                   newPos =  - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
01104                 } else {
01105                   newPos =  ( thisDate.day()-1 ) / 7 + 1;
01106                 }
01107 
01108           // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01109                 recur->unsetRecurs();
01110                 if ( duration == 0 ) { // end by date
01111                   recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt );
01112                 } else {
01113                   recur->setMonthly( Recurrence::rMonthlyPos, freq, duration );
01114                 }
01115                 recur->addMonthlyPos( newPos, rDays );
01116               }
01117               break;}
01118               case Recurrence::rMonthlyDay: {
01119                 int freq = recur->frequency();
01120                 int duration = recur->duration();
01121                 QDate endDt( recur->endDate() );
01122                 QPtrList<int> monthDays( recur->monthDays() );
01123         // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
01124                 recur->unsetRecurs();
01125                 if ( duration == 0 ) { // end by date
01126                   recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt );
01127                 } else {
01128                   recur->setMonthly( Recurrence::rMonthlyDay, freq, duration );
01129                 }
01130         // FIXME: How shall I adapt the n-th day if we move the date across month borders???
01131         // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
01132         // That's fine for korganizer, but might mess up other organizers.
01133                 recur->addMonthlyDay( thisDate.day() );
01134 
01135                 break;}
01136                 case Recurrence::rWeekly: {
01137                   QBitArray days(7), oldDays( recur->days() );
01138                   int offset = daysOffset % 7;
01139                   if ( offset<0 ) offset = (offset+7) % 7;
01140         // rotate the days
01141                   for (int d=0; d<7; d++ ) {
01142                     days.setBit( (d+offset) % 7, oldDays.at(d) );
01143                   }
01144                   if ( recur->duration() == 0 ) { // end by date
01145                     recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() );
01146                   } else { // duration or no end
01147                     recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() );
01148                   }
01149                   break;}
01150       // nothing to be done for the following:
01151       case Recurrence::rDaily:
01152       case Recurrence::rHourly:
01153       case Recurrence::rMinutely:
01154       case Recurrence::rNone:
01155       default:
01156         break;
01157     }
01158     if ( recur->duration()==0 ) { // end by date
01159       recur->setEndDate( recur->endDate().addDays( daysOffset ) );
01160     }
01161     KMessageBox::information( this, i18n("A recurring calendar item was moved "
01162                               "to a different day. The recurrence settings "
01163                               "have been updated with that move. Please check "
01164                               "them in the editor."),
01165                               i18n("Recurrence Moved"),
01166                               "RecurrenceMoveInAgendaWarning" );
01167   }*/
01168 
01169   // FIXME: use a visitor here
01170   if ( incidence->type() == "Event" ) {
01171     incidence->setDtStart( startDt );
01172     (static_cast<Event*>( incidence ) )->setDtEnd( endDt );
01173   } else if ( incidence->type() == "Todo" ) {
01174     Todo *td = static_cast<Todo*>( incidence );
01175     if ( td->hasStartDate() )
01176       td->setDtStart( startDt );
01177     td->setDtDue( endDt );
01178   }
01179 
01180   item->setItemDate( startDt.date() );
01181 
01182   KOIncidenceToolTip::remove( item );
01183   KOIncidenceToolTip::add( item, incidence, KOAgendaItem::toolTipGroup() );
01184 
01185   mChanger->changeIncidence( oldIncidence, incidence );
01186   mChanger->endChange(incidence);
01187   delete oldIncidence;
01188 
01189   // don't update the agenda as the item already has the correct coordinates.
01190   // an update would delete the current item and recreate it, but we are still
01191   // using a pointer to that item! => CRASH
01192   enableAgendaUpdate( false );
01193   // We need to do this in a timer to make sure we are not deleting the item
01194   // we are currently working on, which would lead to crashes
01195   // Only the actually moved agenda item is already at the correct position and mustn't be
01196   // recreated. All others have to!!!
01197   if ( incidence->doesRecur() ) {
01198     mUpdateItem = incidence;
01199     QTimer::singleShot( 0, this, SLOT( doUpdateItem() ) );
01200   }
01201 
01202     enableAgendaUpdate( true );
01203 
01204 //  kdDebug(5850) << "KOAgendaView::updateEventDates() done " << endl;
01205 }
01206 
01207 void KOAgendaView::doUpdateItem()
01208 {
01209   if ( mUpdateItem ) {
01210     changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED );
01211     mUpdateItem = 0;
01212   }
01213 }
01214 
01215 
01216 
01217 void KOAgendaView::showDates( const QDate &start, const QDate &end )
01218 {
01219 //  kdDebug(5850) << "KOAgendaView::selectDates" << endl;
01220 
01221   mSelectedDates.clear();
01222 
01223   QDate d = start;
01224   while (d <= end) {
01225     mSelectedDates.append(d);
01226     d = d.addDays( 1 );
01227   }
01228 
01229   // and update the view
01230   fillAgenda();
01231 }
01232 
01233 
01234 void KOAgendaView::showIncidences( const Incidence::List & )
01235 {
01236   kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl;
01237 }
01238 
01239 void KOAgendaView::insertIncidence( Incidence *incidence, const QDate &curDate,
01240                                     int curCol )
01241 {
01242   if ( !filterByResource( incidence ) )
01243     return;
01244 
01245   // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's
01246   Event *event = dynamic_cast<Event *>( incidence );
01247   Todo  *todo  = dynamic_cast<Todo  *>( incidence );
01248 
01249   if ( curCol < 0 ) {
01250     curCol = mSelectedDates.findIndex( curDate );
01251   }
01252   // The date for the event is not displayed, just ignore it
01253   if ( curCol < 0 || curCol > int( mSelectedDates.size() ) )
01254     return;
01255 
01256   int beginX;
01257   int endX;
01258   if ( event ) {
01259     beginX = curDate.daysTo( incidence->dtStart().date() ) + curCol;
01260     endX = curDate.daysTo( event->dateEnd() ) + curCol;
01261   } else if ( todo ) {
01262     if ( ! todo->hasDueDate() ) return;  // todo shall not be displayed if it has no date
01263     beginX = curDate.daysTo( todo->dtDue().date() ) + curCol;
01264     endX = beginX;
01265   } else {
01266     return;
01267   }
01268 
01269   if ( todo && todo->isOverdue() ) {
01270     mAllDayAgenda->insertAllDayItem( incidence, curDate, curCol, curCol );
01271   } else if ( incidence->doesFloat() ) {
01272 // FIXME: This breaks with recurring multi-day events!
01273     if ( incidence->recurrence()->doesRecur() ) {
01274       mAllDayAgenda->insertAllDayItem( incidence, curDate, curCol, curCol );
01275     } else {
01276       // Insert multi-day events only on the first day, otherwise it will
01277       // appear multiple times
01278       if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) {
01279         mAllDayAgenda->insertAllDayItem( incidence, curDate, beginX, endX );
01280       }
01281     }
01282   } else if ( event && event->isMultiDay() ) {
01283     int startY = mAgenda->timeToY( event->dtStart().time() );
01284     QTime endtime( event->dtEnd().time() );
01285     if ( endtime == QTime( 0, 0, 0 ) ) endtime = QTime( 23, 59, 59 );
01286     int endY = mAgenda->timeToY( endtime ) - 1;
01287     if ( (beginX <= 0 && curCol == 0) || beginX == curCol ) {
01288       mAgenda->insertMultiItem( event, curDate, beginX, endX, startY, endY );
01289     }
01290     if ( beginX == curCol ) {
01291       mMaxY[curCol] = mAgenda->timeToY( QTime(23,59) );
01292       if ( startY < mMinY[curCol] ) mMinY[curCol] = startY;
01293     } else if ( endX == curCol ) {
01294       mMinY[curCol] = mAgenda->timeToY( QTime(0,0) );
01295       if ( endY > mMaxY[curCol] ) mMaxY[curCol] = endY;
01296     } else {
01297       mMinY[curCol] = mAgenda->timeToY( QTime(0,0) );
01298       mMaxY[curCol] = mAgenda->timeToY( QTime(23,59) );
01299     }
01300   } else {
01301     int startY = 0, endY = 0;
01302     if ( event ) {
01303       startY = mAgenda->timeToY( incidence->dtStart().time() );
01304       QTime endtime( event->dtEnd().time() );
01305       if ( endtime == QTime( 0, 0, 0 ) ) endtime = QTime( 23, 59, 59 );
01306       endY = mAgenda->timeToY( endtime ) - 1;
01307     }
01308     if ( todo ) {
01309       QTime t = todo->dtDue().time();
01310       endY = mAgenda->timeToY( t ) - 1;
01311       startY = mAgenda->timeToY( t.addSecs( -1800 ) );
01312     }
01313     if ( endY < startY ) endY = startY;
01314     mAgenda->insertItem( incidence, curDate, curCol, startY, endY );
01315     if ( startY < mMinY[curCol] ) mMinY[curCol] = startY;
01316     if ( endY > mMaxY[curCol] ) mMaxY[curCol] = endY;
01317   }
01318 }
01319 
01320 void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence )
01321 {
01322   Todo *todo = dynamic_cast<Todo *>(incidence);
01323   CalFilter *filter = calendar()->filter();
01324   if ( filter && !filter->filterIncidence( incidence ) ||
01325      ( todo && !KOPrefs::instance()->showAllDayTodo() ) )
01326     return;
01327 
01328   QDate f = mSelectedDates.first();
01329   QDate l = mSelectedDates.last();
01330   QDate startDt = incidence->dtStart().date();
01331 
01332   if ( incidence->doesRecur() ) {
01333     DateList::ConstIterator dit;
01334     QDate curDate;
01335     for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
01336       curDate = *dit;
01337 // FIXME: This breaks with recurring multi-day events!
01338       if ( incidence->recursOn( curDate ) ) {
01339         insertIncidence( incidence, curDate );
01340       }
01341     }
01342     return;
01343   }
01344 
01345   QDate endDt;
01346   if ( incidence->type() == "Event" )
01347     endDt = (static_cast<Event *>(incidence))->dateEnd();
01348   if ( todo ) {
01349     endDt = todo->isOverdue() ? QDate::currentDate()
01350                               : todo->dtDue().date();
01351 
01352     if ( endDt >= f && endDt <= l ) {
01353       insertIncidence( incidence, endDt );
01354       return;
01355     }
01356   }
01357 
01358   if ( startDt >= f && startDt <= l ) {
01359     insertIncidence( incidence, startDt );
01360   }
01361 }
01362 
01363 void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode )
01364 {
01365   switch ( mode ) {
01366     case KOGlobals::INCIDENCEADDED: {
01367         //  Add an event. No need to recreate the whole view!
01368         // recreating everything even causes troubles: dropping to the day matrix
01369         // recreates the agenda items, but the evaluation is still in an agendaItems' code,
01370         // which was deleted in the mean time. Thus KOrg crashes...
01371       if ( mAllowAgendaUpdate )
01372         changeIncidenceDisplayAdded( incidence );
01373       break;
01374     }
01375     case KOGlobals::INCIDENCEEDITED: {
01376       if ( !mAllowAgendaUpdate ) {
01377         updateEventIndicators();
01378       } else {
01379         removeIncidence( incidence );
01380         updateEventIndicators();
01381         changeIncidenceDisplayAdded( incidence );
01382       }
01383       break;
01384     }
01385     case KOGlobals::INCIDENCEDELETED: {
01386       mAgenda->removeIncidence( incidence );
01387       mAllDayAgenda->removeIncidence( incidence );
01388       updateEventIndicators();
01389       break;
01390     }
01391     default:
01392       updateView();
01393   }
01394 }
01395 
01396 void KOAgendaView::fillAgenda( const QDate & )
01397 {
01398   fillAgenda();
01399 }
01400 
01401 void KOAgendaView::fillAgenda()
01402 {
01403   /* Remember the uids of the selected items. In case one of the
01404    * items was deleted and re-added, we want to reselect it. */
01405   const QString &selectedAgendaUid = mAgenda->lastSelectedUid();
01406   const QString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
01407 
01408   enableAgendaUpdate( true );
01409   clearView();
01410 
01411   mAllDayAgenda->changeColumns(mSelectedDates.count());
01412   mAgenda->changeColumns(mSelectedDates.count());
01413   mEventIndicatorTop->changeColumns(mSelectedDates.count());
01414   mEventIndicatorBottom->changeColumns(mSelectedDates.count());
01415 
01416   createDayLabels();
01417   setHolidayMasks();
01418 
01419   mMinY.resize(mSelectedDates.count());
01420   mMaxY.resize(mSelectedDates.count());
01421 
01422   Event::List dayEvents;
01423 
01424   // ToDo items shall be displayed for the day they are due, but only shown today if they are already overdue.
01425   // Therefore, get all of them.
01426   Todo::List todos  = calendar()->todos();
01427 
01428   mAgenda->setDateList(mSelectedDates);
01429 
01430   QDate today = QDate::currentDate();
01431 
01432   bool somethingReselected = false;
01433   DateList::ConstIterator dit;
01434   int curCol = 0;
01435   for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
01436     QDate currentDate = *dit;
01437 //    kdDebug(5850) << "KOAgendaView::fillAgenda(): " << currentDate.toString()
01438 //              << endl;
01439 
01440     dayEvents = calendar()->events(currentDate,
01441                                    EventSortStartDate,
01442                                    SortDirectionAscending);
01443 
01444     // Default values, which can never be reached
01445     mMinY[curCol] = mAgenda->timeToY(QTime(23,59)) + 1;
01446     mMaxY[curCol] = mAgenda->timeToY(QTime(0,0)) - 1;
01447 
01448     unsigned int numEvent;
01449     for(numEvent=0;numEvent<dayEvents.count();++numEvent) {
01450       Event *event = *dayEvents.at(numEvent);
01451 //      kdDebug(5850) << " Event: " << event->summary() << endl;
01452       insertIncidence( event, currentDate, curCol );
01453       if( event->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
01454         mAgenda->selectItemByUID( event->uid() );
01455         somethingReselected = true;
01456       }
01457       if( event->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
01458         mAllDayAgenda->selectItemByUID( event->uid() );
01459         somethingReselected = true;
01460       }
01461 
01462     }
01463 //    if (numEvent == 0) kdDebug(5850) << " No events" << endl;
01464 
01465 
01466     // ---------- [display Todos --------------
01467     if ( KOPrefs::instance()->showAllDayTodo() ) {
01468       unsigned int numTodo;
01469       for (numTodo = 0; numTodo < todos.count(); ++numTodo) {
01470         Todo *todo = *todos.at(numTodo);
01471 
01472         if ( ! todo->hasDueDate() ) continue;  // todo shall not be displayed if it has no date
01473 
01474         if ( !filterByResource( todo ) ) continue;
01475 
01476         // ToDo items shall be displayed for the day they are due, but only showed today if they are already overdue.
01477         // Already completed items can be displayed on their original due date
01478         bool overdue = todo->isOverdue();
01479 
01480         if ( (( todo->dtDue().date() == currentDate) && !overdue) ||
01481              (( currentDate == today) && overdue) ||
01482              ( todo->recursOn( currentDate ) ) ) {
01483           if ( todo->doesFloat() || overdue ) {  // Todo has no due-time set or is already overdue
01484             //kdDebug(5850) << "todo without time:" << todo->dtDueDateStr() << ";" << todo->summary() << endl;
01485 
01486             mAllDayAgenda->insertAllDayItem(todo, currentDate, curCol, curCol);
01487           } else {
01488             //kdDebug(5850) << "todo with time:" << todo->dtDueStr() << ";" << todo->summary() << endl;
01489 
01490             int endY = mAgenda->timeToY(todo->dtDue().time()) - 1;
01491             int startY = endY - 1;
01492 
01493             mAgenda->insertItem(todo,currentDate,curCol,startY,endY);
01494 
01495             if (startY < mMinY[curCol]) mMinY[curCol] = startY;
01496             if (endY > mMaxY[curCol]) mMaxY[curCol] = endY;
01497           }
01498         }
01499       }
01500     }
01501     // ---------- display Todos] --------------
01502 
01503     ++curCol;
01504   }
01505 
01506   mAgenda->checkScrollBoundaries();
01507   updateEventIndicators();
01508 
01509 //  mAgenda->viewport()->update();
01510 //  mAllDayAgenda->viewport()->update();
01511 
01512 // make invalid
01513   deleteSelectedDateTime();
01514 
01515   if( !somethingReselected ) {
01516     emit incidenceSelected( 0 );
01517   }
01518 
01519 //  kdDebug(5850) << "Fill Agenda done" << endl;
01520 }
01521 
01522 void KOAgendaView::clearView()
01523 {
01524 //  kdDebug(5850) << "ClearView" << endl;
01525   mAllDayAgenda->clear();
01526   mAgenda->clear();
01527 }
01528 
01529 CalPrinterBase::PrintType KOAgendaView::printType()
01530 {
01531   if ( currentDateCount() == 1 ) return CalPrinterBase::Day;
01532   else return CalPrinterBase::Week;
01533 }
01534 
01535 void KOAgendaView::updateEventIndicatorTop( int newY )
01536 {
01537   uint i;
01538   for( i = 0; i < mMinY.size(); ++i ) {
01539     mEventIndicatorTop->enableColumn( i, newY >= mMinY[i] );
01540   }
01541   mEventIndicatorTop->update();
01542 }
01543 
01544 void KOAgendaView::updateEventIndicatorBottom( int newY )
01545 {
01546   uint i;
01547   for( i = 0; i < mMaxY.size(); ++i ) {
01548     mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
01549   }
01550   mEventIndicatorBottom->update();
01551 }
01552 
01553 void KOAgendaView::slotTodoDropped( Todo *todo, const QPoint &gpos, bool allDay )
01554 {
01555   if ( gpos.x()<0 || gpos.y()<0 ) return;
01556   QDate day = mSelectedDates[gpos.x()];
01557   QTime time = mAgenda->gyToTime( gpos.y() );
01558   QDateTime newTime( day, time );
01559 
01560   if ( todo ) {
01561     Todo *existingTodo = calendar()->todo( todo->uid() );
01562     if ( existingTodo ) {
01563       kdDebug(5850) << "Drop existing Todo" << endl;
01564       Todo *oldTodo = existingTodo->clone();
01565       if ( mChanger && mChanger->beginChange( existingTodo ) ) {
01566         existingTodo->setDtDue( newTime );
01567         existingTodo->setFloats( allDay );
01568         existingTodo->setHasDueDate( true );
01569         mChanger->changeIncidence( oldTodo, existingTodo );
01570         mChanger->endChange( existingTodo );
01571       } else {
01572         KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
01573                             "because it cannot be locked.") );
01574       }
01575       delete oldTodo;
01576     } else {
01577       kdDebug(5850) << "Drop new Todo" << endl;
01578       todo->setDtDue( newTime );
01579       todo->setFloats( allDay );
01580       todo->setHasDueDate( true );
01581       if ( !mChanger->addIncidence( todo, this ) ) {
01582         KODialogManager::errorSaveIncidence( this, todo );
01583       }
01584     }
01585   }
01586 }
01587 
01588 void KOAgendaView::startDrag( Incidence *incidence )
01589 {
01590 #ifndef KORG_NODND
01591   DndFactory factory( calendar() );
01592   ICalDrag *vd = factory.createDrag( incidence, this );
01593   if ( vd->drag() ) {
01594     kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl;
01595   }
01596 #endif
01597 }
01598 
01599 void KOAgendaView::readSettings()
01600 {
01601   readSettings(KOGlobals::self()->config());
01602 }
01603 
01604 void KOAgendaView::readSettings(KConfig *config)
01605 {
01606 //  kdDebug(5850) << "KOAgendaView::readSettings()" << endl;
01607 
01608   config->setGroup("Views");
01609 
01610 #ifndef KORG_NOSPLITTER
01611   QValueList<int> sizes = config->readIntListEntry("Separator AgendaView");
01612   if (sizes.count() == 2) {
01613     mSplitterAgenda->setSizes(sizes);
01614   }
01615 #endif
01616 
01617   updateConfig();
01618 }
01619 
01620 void KOAgendaView::writeSettings(KConfig *config)
01621 {
01622 //  kdDebug(5850) << "KOAgendaView::writeSettings()" << endl;
01623 
01624   config->setGroup("Views");
01625 
01626 #ifndef KORG_NOSPLITTER
01627   QValueList<int> list = mSplitterAgenda->sizes();
01628   config->writeEntry("Separator AgendaView",list);
01629 #endif
01630 }
01631 
01632 void KOAgendaView::setHolidayMasks()
01633 {
01634   mHolidayMask.resize( mSelectedDates.count() + 1 );
01635 
01636   for( uint i = 0; i < mSelectedDates.count(); ++i ) {
01637     mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
01638   }
01639 
01640   // Store the information about the day before the visible area (needed for
01641   // overnight working hours) in the last bit of the mask:
01642   bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
01643   mHolidayMask[ mSelectedDates.count() ] = showDay;
01644 
01645   mAgenda->setHolidayMask( &mHolidayMask );
01646   mAllDayAgenda->setHolidayMask( &mHolidayMask );
01647 }
01648 
01649 void KOAgendaView::setContentsPos( int y )
01650 {
01651   mAgenda->setContentsPos( 0, y );
01652 }
01653 
01654 void KOAgendaView::setExpandedButton( bool expanded )
01655 {
01656   if ( !mExpandButton ) return;
01657 
01658   if ( expanded ) {
01659     mExpandButton->setPixmap( mExpandedPixmap );
01660   } else {
01661     mExpandButton->setPixmap( mNotExpandedPixmap );
01662   }
01663 }
01664 
01665 void KOAgendaView::clearSelection()
01666 {
01667   mAgenda->deselectItem();
01668   mAllDayAgenda->deselectItem();
01669 }
01670 
01671 void KOAgendaView::newTimeSpanSelectedAllDay( const QPoint &start, const QPoint &end )
01672 {
01673   newTimeSpanSelected( start, end );
01674   mTimeSpanInAllDay = true;
01675 }
01676 
01677 void KOAgendaView::newTimeSpanSelected( const QPoint &start, const QPoint &end )
01678 {
01679   if (!mSelectedDates.count()) return;
01680 
01681   mTimeSpanInAllDay = false;
01682 
01683   QDate dayStart = mSelectedDates[start.x()];
01684   QDate dayEnd = mSelectedDates[end.x()];
01685 
01686   QTime timeStart = mAgenda->gyToTime(start.y());
01687   QTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
01688 
01689   QDateTime dtStart(dayStart,timeStart);
01690   QDateTime dtEnd(dayEnd,timeEnd);
01691 
01692   mTimeSpanBegin = dtStart;
01693   mTimeSpanEnd = dtEnd;
01694 }
01695 
01696 void KOAgendaView::deleteSelectedDateTime()
01697 {
01698   mTimeSpanBegin.setDate(QDate());
01699   mTimeSpanEnd.setDate(QDate());
01700   mTimeSpanInAllDay = false;
01701 }
01702 
01703 void KOAgendaView::setTypeAheadReceiver( QObject *o )
01704 {
01705   mAgenda->setTypeAheadReceiver( o );
01706   mAllDayAgenda->setTypeAheadReceiver( o );
01707 }
01708 
01709 void KOAgendaView::finishTypeAhead()
01710 {
01711   mAgenda->finishTypeAhead();
01712   mAllDayAgenda->finishTypeAhead();
01713 }
01714 
01715 void KOAgendaView::removeIncidence( Incidence *incidence )
01716 {
01717   mAgenda->removeIncidence( incidence );
01718   mAllDayAgenda->removeIncidence( incidence );
01719 }
01720 
01721 void KOAgendaView::updateEventIndicators()
01722 {
01723   mMinY = mAgenda->minContentsY();
01724   mMaxY = mAgenda->maxContentsY();
01725 
01726   mAgenda->checkScrollBoundaries();
01727   updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
01728   updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
01729 }
01730 
01731 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
01732 {
01733   mChanger = changer;
01734   mAgenda->setIncidenceChanger( changer );
01735   mAllDayAgenda->setIncidenceChanger( changer );
01736 }
01737 
01738 void KOAgendaView::clearTimeSpanSelection()
01739 {
01740   mAgenda->clearSelection();
01741   mAllDayAgenda->clearSelection();
01742   deleteSelectedDateTime();
01743 }
01744 
01745 void KOAgendaView::setResource(KCal::ResourceCalendar * res, const QString & subResource)
01746 {
01747   mResource = res;
01748   mSubResource = subResource;
01749 }
01750 
01751 bool KOAgendaView::filterByResource(Incidence * incidence)
01752 {
01753   if ( !mResource )
01754     return true;
01755   CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() );
01756   if ( !calRes )
01757     return true;
01758   if ( calRes->resource( incidence ) != mResource )
01759     return false;
01760   if ( !mSubResource.isEmpty() ) {
01761     if ( mResource->subresourceIdentifier( incidence ) != mSubResource )
01762       return false;
01763   }
01764   return true;
01765 }
KDE Home | KDE Accessibility Home | Description of Access Keys