korganizer

koagenda.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     Marcus Bains line.
00007     Copyright (c) 2001 Ali Rahimi
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017     GNU General Public License for more details.
00018 
00019     You should have received a copy of the GNU General Public License
00020     along with this program; if not, write to the Free Software
00021     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00022 
00023     As a special exception, permission is given to link this program
00024     with any edition of Qt, and distribute the resulting executable,
00025     without including the source code for Qt in the source distribution.
00026 */
00027 #include <assert.h>
00028 
00029 #include <qintdict.h>
00030 #include <qdatetime.h>
00031 #include <qapplication.h>
00032 #include <qpopupmenu.h>
00033 #include <qcursor.h>
00034 #include <qpainter.h>
00035 #include <qlabel.h>
00036 
00037 #include <kdebug.h>
00038 #include <klocale.h>
00039 #include <kiconloader.h>
00040 #include <kglobal.h>
00041 #include <kmessagebox.h>
00042 
00043 #include "koagendaitem.h"
00044 #include "koprefs.h"
00045 #include "koglobals.h"
00046 #include "komessagebox.h"
00047 #include "incidencechanger.h"
00048 #include "kohelper.h"
00049 
00050 #include "koagenda.h"
00051 #include "koagenda.moc"
00052 #include <korganizer/baseview.h>
00053 
00054 #include <libkcal/event.h>
00055 #include <libkcal/todo.h>
00056 #include <libkcal/dndfactory.h>
00057 #include <libkcal/icaldrag.h>
00058 #include <libkcal/vcaldrag.h>
00059 #include <libkcal/calendar.h>
00060 #include <libkcal/calendarresources.h>
00061 #include <math.h>
00062 
00064 MarcusBains::MarcusBains(KOAgenda *_agenda,const char *name)
00065     : QFrame(_agenda->viewport(),name), agenda(_agenda)
00066 {
00067   setLineWidth(0);
00068   setMargin(0);
00069   setBackgroundColor(Qt::red);
00070   minutes = new QTimer(this);
00071   connect(minutes, SIGNAL(timeout()), this, SLOT(updateLocation()));
00072   minutes->start(0, true);
00073 
00074   mTimeBox = new QLabel(this);
00075   mTimeBox->setAlignment(Qt::AlignRight | Qt::AlignBottom);
00076   QPalette pal = mTimeBox->palette();
00077   pal.setColor(QColorGroup::Foreground, Qt::red);
00078   mTimeBox->setPalette(pal);
00079   mTimeBox->setAutoMask(true);
00080 
00081   agenda->addChild(mTimeBox);
00082 
00083   mOldTime = QTime( 0, 0 );
00084   mOldToday = -1;
00085 }
00086 
00087 MarcusBains::~MarcusBains()
00088 {
00089   delete minutes;
00090 }
00091 
00092 int MarcusBains::todayColumn()
00093 {
00094   QDate currentDate = QDate::currentDate();
00095 
00096   DateList dateList = agenda->dateList();
00097   DateList::ConstIterator it;
00098   int col = 0;
00099   for(it = dateList.begin(); it != dateList.end(); ++it) {
00100     if((*it) == currentDate)
00101       return KOGlobals::self()->reverseLayout() ?
00102              agenda->columns() - 1 - col : col;
00103     ++col;
00104   }
00105 
00106   return -1;
00107 }
00108 
00109 void MarcusBains::updateLocation()
00110 {
00111   updateLocationRecalc();
00112 }
00113 
00114 void MarcusBains::updateLocationRecalc( bool recalculate )
00115 {
00116   QTime tim = QTime::currentTime();
00117   if((tim.hour() == 0) && (mOldTime.hour()==23))
00118     recalculate = true;
00119 
00120   int mins = tim.hour()*60 + tim.minute();
00121   int minutesPerCell = 24 * 60 / agenda->rows();
00122   int y = int( mins * agenda->gridSpacingY() / minutesPerCell );
00123   int today = recalculate ? todayColumn() : mOldToday;
00124   int x = int( agenda->gridSpacingX() * today );
00125 
00126   mOldTime = tim;
00127   mOldToday = today;
00128 
00129   bool hideIt = !( KOPrefs::instance()->mMarcusBainsEnabled );
00130 
00131   if ( !isHidden() && ( hideIt || ( today < 0 ) ) ) {
00132     hide();
00133     mTimeBox->hide();
00134     return;
00135   }
00136 
00137   if ( isHidden() && !hideIt ) {
00138     show();
00139     mTimeBox->show();
00140   }
00141 
00142   if ( recalculate ) setFixedSize( int( agenda->gridSpacingX() ), 1 );
00143   agenda->moveChild( this, x, y );
00144   raise();
00145 
00146   if(recalculate)
00147     mTimeBox->setFont(KOPrefs::instance()->mMarcusBainsFont);
00148 
00149   QString timeStr = KGlobal::locale()->formatTime(tim, KOPrefs::instance()->mMarcusBainsShowSeconds);
00150   QFontMetrics fm = fontMetrics();
00151   mTimeBox->setText( timeStr );
00152   QSize sz( fm.width( timeStr + ' ' ), fm.height() );
00153   mTimeBox->setFixedSize( sz );
00154 
00155   if (y-mTimeBox->height()>=0) y-=mTimeBox->height(); else y++;
00156   if (x-mTimeBox->width()+agenda->gridSpacingX() > 0)
00157     x += int( agenda->gridSpacingX() - mTimeBox->width() - 1 );
00158   else x++;
00159   agenda->moveChild(mTimeBox,x,y);
00160   mTimeBox->raise();
00161   mTimeBox->setAutoMask(true);
00162 
00163   minutes->start(1000,true);
00164 }
00165 
00166 
00168 
00169 
00170 /*
00171   Create an agenda widget with rows rows and columns columns.
00172 */
00173 KOAgenda::KOAgenda( int columns, int rows, int rowSize, QWidget *parent,
00174                     const char *name, WFlags f )
00175   : QScrollView( parent, name, f ), mChanger( 0 )
00176 {
00177   mColumns = columns;
00178   mRows = rows;
00179   mGridSpacingY = rowSize;
00180   if ( mGridSpacingY < 4 || mGridSpacingY > 30 ) {
00181     mGridSpacingY = 10;
00182   }
00183 
00184   mAllDayMode = false;
00185 
00186   init();
00187 
00188   viewport()->setMouseTracking(true);
00189 }
00190 
00191 /*
00192   Create an agenda widget with columns columns and one row. This is used for
00193   all-day events.
00194 */
00195 KOAgenda::KOAgenda( int columns, QWidget *parent, const char *name, WFlags f )
00196   : QScrollView( parent, name, f )
00197 {
00198   mColumns = columns;
00199   mRows = 1;
00200   mGridSpacingY = 24;
00201   mAllDayMode = true;
00202   setVScrollBarMode( AlwaysOff );
00203 
00204   init();
00205 }
00206 
00207 
00208 KOAgenda::~KOAgenda()
00209 {
00210   delete mMarcusBains;
00211 }
00212 
00213 
00214 Incidence *KOAgenda::selectedIncidence() const
00215 {
00216   return ( mSelectedItem ? mSelectedItem->incidence() : 0 );
00217 }
00218 
00219 
00220 QDate KOAgenda::selectedIncidenceDate() const
00221 {
00222   return ( mSelectedItem ? mSelectedItem->itemDate() : QDate() );
00223 }
00224 
00225 const QString KOAgenda::lastSelectedUid() const
00226 {
00227   return mSelectedUid;
00228 }
00229 
00230 
00231 void KOAgenda::init()
00232 {
00233   mGridSpacingX = 100;
00234   mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
00235   if ( mDesiredGridSpacingY < 4 || mDesiredGridSpacingY > 30 ) {
00236     mDesiredGridSpacingY = 10;
00237   }
00238 
00239  // make sure that there are not more than 24 per day
00240   mGridSpacingY = (double)height() / (double)mRows;
00241   if ( mGridSpacingY < mDesiredGridSpacingY ) {
00242     mGridSpacingY = mDesiredGridSpacingY;
00243   }
00244 
00245   mResizeBorderWidth = 8;
00246   mScrollBorderWidth = 8;
00247   mScrollDelay = 30;
00248   mScrollOffset = 10;
00249 
00250   enableClipper( true );
00251 
00252   // Grab key strokes for keyboard navigation of agenda. Seems to have no
00253   // effect. Has to be fixed.
00254   setFocusPolicy( WheelFocus );
00255 
00256   connect( &mScrollUpTimer, SIGNAL( timeout() ), SLOT( scrollUp() ) );
00257   connect( &mScrollDownTimer, SIGNAL( timeout() ), SLOT( scrollDown() ) );
00258 
00259   mStartCell = QPoint( 0, 0 );
00260   mEndCell = QPoint( 0, 0 );
00261 
00262   mHasSelection = false;
00263   mSelectionStartPoint = QPoint( 0, 0 );
00264   mSelectionStartCell = QPoint( 0, 0 );
00265   mSelectionEndCell = QPoint( 0, 0 );
00266 
00267   mOldLowerScrollValue = -1;
00268   mOldUpperScrollValue = -1;
00269 
00270   mClickedItem = 0;
00271 
00272   mActionItem = 0;
00273   mActionType = NOP;
00274   mItemMoved = false;
00275 
00276   mSelectedItem = 0;
00277   mSelectedUid = QString::null;
00278 
00279   setAcceptDrops( true );
00280   installEventFilter( this );
00281   mItems.setAutoDelete( true );
00282   mItemsToDelete.setAutoDelete( true );
00283 
00284   resizeContents( int( mGridSpacingX * mColumns ),
00285                   int( mGridSpacingY * mRows ) );
00286 
00287   viewport()->update();
00288   viewport()->setBackgroundMode( NoBackground );
00289   viewport()->setFocusPolicy( WheelFocus );
00290 
00291   setMinimumSize( 30, int( mGridSpacingY + 1 ) );
00292 //  setMaximumHeight(mGridSpacingY * mRows + 5);
00293 
00294   // Disable horizontal scrollbar. This is a hack. The geometry should be
00295   // controlled in a way that the contents horizontally always fits. Then it is
00296   // not necessary to turn off the scrollbar.
00297   setHScrollBarMode( AlwaysOff );
00298 
00299   setStartTime( KOPrefs::instance()->mDayBegins.time() );
00300 
00301   calculateWorkingHours();
00302 
00303   connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ),
00304            SLOT( checkScrollBoundaries( int ) ) );
00305 
00306   // Create the Marcus Bains line.
00307   if( mAllDayMode ) {
00308     mMarcusBains = 0;
00309   } else {
00310     mMarcusBains = new MarcusBains( this );
00311     addChild( mMarcusBains );
00312   }
00313 
00314   mTypeAhead = false;
00315   mTypeAheadReceiver = 0;
00316 
00317   mReturnPressed = false;
00318 }
00319 
00320 
00321 void KOAgenda::clear()
00322 {
00323 //  kdDebug(5850) << "KOAgenda::clear()" << endl;
00324 
00325   KOAgendaItem *item;
00326   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
00327     removeChild( item );
00328   }
00329   mItems.clear();
00330   mItemsToDelete.clear();
00331 
00332   mSelectedItem = 0;
00333 
00334   clearSelection();
00335 }
00336 
00337 
00338 void KOAgenda::clearSelection()
00339 {
00340   mHasSelection = false;
00341   mActionType = NOP;
00342   updateContents();
00343 }
00344 
00345 void KOAgenda::marcus_bains()
00346 {
00347   if(mMarcusBains) mMarcusBains->updateLocationRecalc( true );
00348 }
00349 
00350 
00351 void KOAgenda::changeColumns(int columns)
00352 {
00353   if (columns == 0) {
00354     kdDebug(5850) << "KOAgenda::changeColumns() called with argument 0" << endl;
00355     return;
00356   }
00357 
00358   clear();
00359   mColumns = columns;
00360 //  setMinimumSize(mColumns * 10, mGridSpacingY + 1);
00361 //  init();
00362 //  update();
00363 
00364   QResizeEvent event( size(), size() );
00365 
00366   QApplication::sendEvent( this, &event );
00367 }
00368 
00369 /*
00370   This is the eventFilter function, which gets all events from the KOAgendaItems
00371   contained in the agenda. It has to handle moving and resizing for all items.
00372 */
00373 bool KOAgenda::eventFilter ( QObject *object, QEvent *event )
00374 {
00375 //  kdDebug(5850) << "KOAgenda::eventFilter() " << int( event->type() ) << endl;
00376 
00377   switch( event->type() ) {
00378     case QEvent::MouseButtonPress:
00379     case QEvent::MouseButtonDblClick:
00380     case QEvent::MouseButtonRelease:
00381     case QEvent::MouseMove:
00382       return eventFilter_mouse( object, static_cast<QMouseEvent *>( event ) );
00383 #ifndef QT_NO_WHEELEVENT
00384     case QEvent::Wheel:
00385       return eventFilter_wheel( object, static_cast<QWheelEvent *>( event ) );
00386 #endif
00387     case QEvent::KeyPress:
00388     case QEvent::KeyRelease:
00389       return eventFilter_key( object, static_cast<QKeyEvent *>( event ) );
00390 
00391     case ( QEvent::Leave ):
00392       if ( !mActionItem )
00393         setCursor( arrowCursor );
00394       if ( object == viewport() )
00395         emit leaveAgenda();
00396       return true;
00397 
00398     case QEvent::Enter:
00399       emit enterAgenda();
00400       return QScrollView::eventFilter( object, event );
00401 
00402 #ifndef KORG_NODND
00403     case QEvent::DragEnter:
00404     case QEvent::DragMove:
00405     case QEvent::DragLeave:
00406     case QEvent::Drop:
00407  //   case QEvent::DragResponse:
00408       return eventFilter_drag(object, static_cast<QDropEvent*>(event));
00409 #endif
00410 
00411     default:
00412       return QScrollView::eventFilter( object, event );
00413   }
00414 }
00415 
00416 bool KOAgenda::eventFilter_drag( QObject *object, QDropEvent *de )
00417 {
00418 #ifndef KORG_NODND
00419   QPoint viewportPos;
00420   if ( object != viewport() && object != this ) {
00421     viewportPos = static_cast<QWidget *>( object )->mapToParent( de->pos() );
00422   } else {
00423     viewportPos = de->pos();
00424   }
00425 
00426   switch ( de->type() ) {
00427     case QEvent::DragEnter:
00428     case QEvent::DragMove:
00429       if ( ICalDrag::canDecode( de ) || VCalDrag::canDecode( de ) ) {
00430 
00431         DndFactory factory( mCalendar );
00432         Todo *todo = factory.createDropTodo( de );
00433         if ( todo ) {
00434           de->accept();
00435           delete todo;
00436         } else {
00437           de->ignore();
00438         }
00439         return true;
00440       } else return false;
00441       break;
00442     case QEvent::DragLeave:
00443       return false;
00444       break;
00445     case QEvent::Drop:
00446       {
00447         if ( !ICalDrag::canDecode( de ) && !VCalDrag::canDecode( de ) ) {
00448           return false;
00449         }
00450 
00451         DndFactory factory( mCalendar );
00452         Todo *todo = factory.createDropTodo( de );
00453 
00454         if ( todo ) {
00455           de->acceptAction();
00456           QPoint pos;
00457           // FIXME: This is a bad hack, as the viewportToContents seems to be off by
00458           // 2000 (which is the left upper corner of the viewport). It works correctly
00459           // for agendaItems.
00460           if ( object == this  ) {
00461             pos = viewportPos + QPoint( contentsX(), contentsY() );
00462           } else {
00463             pos = viewportToContents( viewportPos );
00464           }
00465           QPoint gpos = contentsToGrid( pos );
00466           emit droppedToDo( todo, gpos, mAllDayMode );
00467           return true;
00468         }
00469       }
00470       break;
00471 
00472     case QEvent::DragResponse:
00473     default:
00474       break;
00475   }
00476 #endif
00477 
00478   return false;
00479 }
00480 
00481 bool KOAgenda::eventFilter_key( QObject *, QKeyEvent *ke )
00482 {
00483   // kdDebug(5850) << "KOAgenda::eventFilter_key() " << ke->type() << endl;
00484 
00485   // If Return is pressed bring up an editor for the current selected time span.
00486   if ( ke->key() == Key_Return ) {
00487     if ( ke->type() == QEvent::KeyPress ) mReturnPressed = true;
00488     else if ( ke->type() == QEvent::KeyRelease ) {
00489       if ( mReturnPressed ) {
00490         emitNewEventForSelection();
00491         mReturnPressed = false;
00492         return true;
00493       } else {
00494         mReturnPressed = false;
00495       }
00496     }
00497   }
00498 
00499   // Ignore all input that does not produce any output
00500   if ( ke->text().isEmpty() ) return false;
00501 
00502   if ( ke->type() == QEvent::KeyPress || ke->type() == QEvent::KeyRelease ) {
00503     switch ( ke->key() ) {
00504       case Key_Escape:
00505       case Key_Return:
00506       case Key_Enter:
00507       case Key_Tab:
00508       case Key_Backtab:
00509       case Key_Left:
00510       case Key_Right:
00511       case Key_Up:
00512       case Key_Down:
00513       case Key_Backspace:
00514       case Key_Delete:
00515       case Key_Prior:
00516       case Key_Next:
00517       case Key_Home:
00518       case Key_End:
00519       case Key_Control:
00520       case Key_Meta:
00521       case Key_Alt:
00522         break;
00523       default:
00524         mTypeAheadEvents.append( new QKeyEvent( ke->type(), ke->key(),
00525                                                 ke->ascii(), ke->state(),
00526                                                 ke->text(), ke->isAutoRepeat(),
00527                                                 ke->count() ) );
00528         if ( !mTypeAhead ) {
00529           mTypeAhead = true;
00530           emitNewEventForSelection();
00531         }
00532         return true;
00533     }
00534   }
00535   return false;
00536 }
00537 
00538 void KOAgenda::emitNewEventForSelection()
00539 {
00540   emit newEventSignal();
00541 }
00542 
00543 void KOAgenda::finishTypeAhead()
00544 {
00545 //  kdDebug(5850) << "KOAgenda::finishTypeAhead()" << endl;
00546   if ( typeAheadReceiver() ) {
00547     for( QEvent *e = mTypeAheadEvents.first(); e;
00548          e = mTypeAheadEvents.next() ) {
00549 //      kdDebug(5850) << "sendEvent() " << int( typeAheadReceiver() ) << endl;
00550       QApplication::sendEvent( typeAheadReceiver(), e );
00551     }
00552   }
00553   mTypeAheadEvents.clear();
00554   mTypeAhead = false;
00555 }
00556 #ifndef QT_NO_WHEELEVENT
00557 bool KOAgenda::eventFilter_wheel ( QObject *object, QWheelEvent *e )
00558 {
00559   QPoint viewportPos;
00560   bool accepted=false;
00561   if  ( ( e->state() & ShiftButton) == ShiftButton ) {
00562     if ( object != viewport() ) {
00563       viewportPos = ( (QWidget *) object )->mapToParent( e->pos() );
00564     } else {
00565       viewportPos = e->pos();
00566     }
00567     //kdDebug(5850)<< "KOAgenda::eventFilter_wheel: type:"<<
00568     //  e->type()<<" delta: "<< e->delta()<< endl;
00569     emit zoomView( -e->delta() ,
00570       contentsToGrid( viewportToContents( viewportPos ) ),
00571       Qt::Horizontal );
00572     accepted=true;
00573   }
00574 
00575   if  ( ( e->state() & ControlButton ) == ControlButton ){
00576     if ( object != viewport() ) {
00577       viewportPos = ( (QWidget *)object )->mapToParent( e->pos() );
00578     } else {
00579       viewportPos = e->pos();
00580     }
00581     emit zoomView( -e->delta() ,
00582       contentsToGrid( viewportToContents( viewportPos ) ),
00583       Qt::Vertical );
00584     emit mousePosSignal(gridToContents(contentsToGrid(viewportToContents( viewportPos ))));
00585     accepted=true;
00586   }
00587   if (accepted ) e->accept();
00588   return accepted;
00589 }
00590 #endif
00591 bool KOAgenda::eventFilter_mouse(QObject *object, QMouseEvent *me)
00592 {
00593   QPoint viewportPos;
00594   if (object != viewport()) {
00595     viewportPos = ((QWidget *)object)->mapToParent(me->pos());
00596   } else {
00597     viewportPos = me->pos();
00598   }
00599 
00600   switch (me->type())  {
00601     case QEvent::MouseButtonPress:
00602 //        kdDebug(5850) << "koagenda: filtered button press" << endl;
00603       if (object != viewport()) {
00604         if (me->button() == RightButton) {
00605           mClickedItem = dynamic_cast<KOAgendaItem *>(object);
00606           if (mClickedItem) {
00607             selectItem(mClickedItem);
00608             emit showIncidencePopupSignal( mCalendar,
00609                                            mClickedItem->incidence(),
00610                                            mClickedItem->itemDate() );
00611           }
00612         } else {
00613           KOAgendaItem* item = dynamic_cast<KOAgendaItem *>(object);
00614           if (item) {
00615             Incidence *incidence = item->incidence();
00616             if ( incidence->isReadOnly() ) {
00617               mActionItem = 0;
00618             } else {
00619               mActionItem = item;
00620               startItemAction(viewportPos);
00621             }
00622             // Warning: do selectItem() as late as possible, since all
00623             // sorts of things happen during this call. Some can lead to
00624             // this filter being run again and mActionItem being set to
00625             // null.
00626             selectItem( item );
00627           }
00628         }
00629       } else {
00630         if (me->button() == RightButton)
00631         {
00632           // if mouse pointer is not in selection, select the cell below the cursor
00633           QPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
00634           if ( !ptInSelection( gpos ) ) {
00635             mSelectionStartCell = gpos;
00636             mSelectionEndCell = gpos;
00637             mHasSelection = true;
00638             emit newStartSelectSignal();
00639             emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
00640             updateContents();
00641           }
00642           showNewEventPopupSignal();
00643         }
00644         else
00645         {
00646           // if mouse pointer is in selection, don't change selection
00647           QPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
00648           if ( !ptInSelection( gpos ) ) {
00649             selectItem(0);
00650             mActionItem = 0;
00651             setCursor(arrowCursor);
00652             startSelectAction(viewportPos);
00653           }
00654         }
00655       }
00656       break;
00657 
00658     case QEvent::MouseButtonRelease:
00659       if (mActionItem) {
00660         endItemAction();
00661       } else if ( mActionType == SELECT ) {
00662         endSelectAction( viewportPos );
00663       }
00664       // This nasty gridToContents(contentsToGrid(..)) is needed to
00665       // avoid an offset of a few pixels. Don't ask me why...
00666       emit mousePosSignal( gridToContents(contentsToGrid(
00667                            viewportToContents( viewportPos ) ) ));
00668       break;
00669 
00670     case QEvent::MouseMove: {
00671       // This nasty gridToContents(contentsToGrid(..)) is needed to
00672       // avoid an offset of a few pixels. Don't ask me why...
00673       QPoint indicatorPos = gridToContents(contentsToGrid(
00674                                           viewportToContents( viewportPos )));
00675       if (object != viewport()) {
00676         KOAgendaItem *moveItem = dynamic_cast<KOAgendaItem *>(object);
00677         if (moveItem && !moveItem->incidence()->isReadOnly() ) {
00678           if (!mActionItem)
00679             setNoActionCursor(moveItem,viewportPos);
00680           else {
00681             performItemAction(viewportPos);
00682 
00683             if ( mActionType == MOVE ) {
00684               // show cursor at the current begin of the item
00685               KOAgendaItem *firstItem = mActionItem->firstMultiItem();
00686               if (!firstItem) firstItem = mActionItem;
00687               indicatorPos = gridToContents( QPoint( firstItem->cellXLeft(),
00688                                                      firstItem->cellYTop() ) );
00689 
00690             } else if ( mActionType == RESIZEBOTTOM ) {
00691               // RESIZETOP is handled correctly, only resizebottom works differently
00692               indicatorPos = gridToContents( QPoint( mActionItem->cellXLeft(),
00693                                                      mActionItem->cellYBottom()+1 ) );
00694             }
00695 
00696           } // If we have an action item
00697         } // If move item && !read only
00698       } else {
00699           if ( mActionType == SELECT ) {
00700             performSelectAction( viewportPos );
00701 
00702             // show cursor at end of timespan
00703             if ( ((mStartCell.y() < mEndCell.y()) && (mEndCell.x() >= mStartCell.x())) ||
00704                  (mEndCell.x() > mStartCell.x()) )
00705               indicatorPos = gridToContents( QPoint(mEndCell.x(), mEndCell.y()+1) );
00706             else
00707               indicatorPos = gridToContents( mEndCell );
00708           }
00709         }
00710       emit mousePosSignal( indicatorPos );
00711       break; }
00712 
00713     case QEvent::MouseButtonDblClick:
00714       if (object == viewport()) {
00715         selectItem(0);
00716         emit newEventSignal();
00717       } else {
00718         KOAgendaItem *doubleClickedItem = dynamic_cast<KOAgendaItem *>(object);
00719         if (doubleClickedItem) {
00720           selectItem(doubleClickedItem);
00721           emit editIncidenceSignal(doubleClickedItem->incidence());
00722         }
00723       }
00724       break;
00725 
00726     default:
00727       break;
00728   }
00729 
00730   return true;
00731 }
00732 
00733 bool KOAgenda::ptInSelection( QPoint gpos ) const
00734 {
00735   if ( !mHasSelection ) {
00736     return false;
00737   } else if ( gpos.x()<mSelectionStartCell.x() || gpos.x()>mSelectionEndCell.x() ) {
00738     return false;
00739   } else if ( (gpos.x()==mSelectionStartCell.x()) && (gpos.y()<mSelectionStartCell.y()) ) {
00740     return false;
00741   } else if ( (gpos.x()==mSelectionEndCell.x()) && (gpos.y()>mSelectionEndCell.y()) ) {
00742     return false;
00743   }
00744   return true;
00745 }
00746 
00747 void KOAgenda::startSelectAction( const QPoint &viewportPos )
00748 {
00749   emit newStartSelectSignal();
00750 
00751   mActionType = SELECT;
00752   mSelectionStartPoint = viewportPos;
00753   mHasSelection = true;
00754 
00755   QPoint pos = viewportToContents( viewportPos );
00756   QPoint gpos = contentsToGrid( pos );
00757 
00758   // Store new selection
00759   mStartCell = gpos;
00760   mEndCell = gpos;
00761   mSelectionStartCell = gpos;
00762   mSelectionEndCell = gpos;
00763 
00764   updateContents();
00765 }
00766 
00767 void KOAgenda::performSelectAction(const QPoint& viewportPos)
00768 {
00769   QPoint pos = viewportToContents( viewportPos );
00770   QPoint gpos = contentsToGrid( pos );
00771 
00772   QPoint clipperPos = clipper()->
00773                       mapFromGlobal(viewport()->mapToGlobal(viewportPos));
00774 
00775   // Scroll if cursor was moved to upper or lower end of agenda.
00776   if (clipperPos.y() < mScrollBorderWidth) {
00777     mScrollUpTimer.start(mScrollDelay);
00778   } else if (visibleHeight() - clipperPos.y() <
00779              mScrollBorderWidth) {
00780     mScrollDownTimer.start(mScrollDelay);
00781   } else {
00782     mScrollUpTimer.stop();
00783     mScrollDownTimer.stop();
00784   }
00785 
00786   if ( gpos != mEndCell ) {
00787     mEndCell = gpos;
00788     if ( mStartCell.x()>mEndCell.x() ||
00789          ( mStartCell.x()==mEndCell.x() && mStartCell.y()>mEndCell.y() ) ) {
00790       // backward selection
00791       mSelectionStartCell = mEndCell;
00792       mSelectionEndCell = mStartCell;
00793     } else {
00794       mSelectionStartCell = mStartCell;
00795       mSelectionEndCell = mEndCell;
00796     }
00797 
00798     updateContents();
00799   }
00800 }
00801 
00802 void KOAgenda::endSelectAction( const QPoint &currentPos )
00803 {
00804   mScrollUpTimer.stop();
00805   mScrollDownTimer.stop();
00806 
00807   mActionType = NOP;
00808 
00809   emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
00810 
00811   if ( KOPrefs::instance()->mSelectionStartsEditor ) {
00812     if ( ( mSelectionStartPoint - currentPos ).manhattanLength() >
00813          QApplication::startDragDistance() ) {
00814        emitNewEventForSelection();
00815     }
00816   }
00817 }
00818 
00819 KOAgenda::MouseActionType KOAgenda::isInResizeArea( bool horizontal,
00820     const QPoint &pos, KOAgendaItem*item )
00821 {
00822   if (!item) return NOP;
00823   QPoint gridpos = contentsToGrid( pos );
00824   QPoint contpos = gridToContents( gridpos +
00825       QPoint( (KOGlobals::self()->reverseLayout())?1:0, 0 ) );
00826 
00827 //kdDebug(5850)<<"contpos="<<contpos<<", pos="<<pos<<", gpos="<<gpos<<endl;
00828 //kdDebug(5850)<<"clXLeft="<<clXLeft<<", clXRight="<<clXRight<<endl;
00829 
00830   if ( horizontal ) {
00831     int clXLeft = item->cellXLeft();
00832     int clXRight = item->cellXRight();
00833     if ( KOGlobals::self()->reverseLayout() ) {
00834       int tmp = clXLeft;
00835       clXLeft = clXRight;
00836       clXRight = tmp;
00837     }
00838     int gridDistanceX = int( pos.x() - contpos.x() );
00839     if (gridDistanceX < mResizeBorderWidth && clXLeft == gridpos.x() ) {
00840       if ( KOGlobals::self()->reverseLayout() ) return RESIZERIGHT;
00841       else return RESIZELEFT;
00842     } else if ((mGridSpacingX - gridDistanceX) < mResizeBorderWidth &&
00843                clXRight == gridpos.x() ) {
00844       if ( KOGlobals::self()->reverseLayout() ) return RESIZELEFT;
00845       else return RESIZERIGHT;
00846     } else {
00847       return MOVE;
00848     }
00849   } else {
00850     int gridDistanceY = int( pos.y() - contpos.y() );
00851     if (gridDistanceY < mResizeBorderWidth &&
00852         item->cellYTop() == gridpos.y() &&
00853         !item->firstMultiItem() ) {
00854       return RESIZETOP;
00855     } else if ((mGridSpacingY - gridDistanceY) < mResizeBorderWidth &&
00856                item->cellYBottom() == gridpos.y() &&
00857                !item->lastMultiItem() )  {
00858       return RESIZEBOTTOM;
00859     } else {
00860       return MOVE;
00861     }
00862   }
00863 }
00864 
00865 void KOAgenda::startItemAction(const QPoint& viewportPos)
00866 {
00867   QPoint pos = viewportToContents( viewportPos );
00868   mStartCell = contentsToGrid( pos );
00869   mEndCell = mStartCell;
00870 
00871   bool noResize = ( mActionItem->incidence()->type() == "Todo");
00872 
00873   mActionType = MOVE;
00874   if ( !noResize ) {
00875     mActionType = isInResizeArea( mAllDayMode, pos, mActionItem );
00876   }
00877 
00878 
00879   mActionItem->startMove();
00880   setActionCursor( mActionType, true );
00881 }
00882 
00883 void KOAgenda::performItemAction(const QPoint& viewportPos)
00884 {
00885 //  kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
00886 //  QPoint point = viewport()->mapToGlobal(viewportPos);
00887 //  kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
00888 //  point = clipper()->mapFromGlobal(point);
00889 //  kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
00890 //  kdDebug(5850) << "visible height: " << visibleHeight() << endl;
00891   QPoint pos = viewportToContents( viewportPos );
00892 //  kdDebug(5850) << "contents: " << x << "," << y << "\n" << endl;
00893   QPoint gpos = contentsToGrid( pos );
00894   QPoint clipperPos = clipper()->
00895                       mapFromGlobal(viewport()->mapToGlobal(viewportPos));
00896 
00897   // Cursor left active agenda area.
00898   // This starts a drag.
00899   if ( clipperPos.y() < 0 || clipperPos.y() > visibleHeight() ||
00900        clipperPos.x() < 0 || clipperPos.x() > visibleWidth() ) {
00901     if ( mActionType == MOVE ) {
00902       mScrollUpTimer.stop();
00903       mScrollDownTimer.stop();
00904       mActionItem->resetMove();
00905       placeSubCells( mActionItem );
00906       emit startDragSignal( mActionItem->incidence() );
00907       setCursor( arrowCursor );
00908       mActionItem = 0;
00909       mActionType = NOP;
00910       mItemMoved = false;
00911       if ( mItemMoved && mChanger )
00912         mChanger->endChange( mActionItem->incidence() );
00913       return;
00914     }
00915   } else {
00916     setActionCursor( mActionType );
00917   }
00918 
00919   // Scroll if item was moved to upper or lower end of agenda.
00920   if (clipperPos.y() < mScrollBorderWidth) {
00921     mScrollUpTimer.start(mScrollDelay);
00922   } else if (visibleHeight() - clipperPos.y() <
00923              mScrollBorderWidth) {
00924     mScrollDownTimer.start(mScrollDelay);
00925   } else {
00926     mScrollUpTimer.stop();
00927     mScrollDownTimer.stop();
00928   }
00929 
00930   // Move or resize item if necessary
00931   if ( mEndCell != gpos ) {
00932     if ( !mItemMoved ) {
00933       if ( !mChanger || !mChanger->beginChange( mActionItem->incidence() ) ) {
00934         KMessageBox::information( this, i18n("Unable to lock item for "
00935                              "modification. You cannot make any changes."),
00936                              i18n("Locking Failed"), "AgendaLockingFailed" );
00937         mScrollUpTimer.stop();
00938         mScrollDownTimer.stop();
00939         mActionItem->resetMove();
00940         placeSubCells( mActionItem );
00941         setCursor( arrowCursor );
00942         mActionItem = 0;
00943         mActionType = NOP;
00944         mItemMoved = false;
00945         return;
00946       }
00947       mItemMoved = true;
00948     }
00949     mActionItem->raise();
00950     if (mActionType == MOVE) {
00951       // Move all items belonging to a multi item
00952       KOAgendaItem *firstItem = mActionItem->firstMultiItem();
00953       if (!firstItem) firstItem = mActionItem;
00954       KOAgendaItem *lastItem = mActionItem->lastMultiItem();
00955       if (!lastItem) lastItem = mActionItem;
00956       QPoint deltapos = gpos - mEndCell;
00957       KOAgendaItem *moveItem = firstItem;
00958       while (moveItem) {
00959         bool changed=false;
00960         if ( deltapos.x()!=0 ) {
00961           moveItem->moveRelative( deltapos.x(), 0 );
00962           changed=true;
00963         }
00964         // in agenda's all day view don't try to move multi items, since there are none
00965         if ( moveItem==firstItem && !mAllDayMode ) { // is the first item
00966           int newY = deltapos.y() + moveItem->cellYTop();
00967           // If event start moved earlier than 0:00, it starts the previous day
00968           if ( newY<0 ) {
00969             moveItem->expandTop( -moveItem->cellYTop() );
00970             // prepend a new item at ( x-1, rows()+newY to rows() )
00971             KOAgendaItem *newFirst = firstItem->prevMoveItem();
00972             // cell's y values are first and last cell of the bar, so if newY=-1, they need to be the same
00973             if (newFirst) {
00974               newFirst->setCellXY(moveItem->cellXLeft()-1, rows()+newY, rows()-1);
00975               mItems.append( newFirst );
00976               moveItem->resize( int( mGridSpacingX * newFirst->cellWidth() ),
00977                                 int( mGridSpacingY * newFirst->cellHeight() ));
00978               QPoint cpos = gridToContents( QPoint( newFirst->cellXLeft(), newFirst->cellYTop() ) );
00979               addChild( newFirst, cpos.x(), cpos.y() );
00980             } else {
00981               newFirst = insertItem( moveItem->incidence(), moveItem->itemDate(),
00982                 moveItem->cellXLeft()-1, rows()+newY, rows()-1 ) ;
00983             }
00984             if (newFirst) newFirst->show();
00985             moveItem->prependMoveItem(newFirst);
00986             firstItem=newFirst;
00987           } else if ( newY>=rows() ) {
00988             // If event start is moved past 24:00, it starts the next day
00989             // erase current item (i.e. remove it from the multiItem list)
00990             firstItem = moveItem->nextMultiItem();
00991             moveItem->hide();
00992             mItems.take( mItems.find( moveItem ) );
00993             removeChild( moveItem );
00994             mActionItem->removeMoveItem(moveItem);
00995             moveItem=firstItem;
00996             // adjust next day's item
00997             if (moveItem) moveItem->expandTop( rows()-newY );
00998           } else {
00999             moveItem->expandTop(deltapos.y());
01000           }
01001           changed=true;
01002         }
01003         if ( !moveItem->lastMultiItem() && !mAllDayMode ) { // is the last item
01004           int newY = deltapos.y()+moveItem->cellYBottom();
01005           if (newY<0) {
01006             // erase current item
01007             lastItem = moveItem->prevMultiItem();
01008             moveItem->hide();
01009             mItems.take( mItems.find(moveItem) );
01010             removeChild( moveItem );
01011             moveItem->removeMoveItem( moveItem );
01012             moveItem = lastItem;
01013             moveItem->expandBottom(newY+1);
01014           } else if (newY>=rows()) {
01015             moveItem->expandBottom( rows()-moveItem->cellYBottom()-1 );
01016             // append item at ( x+1, 0 to newY-rows() )
01017             KOAgendaItem *newLast = lastItem->nextMoveItem();
01018             if (newLast) {
01019               newLast->setCellXY( moveItem->cellXLeft()+1, 0, newY-rows()-1 );
01020               mItems.append(newLast);
01021               moveItem->resize( int( mGridSpacingX * newLast->cellWidth() ),
01022                                 int( mGridSpacingY * newLast->cellHeight() ));
01023               QPoint cpos = gridToContents( QPoint( newLast->cellXLeft(), newLast->cellYTop() ) ) ;
01024               addChild( newLast, cpos.x(), cpos.y() );
01025             } else {
01026               newLast = insertItem( moveItem->incidence(), moveItem->itemDate(),
01027                 moveItem->cellXLeft()+1, 0, newY-rows()-1 ) ;
01028             }
01029             moveItem->appendMoveItem( newLast );
01030             newLast->show();
01031             lastItem = newLast;
01032           } else {
01033             moveItem->expandBottom( deltapos.y() );
01034           }
01035           changed=true;
01036         }
01037         if (changed) {
01038           adjustItemPosition( moveItem );
01039         }
01040         moveItem = moveItem->nextMultiItem();
01041       }
01042     } else if (mActionType == RESIZETOP) {
01043       if (mEndCell.y() <= mActionItem->cellYBottom()) {
01044         mActionItem->expandTop(gpos.y() - mEndCell.y());
01045         adjustItemPosition( mActionItem );
01046       }
01047     } else if (mActionType == RESIZEBOTTOM) {
01048       if (mEndCell.y() >= mActionItem->cellYTop()) {
01049         mActionItem->expandBottom(gpos.y() - mEndCell.y());
01050         adjustItemPosition( mActionItem );
01051       }
01052     } else if (mActionType == RESIZELEFT) {
01053       if (mEndCell.x() <= mActionItem->cellXRight()) {
01054         mActionItem->expandLeft( gpos.x() - mEndCell.x() );
01055         adjustItemPosition( mActionItem );
01056       }
01057     } else if (mActionType == RESIZERIGHT) {
01058       if (mEndCell.x() >= mActionItem->cellXLeft()) {
01059         mActionItem->expandRight(gpos.x() - mEndCell.x());
01060         adjustItemPosition( mActionItem );
01061       }
01062     }
01063     mEndCell = gpos;
01064   }
01065 }
01066 
01067 void KOAgenda::endItemAction()
01068 {
01069 //  kdDebug(5850) << "KOAgenda::endItemAction() " << endl;
01070   mActionType = NOP;
01071   mScrollUpTimer.stop();
01072   mScrollDownTimer.stop();
01073   setCursor( arrowCursor );
01074   bool multiModify = false;
01075   // FIXME: do the cloning here...
01076   Incidence* inc = mActionItem->incidence();
01077 
01078   if ( mItemMoved ) {
01079     bool modify = true;
01080     if ( mActionItem->incidence()->doesRecur() ) {
01081       int res = KOMessageBox::fourBtnMsgBox( this, QMessageBox::Question,
01082           i18n("The item you try to change is a recurring item. Shall the changes "
01083                "be applied only to this single occurrence, only to the future items, "
01084                "or to all items in the recurrence?"),
01085           i18n("Changing Recurring Item"),
01086           i18n("Only &This Item"), i18n("Only &Future Items"), i18n("&All Occurrences") );
01087       switch ( res ) {
01088         case KMessageBox::Ok: // All occurrences
01089             // Moving the whole sequene of events is handled by the itemModified below.
01090             modify = true;
01091             break;
01092         case KMessageBox::Yes: { // Just this occurrence
01093             // Dissociate this occurrence:
01094             // create clone of event, set relation to old event, set cloned event
01095             // for mActionItem, add exception date to old event, changeIncidence
01096             // for the old event, remove the recurrence from the new copy and then just
01097             // go on with the newly adjusted mActionItem and let the usual code take
01098             // care of the new time!
01099             modify = true;
01100             multiModify = true;
01101             emit startMultiModify( i18n("Dissociate event from recurrence") );
01102             Incidence* oldInc = mActionItem->incidence();
01103             Incidence* oldIncSaved = mActionItem->incidence()->clone();
01104             Incidence* newInc = mCalendar->dissociateOccurrence(
01105                 oldInc, mActionItem->itemDate() );
01106             if ( newInc ) {
01107               // don't recreate items, they already have the correct position
01108               emit enableAgendaUpdate( false );
01109               mActionItem->dissociateFromMultiItem();
01110               mActionItem->setIncidence( newInc );
01111               mChanger->addIncidence( newInc, this );
01112               emit enableAgendaUpdate( true );
01113               mChanger->changeIncidence( oldIncSaved, oldInc,
01114                                          KOGlobals::RECURRENCE_MODIFIED_ONE_ONLY, this );
01115             } else {
01116               KMessageBox::sorry( this, i18n("Unable to add the exception item to the "
01117                   "calendar. No change will be done."), i18n("Error Occurred") );
01118             }
01119             delete oldIncSaved;
01120             break; }
01121         case KMessageBox::No/*Future*/: { // All future occurrences
01122             // Dissociate this occurrence:
01123             // create clone of event, set relation to old event, set cloned event
01124             // for mActionItem, add recurrence end date to old event, changeIncidence
01125             // for the old event, adjust the recurrence for the new copy and then just
01126             // go on with the newly adjusted mActionItem and let the usual code take
01127             // care of the new time!
01128             modify = true;
01129             multiModify = true;
01130             emit startMultiModify( i18n("Split future recurrences") );
01131             Incidence* oldInc = mActionItem->incidence();
01132             Incidence* oldIncSaved = mActionItem->incidence()->clone();
01133             Incidence* newInc = mCalendar->dissociateOccurrence(
01134                 oldInc, mActionItem->itemDate(), false );
01135             if ( newInc ) {
01136               emit enableAgendaUpdate( false );
01137               mActionItem->dissociateFromMultiItem();
01138               mActionItem->setIncidence( newInc );
01139               mChanger->addIncidence( newInc, this );
01140               emit enableAgendaUpdate( true );
01141               mChanger->changeIncidence( oldIncSaved, oldInc,
01142                                          KOGlobals::RECURRENCE_MODIFIED_ALL_FUTURE, this );
01143             } else {
01144               KMessageBox::sorry( this, i18n("Unable to add the future items to the "
01145                   "calendar. No change will be done."), i18n("Error Occurred") );
01146             }
01147             delete oldIncSaved;
01148             break; }
01149         default:
01150           modify = false;
01151           mActionItem->resetMove();
01152           placeSubCells( mActionItem );
01153       }
01154     }
01155 
01156     if ( modify ) {
01157       mActionItem->endMove();
01158       KOAgendaItem *placeItem = mActionItem->firstMultiItem();
01159       if  ( !placeItem ) {
01160         placeItem = mActionItem;
01161       }
01162 
01163       KOAgendaItem *modif = placeItem;
01164 
01165       QPtrList<KOAgendaItem> oldconflictItems = placeItem->conflictItems();
01166       KOAgendaItem *item;
01167       for ( item = oldconflictItems.first(); item != 0;
01168             item = oldconflictItems.next() ) {
01169         placeSubCells( item );
01170       }
01171       while ( placeItem ) {
01172         placeSubCells( placeItem );
01173         placeItem = placeItem->nextMultiItem();
01174       }
01175 
01176       // Notify about change
01177       // the agenda view will apply the changes to the actual Incidence*!
01178       emit itemModified( modif );
01179     }
01180     // FIXME: If the change failed, we need to update the view!
01181     mChanger->endChange( inc );
01182   }
01183 
01184   mActionItem = 0;
01185   mItemMoved = false;
01186 
01187   if ( multiModify ) emit endMultiModify();
01188 
01189   kdDebug(5850) << "KOAgenda::endItemAction() done" << endl;
01190 }
01191 
01192 void KOAgenda::setActionCursor( int actionType, bool acting )
01193 {
01194   switch ( actionType ) {
01195     case MOVE:
01196       if (acting) setCursor( sizeAllCursor );
01197       else setCursor( arrowCursor );
01198       break;
01199     case RESIZETOP:
01200     case RESIZEBOTTOM:
01201       setCursor( sizeVerCursor );
01202       break;
01203     case RESIZELEFT:
01204     case RESIZERIGHT:
01205       setCursor( sizeHorCursor );
01206       break;
01207     default:
01208       setCursor( arrowCursor );
01209   }
01210 }
01211 
01212 void KOAgenda::setNoActionCursor( KOAgendaItem *moveItem, const QPoint& viewportPos )
01213 {
01214 //  kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
01215 //  QPoint point = viewport()->mapToGlobal(viewportPos);
01216 //  kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
01217 //  point = clipper()->mapFromGlobal(point);
01218 //  kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
01219 
01220   QPoint pos = viewportToContents( viewportPos );
01221   bool noResize = (moveItem && moveItem->incidence() &&
01222       moveItem->incidence()->type() == "Todo");
01223 
01224   KOAgenda::MouseActionType resizeType = MOVE;
01225   if ( !noResize ) resizeType = isInResizeArea( mAllDayMode, pos , moveItem);
01226   setActionCursor( resizeType );
01227 }
01228 
01229 
01232 double KOAgenda::calcSubCellWidth( KOAgendaItem *item )
01233 {
01234   QPoint pt, pt1;
01235   pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01236   pt1 = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) +
01237                         QPoint( 1, 1 ) );
01238   pt1 -= pt;
01239   int maxSubCells = item->subCells();
01240   double newSubCellWidth;
01241   if ( mAllDayMode ) {
01242     newSubCellWidth = double( pt1.y() ) / maxSubCells;
01243   } else {
01244     newSubCellWidth = double( pt1.x() ) / maxSubCells;
01245   }
01246   return newSubCellWidth;
01247 }
01248 
01249 void KOAgenda::adjustItemPosition( KOAgendaItem *item )
01250 {
01251   if (!item) return;
01252   item->resize( int( mGridSpacingX * item->cellWidth() ),
01253                 int( mGridSpacingY * item->cellHeight() ) );
01254   int clXLeft = item->cellXLeft();
01255   if ( KOGlobals::self()->reverseLayout() )
01256     clXLeft = item->cellXRight() + 1;
01257   QPoint cpos = gridToContents( QPoint( clXLeft, item->cellYTop() ) );
01258   moveChild( item, cpos.x(), cpos.y() );
01259 }
01260 
01261 void KOAgenda::placeAgendaItem( KOAgendaItem *item, double subCellWidth )
01262 {
01263 //  kdDebug(5850) << "KOAgenda::placeAgendaItem(): " << item->incidence()->summary()
01264 //            << " subCellWidth: " << subCellWidth << endl;
01265 
01266   // "left" upper corner, no subcells yet, RTL layouts have right/left switched, widths are negative then
01267   QPoint pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01268   // right lower corner
01269   QPoint pt1 = gridToContents( QPoint( item->cellXLeft() + item->cellWidth(),
01270       item->cellYBottom()+1 ) );
01271 
01272   double subCellPos = item->subCell() * subCellWidth;
01273 
01274   // we need to add 0.01 to make sure we don't loose one pixed due to
01275   // numerics (i.e. if it would be x.9998, we want the integer, not rounded down.
01276   double delta=0.01;
01277   if (subCellWidth<0) delta=-delta;
01278   int height, width, xpos, ypos;
01279   if (mAllDayMode) {
01280     width = pt1.x()-pt.x();
01281     height = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01282     xpos = pt.x();
01283     ypos = pt.y() + int( subCellPos );
01284   } else {
01285     width = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01286     height = pt1.y()-pt.y();
01287     xpos = pt.x() + int( subCellPos );
01288     ypos = pt.y();
01289   }
01290   if ( KOGlobals::self()->reverseLayout() ) { // RTL language/layout
01291     xpos += width;
01292     width = -width;
01293   }
01294   if ( height<0 ) { // BTT (bottom-to-top) layout ?!?
01295     ypos += height;
01296     height = -height;
01297   }
01298   item->resize( width, height );
01299   moveChild( item, xpos, ypos );
01300 }
01301 
01302 /*
01303   Place item in cell and take care that multiple items using the same cell do
01304   not overlap. This method is not yet optimal. It doesn't use the maximum space
01305   it can get in all cases.
01306   At the moment the method has a bug: When an item is placed only the sub cell
01307   widths of the items are changed, which are within the Y region the item to
01308   place spans. When the sub cell width change of one of this items affects a
01309   cell, where other items are, which do not overlap in Y with the item to place,
01310   the display gets corrupted, although the corruption looks quite nice.
01311 */
01312 void KOAgenda::placeSubCells( KOAgendaItem *placeItem )
01313 {
01314 #if 0
01315   kdDebug(5850) << "KOAgenda::placeSubCells()" << endl;
01316   if ( placeItem ) {
01317     Incidence *event = placeItem->incidence();
01318     if ( !event ) {
01319       kdDebug(5850) << "  event is 0" << endl;
01320     } else {
01321       kdDebug(5850) << "  event: " << event->summary() << endl;
01322     }
01323   } else {
01324     kdDebug(5850) << "  placeItem is 0" << endl;
01325   }
01326   kdDebug(5850) << "KOAgenda::placeSubCells()..." << endl;
01327 #endif
01328 
01329   QPtrList<KOrg::CellItem> cells;
01330   KOAgendaItem *item;
01331   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01332     cells.append( item );
01333   }
01334 
01335   QPtrList<KOrg::CellItem> items = KOrg::CellItem::placeItem( cells,
01336                                                               placeItem );
01337 
01338   placeItem->setConflictItems( QPtrList<KOAgendaItem>() );
01339   double newSubCellWidth = calcSubCellWidth( placeItem );
01340   KOrg::CellItem *i;
01341   for ( i = items.first(); i; i = items.next() ) {
01342     item = static_cast<KOAgendaItem *>( i );
01343     placeAgendaItem( item, newSubCellWidth );
01344     item->addConflictItem( placeItem );
01345     placeItem->addConflictItem( item );
01346   }
01347   if ( items.isEmpty() ) {
01348     placeAgendaItem( placeItem, newSubCellWidth );
01349   }
01350   placeItem->update();
01351 }
01352 
01353 int KOAgenda::columnWidth( int column )
01354 {
01355   int start = gridToContents( QPoint( column, 0 ) ).x();
01356   if (KOGlobals::self()->reverseLayout() )
01357     column--;
01358   else
01359     column++;
01360   int end = gridToContents( QPoint( column, 0 ) ).x();
01361   return end - start;
01362 }
01363 /*
01364   Draw grid in the background of the agenda.
01365 */
01366 void KOAgenda::drawContents(QPainter* p, int cx, int cy, int cw, int ch)
01367 {
01368   QPixmap db(cw, ch);
01369   db.fill(KOPrefs::instance()->mAgendaBgColor);
01370   QPainter dbp(&db);
01371   dbp.translate(-cx,-cy);
01372 
01373 //  kdDebug(5850) << "KOAgenda::drawContents()" << endl;
01374   double lGridSpacingY = mGridSpacingY*2;
01375 
01376   // Highlight working hours
01377   if (mWorkingHoursEnable) {
01378     QPoint pt1( cx, mWorkingHoursYTop );
01379     QPoint pt2( cx+cw, mWorkingHoursYBottom );
01380     if ( pt2.x() >= pt1.x() /*&& pt2.y() >= pt1.y()*/) {
01381       int gxStart = contentsToGrid( pt1 ).x();
01382       int gxEnd = contentsToGrid( pt2 ).x();
01383       // correct start/end for rtl layouts
01384       if ( gxStart > gxEnd ) {
01385         int tmp = gxStart;
01386         gxStart = gxEnd;
01387         gxEnd = tmp;
01388       }
01389       int xoffset = ( KOGlobals::self()->reverseLayout()?1:0 );
01390       while( gxStart <= gxEnd ) {
01391         int xStart = gridToContents( QPoint( gxStart+xoffset, 0 ) ).x();
01392         int xWidth = columnWidth( gxStart ) + 1;
01393         if ( pt2.y() < pt1.y() ) {
01394           // overnight working hours
01395           if ( ( (gxStart==0) && !mHolidayMask->at(mHolidayMask->count()-1) ) ||
01396                ( (gxStart>0) && (gxStart<int(mHolidayMask->count())) && (!mHolidayMask->at(gxStart-1) ) ) ) {
01397             if ( pt2.y() > cy ) {
01398               dbp.fillRect( xStart, cy, xWidth, pt2.y() - cy + 1,
01399                             KOPrefs::instance()->mWorkingHoursColor);
01400             }
01401           }
01402           if ( (gxStart < int(mHolidayMask->count()-1)) && (!mHolidayMask->at(gxStart)) ) {
01403             if ( pt1.y() < cy + ch - 1 ) {
01404               dbp.fillRect( xStart, pt1.y(), xWidth, cy + ch - pt1.y() + 1,
01405                             KOPrefs::instance()->mWorkingHoursColor);
01406             }
01407           }
01408         } else {
01409           // last entry in holiday mask denotes the previous day not visible (needed for overnight shifts)
01410           if ( gxStart < int(mHolidayMask->count()-1) && !mHolidayMask->at(gxStart)) {
01411             dbp.fillRect( xStart, pt1.y(), xWidth, pt2.y() - pt1.y() + 1,
01412                           KOPrefs::instance()->mWorkingHoursColor );
01413           }
01414         }
01415         ++gxStart;
01416       }
01417     }
01418   }
01419 
01420   // draw selection
01421   if ( mHasSelection ) {
01422     QPoint pt, pt1;
01423 
01424     if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
01425       // draw start day
01426       pt = gridToContents( mSelectionStartCell );
01427       pt1 = gridToContents( QPoint( mSelectionStartCell.x() + 1, mRows + 1 ) );
01428       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01429       // draw all other days between the start day and the day of the selection end
01430       for ( int c = mSelectionStartCell.x() + 1; c < mSelectionEndCell.x(); ++c ) {
01431         pt = gridToContents( QPoint( c, 0 ) );
01432         pt1 = gridToContents( QPoint( c + 1, mRows + 1 ) );
01433         dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01434       }
01435       // draw end day
01436       pt = gridToContents( QPoint( mSelectionEndCell.x(), 0 ) );
01437       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01438       dbp.fillRect( QRect( pt, pt1), KOPrefs::instance()->mHighlightColor );
01439     }  else { // single day selection
01440       pt = gridToContents( mSelectionStartCell );
01441       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01442       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01443     }
01444   }
01445 
01446   QPen hourPen( KOPrefs::instance()->mAgendaBgColor.dark( 150 ) );
01447   QPen halfHourPen( KOPrefs::instance()->mAgendaBgColor.dark( 125 ) );
01448   dbp.setPen( hourPen );
01449 
01450   // Draw vertical lines of grid, start with the last line not yet visible
01451   //  kdDebug(5850) << "drawContents cx: " << cx << " cy: " << cy << " cw: " << cw << " ch: " << ch << endl;
01452   double x = ( int( cx / mGridSpacingX ) ) * mGridSpacingX;
01453   while (x < cx + cw) {
01454     dbp.drawLine( int( x ), cy, int( x ), cy + ch );
01455     x+=mGridSpacingX;
01456   }
01457 
01458   // Draw horizontal lines of grid
01459   double y = ( int( cy / (2*lGridSpacingY) ) ) * 2 * lGridSpacingY;
01460   while (y < cy + ch) {
01461 //    kdDebug(5850) << " y: " << y << endl;
01462     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01463     y += 2 * lGridSpacingY;
01464   }
01465   y = ( 2 * int( cy / (2*lGridSpacingY) ) + 1) * lGridSpacingY;
01466   dbp.setPen( halfHourPen );
01467   while (y < cy + ch) {
01468 //    kdDebug(5850) << " y: " << y << endl;
01469     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01470     y+=2*lGridSpacingY;
01471   }
01472   p->drawPixmap(cx,cy, db);
01473 }
01474 
01475 /*
01476   Convert srcollview contents coordinates to agenda grid coordinates.
01477 */
01478 QPoint KOAgenda::contentsToGrid ( const QPoint &pos ) const
01479 {
01480   int gx = int( KOGlobals::self()->reverseLayout() ?
01481         mColumns - pos.x()/mGridSpacingX : pos.x()/mGridSpacingX );
01482   int gy = int( pos.y()/mGridSpacingY );
01483   return QPoint( gx, gy );
01484 }
01485 
01486 /*
01487   Convert agenda grid coordinates to scrollview contents coordinates.
01488 */
01489 QPoint KOAgenda::gridToContents( const QPoint &gpos ) const
01490 {
01491   int x = int( KOGlobals::self()->reverseLayout() ?
01492              (mColumns - gpos.x())*mGridSpacingX : gpos.x()*mGridSpacingX );
01493   int y = int( gpos.y()*mGridSpacingY );
01494   return QPoint( x, y );
01495 }
01496 
01497 
01498 /*
01499   Return Y coordinate corresponding to time. Coordinates are rounded to fit into
01500   the grid.
01501 */
01502 int KOAgenda::timeToY(const QTime &time)
01503 {
01504 //  kdDebug(5850) << "Time: " << time.toString() << endl;
01505   int minutesPerCell = 24 * 60 / mRows;
01506 //  kdDebug(5850) << "minutesPerCell: " << minutesPerCell << endl;
01507   int timeMinutes = time.hour() * 60 + time.minute();
01508 //  kdDebug(5850) << "timeMinutes: " << timeMinutes << endl;
01509   int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
01510 //  kdDebug(5850) << "y: " << Y << endl;
01511 //  kdDebug(5850) << "\n" << endl;
01512   return Y;
01513 }
01514 
01515 
01516 /*
01517   Return time corresponding to cell y coordinate. Coordinates are rounded to
01518   fit into the grid.
01519 */
01520 QTime KOAgenda::gyToTime(int gy)
01521 {
01522 //  kdDebug(5850) << "gyToTime: " << gy << endl;
01523   int secondsPerCell = 24 * 60 * 60/ mRows;
01524 
01525   int timeSeconds = secondsPerCell * gy;
01526 
01527   QTime time( 0, 0, 0 );
01528   if ( timeSeconds < 24 * 60 * 60 ) {
01529     time = time.addSecs(timeSeconds);
01530   } else {
01531     time.setHMS( 23, 59, 59 );
01532   }
01533 //  kdDebug(5850) << "  gyToTime: " << time.toString() << endl;
01534 
01535   return time;
01536 }
01537 
01538 QMemArray<int> KOAgenda::minContentsY()
01539 {
01540   QMemArray<int> minArray;
01541   minArray.fill( timeToY( QTime(23, 59) ), mSelectedDates.count() );
01542   for ( KOAgendaItem *item = mItems.first();
01543         item != 0; item = mItems.next() ) {
01544     int ymin = item->cellYTop();
01545     int index = item->cellXLeft();
01546     if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
01547       if ( ymin < minArray[index] && mItemsToDelete.findRef( item ) == -1 )
01548         minArray[index] = ymin;
01549     }
01550   }
01551 
01552   return minArray;
01553 }
01554 
01555 QMemArray<int> KOAgenda::maxContentsY()
01556 {
01557   QMemArray<int> maxArray;
01558   maxArray.fill( timeToY( QTime(0, 0) ), mSelectedDates.count() );
01559   for ( KOAgendaItem *item = mItems.first();
01560         item != 0; item = mItems.next() ) {
01561     int ymax = item->cellYBottom();
01562     int index = item->cellXLeft();
01563     if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
01564       if ( ymax > maxArray[index] && mItemsToDelete.findRef( item ) == -1 )
01565         maxArray[index] = ymax;
01566     }
01567   }
01568 
01569   return maxArray;
01570 }
01571 
01572 void KOAgenda::setStartTime( const QTime &startHour )
01573 {
01574   double startPos = ( startHour.hour()/24. + startHour.minute()/1440. +
01575                       startHour.second()/86400. ) * mRows * gridSpacingY();
01576   setContentsPos( 0, int( startPos ) );
01577 }
01578 
01579 
01580 /*
01581   Insert KOAgendaItem into agenda.
01582 */
01583 KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const QDate &qd, int X,
01584                                     int YTop, int YBottom )
01585 {
01586 #if 0
01587   kdDebug(5850) << "KOAgenda::insertItem:" << incidence->summary() << "-"
01588                 << qd.toString() << " ;top, bottom:" << YTop << "," << YBottom
01589                 << endl;
01590 #endif
01591 
01592   if ( mAllDayMode ) {
01593     kdDebug(5850) << "KOAgenda: calling insertItem in all-day mode is illegal." << endl;
01594     return 0;
01595   }
01596 
01597 
01598   mActionType = NOP;
01599 
01600   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, incidence, qd, viewport() );
01601   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem * ) ),
01602            SLOT( removeAgendaItem( KOAgendaItem * ) ) );
01603   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem * ) ),
01604            SLOT( showAgendaItem( KOAgendaItem * ) ) );
01605 
01606   if ( YBottom <= YTop ) {
01607     kdDebug(5850) << "KOAgenda::insertItem(): Text: " << agendaItem->text() << " YSize<0" << endl;
01608     YBottom = YTop;
01609   }
01610 
01611   agendaItem->resize( int( ( X + 1 ) * mGridSpacingX ) -
01612                       int( X * mGridSpacingX ),
01613                       int( YTop * mGridSpacingY ) -
01614                       int( ( YBottom + 1 ) * mGridSpacingY ) );
01615   agendaItem->setCellXY( X, YTop, YBottom );
01616   agendaItem->setCellXRight( X );
01617   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, incidence ) );
01618   agendaItem->installEventFilter( this );
01619 
01620   addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
01621   mItems.append( agendaItem );
01622 
01623   placeSubCells( agendaItem );
01624 
01625   agendaItem->show();
01626 
01627   marcus_bains();
01628 
01629   return agendaItem;
01630 }
01631 
01632 /*
01633   Insert all-day KOAgendaItem into agenda.
01634 */
01635 KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const QDate &qd,
01636                                           int XBegin, int XEnd )
01637 {
01638   if ( !mAllDayMode ) {
01639     kdDebug(5850) << "KOAgenda: calling insertAllDayItem in non all-day mode is illegal." << endl;
01640     return 0;
01641   }
01642 
01643   mActionType = NOP;
01644 
01645   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, event, qd, viewport() );
01646   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem* ) ),
01647            SLOT( removeAgendaItem( KOAgendaItem* ) ) );
01648   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem* ) ),
01649            SLOT( showAgendaItem( KOAgendaItem* ) ) );
01650 
01651   agendaItem->setCellXY( XBegin, 0, 0 );
01652   agendaItem->setCellXRight( XEnd );
01653 
01654   double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
01655   double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
01656                                    agendaItem->cellXLeft() );
01657 
01658   agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
01659 
01660   agendaItem->installEventFilter( this );
01661   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
01662   addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
01663   mItems.append( agendaItem );
01664 
01665   placeSubCells( agendaItem );
01666 
01667   agendaItem->show();
01668 
01669   return agendaItem;
01670 }
01671 
01672 
01673 void KOAgenda::insertMultiItem (Event *event,const QDate &qd,int XBegin,int XEnd,
01674                                 int YTop,int YBottom)
01675 {
01676   if (mAllDayMode) {
01677     kdDebug(5850) << "KOAgenda: calling insertMultiItem in all-day mode is illegal." << endl;
01678     return;
01679   }
01680   mActionType = NOP;
01681 
01682   int cellX,cellYTop,cellYBottom;
01683   QString newtext;
01684   int width = XEnd - XBegin + 1;
01685   int count = 0;
01686   KOAgendaItem *current = 0;
01687   QPtrList<KOAgendaItem> multiItems;
01688   int visibleCount = mSelectedDates.first().daysTo(mSelectedDates.last());
01689   for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
01690     ++count;
01691     //Only add the items that are visible.
01692     if( cellX >=0 && cellX <= visibleCount ) {
01693       if ( cellX == XBegin ) cellYTop = YTop;
01694       else cellYTop = 0;
01695       if ( cellX == XEnd ) cellYBottom = YBottom;
01696       else cellYBottom = rows() - 1;
01697       newtext = QString("(%1/%2): ").arg( count ).arg( width );
01698       newtext.append( event->summary() );
01699 
01700       current = insertItem( event, qd, cellX, cellYTop, cellYBottom );
01701       current->setText( newtext );
01702       multiItems.append( current );
01703     }
01704   }
01705 
01706   KOAgendaItem *next = 0;
01707   KOAgendaItem *prev = 0;
01708   KOAgendaItem *last = multiItems.last();
01709   KOAgendaItem *first = multiItems.first();
01710   KOAgendaItem *setFirst,*setLast;
01711   current = first;
01712   while (current) {
01713     next = multiItems.next();
01714     if (current == first) setFirst = 0;
01715     else setFirst = first;
01716     if (current == last) setLast = 0;
01717     else setLast = last;
01718 
01719     current->setMultiItem(setFirst, prev, next, setLast);
01720     prev=current;
01721     current = next;
01722   }
01723 
01724   marcus_bains();
01725 }
01726 
01727 void KOAgenda::removeIncidence( Incidence *incidence )
01728 {
01729   // First find all items to be deleted and store them
01730   // in its own list. Otherwise removeAgendaItem will reset
01731   // the current position and mess this up.
01732   QPtrList<KOAgendaItem> itemsToRemove;
01733 
01734   KOAgendaItem *item = mItems.first();
01735   while ( item ) {
01736     if ( item->incidence() == incidence ) {
01737       itemsToRemove.append( item );
01738     }
01739     item = mItems.next();
01740   }
01741   item = itemsToRemove.first();
01742   while ( item ) {
01743     removeAgendaItem( item );
01744     item = itemsToRemove.next();
01745   }
01746 }
01747 
01748 void KOAgenda::showAgendaItem( KOAgendaItem *agendaItem )
01749 {
01750   if ( !agendaItem ) return;
01751   agendaItem->hide();
01752   addChild( agendaItem );
01753   if ( !mItems.containsRef( agendaItem ) )
01754     mItems.append( agendaItem );
01755   placeSubCells( agendaItem );
01756 
01757   agendaItem->show();
01758 }
01759 
01760 bool KOAgenda::removeAgendaItem( KOAgendaItem *item )
01761 {
01762   // we found the item. Let's remove it and update the conflicts
01763   bool taken = false;
01764   KOAgendaItem *thisItem = item;
01765   QPtrList<KOAgendaItem> conflictItems = thisItem->conflictItems();
01766   removeChild( thisItem );
01767   int pos = mItems.find( thisItem );
01768   if ( pos>=0 ) {
01769     mItems.take( pos );
01770     taken = true;
01771   }
01772 
01773   KOAgendaItem *confitem;
01774   for ( confitem = conflictItems.first(); confitem != 0;
01775         confitem = conflictItems.next() ) {
01776     // the item itself is also in its own conflictItems list!
01777     if ( confitem != thisItem ) placeSubCells(confitem);
01778 
01779   }
01780   mItemsToDelete.append( thisItem );
01781   QTimer::singleShot( 0, this, SLOT( deleteItemsToDelete() ) );
01782   return taken;
01783 }
01784 
01785 void KOAgenda::deleteItemsToDelete()
01786 {
01787   mItemsToDelete.clear();
01788 }
01789 
01790 /*QSizePolicy KOAgenda::sizePolicy() const
01791 {
01792   // Thought this would make the all-day event agenda minimum size and the
01793   // normal agenda take the remaining space. But it doesnt work. The QSplitter
01794   // dont seem to think that an Expanding widget needs more space than a
01795   // Preferred one.
01796   // But it doesnt hurt, so it stays.
01797   if (mAllDayMode) {
01798     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
01799   } else {
01800     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
01801   }
01802 }
01803 */
01804 
01805 /*
01806   Overridden from QScrollView to provide proper resizing of KOAgendaItems.
01807 */
01808 void KOAgenda::resizeEvent ( QResizeEvent *ev )
01809 {
01810 //  kdDebug(5850) << "KOAgenda::resizeEvent" << endl;
01811 
01812   QSize newSize( ev->size() );
01813   if (mAllDayMode) {
01814     mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
01815     mGridSpacingY = newSize.height() - 2 * frameWidth();
01816   } else {
01817     int scrollbarWidth = vScrollBarMode() != AlwaysOff ? verticalScrollBar()->width() : 0;
01818     mGridSpacingX = double( newSize.width() - scrollbarWidth - 2 * frameWidth()) / double(mColumns);
01819     // make sure that there are not more than 24 per day
01820     mGridSpacingY = double(newSize.height() - 2 * frameWidth()) / double(mRows);
01821     if ( mGridSpacingY < mDesiredGridSpacingY )
01822       mGridSpacingY = mDesiredGridSpacingY;
01823   }
01824   calculateWorkingHours();
01825   QTimer::singleShot( 0, this, SLOT( resizeAllContents() ) );
01826   emit gridSpacingYChanged( mGridSpacingY * 4 );
01827   QScrollView::resizeEvent(ev);
01828 }
01829 
01830 void KOAgenda::resizeAllContents()
01831 {
01832   double subCellWidth;
01833   if ( mItems.count() > 0 ) {
01834     KOAgendaItem *item;
01835     if (mAllDayMode) {
01836       for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01837         subCellWidth = calcSubCellWidth( item );
01838         placeAgendaItem( item, subCellWidth );
01839       }
01840     } else {
01841       for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01842         subCellWidth = calcSubCellWidth( item );
01843         placeAgendaItem( item, subCellWidth );
01844       }
01845     }
01846   }
01847   checkScrollBoundaries();
01848   marcus_bains();
01849 }
01850 
01851 void KOAgenda::scrollUp()
01852 {
01853   scrollBy(0,-mScrollOffset);
01854 }
01855 
01856 
01857 void KOAgenda::scrollDown()
01858 {
01859   scrollBy(0,mScrollOffset);
01860 }
01861 
01862 
01863 /*
01864   Calculates the minimum width
01865 */
01866 int KOAgenda::minimumWidth() const
01867 {
01868   // FIXME:: develop a way to dynamically determine the minimum width
01869   int min = 100;
01870 
01871   return min;
01872 }
01873 
01874 void KOAgenda::updateConfig()
01875 {
01876   double oldGridSpacingY = mGridSpacingY;
01877 
01878   mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
01879   if ( mDesiredGridSpacingY < 4 || mDesiredGridSpacingY > 30 ) {
01880     mDesiredGridSpacingY = 10;
01881   }
01882 
01883   // make sure that there are not more than 24 per day
01884   mGridSpacingY = (double)height() / (double)mRows;
01885   if ( mGridSpacingY < mDesiredGridSpacingY ) {
01886     mGridSpacingY = mDesiredGridSpacingY;
01887   }
01888 
01889   //can be two doubles equal?, it's better to compare them with an epsilon
01890   if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 ) {
01891     resizeContents( int( mGridSpacingX * mColumns ),
01892                     int( mGridSpacingY * mRows ) );
01893   }
01894 
01895   calculateWorkingHours();
01896 
01897   marcus_bains();
01898 }
01899 
01900 void KOAgenda::checkScrollBoundaries()
01901 {
01902   // Invalidate old values to force update
01903   mOldLowerScrollValue = -1;
01904   mOldUpperScrollValue = -1;
01905 
01906   checkScrollBoundaries(verticalScrollBar()->value());
01907 }
01908 
01909 void KOAgenda::checkScrollBoundaries( int v )
01910 {
01911   int yMin = int( (v) / mGridSpacingY );
01912   int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
01913 
01914 //  kdDebug(5850) << "--- yMin: " << yMin << "  yMax: " << yMax << endl;
01915 
01916   if ( yMin != mOldLowerScrollValue ) {
01917     mOldLowerScrollValue = yMin;
01918     emit lowerYChanged(yMin);
01919   }
01920   if ( yMax != mOldUpperScrollValue ) {
01921     mOldUpperScrollValue = yMax;
01922     emit upperYChanged(yMax);
01923   }
01924 }
01925 
01926 int KOAgenda::visibleContentsYMin()
01927 {
01928   int v = verticalScrollBar()->value();
01929   return int( v / mGridSpacingY );
01930 }
01931 
01932 int KOAgenda::visibleContentsYMax()
01933 {
01934   int v = verticalScrollBar()->value();
01935   return int( ( v + visibleHeight() ) / mGridSpacingY );
01936 }
01937 
01938 void KOAgenda::deselectItem()
01939 {
01940   if (mSelectedItem.isNull()) return;
01941   mSelectedItem->select(false);
01942   mSelectedItem = 0;
01943 }
01944 
01945 void KOAgenda::selectItem(KOAgendaItem *item)
01946 {
01947   if ((KOAgendaItem *)mSelectedItem == item) return;
01948   deselectItem();
01949   if (item == 0) {
01950     emit incidenceSelected( 0, QDate() );
01951     return;
01952   }
01953   mSelectedItem = item;
01954   mSelectedItem->select();
01955   assert( mSelectedItem->incidence() );
01956   mSelectedUid = mSelectedItem->incidence()->uid();
01957   emit incidenceSelected( mSelectedItem->incidence(), mSelectedItem->itemDate() );
01958 }
01959 
01960 void KOAgenda::selectItemByUID( const QString& uid )
01961 {
01962   KOAgendaItem *item;
01963   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01964     if( item->incidence() && item->incidence()->uid() == uid ) {
01965       selectItem( item );
01966       break;
01967     }
01968   }
01969 }
01970 
01971 // This function seems never be called.
01972 void KOAgenda::keyPressEvent( QKeyEvent *kev )
01973 {
01974   switch(kev->key()) {
01975     case Key_PageDown:
01976       verticalScrollBar()->addPage();
01977       break;
01978     case Key_PageUp:
01979       verticalScrollBar()->subtractPage();
01980       break;
01981     case Key_Down:
01982       verticalScrollBar()->addLine();
01983       break;
01984     case Key_Up:
01985       verticalScrollBar()->subtractLine();
01986       break;
01987     default:
01988       ;
01989   }
01990 }
01991 
01992 void KOAgenda::calculateWorkingHours()
01993 {
01994   mWorkingHoursEnable = !mAllDayMode;
01995 
01996   QTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
01997   mWorkingHoursYTop = int( 4 * mGridSpacingY *
01998                            ( tmp.hour() + tmp.minute() / 60. +
01999                              tmp.second() / 3600. ) );
02000   tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
02001   mWorkingHoursYBottom = int( 4 * mGridSpacingY *
02002                               ( tmp.hour() + tmp.minute() / 60. +
02003                                 tmp.second() / 3600. ) - 1 );
02004 }
02005 
02006 
02007 DateList KOAgenda::dateList() const
02008 {
02009     return mSelectedDates;
02010 }
02011 
02012 void KOAgenda::setDateList(const DateList &selectedDates)
02013 {
02014     mSelectedDates = selectedDates;
02015     marcus_bains();
02016 }
02017 
02018 void KOAgenda::setHolidayMask(QMemArray<bool> *mask)
02019 {
02020   mHolidayMask = mask;
02021 
02022 }
02023 
02024 void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
02025 {
02026   kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
02027   QScrollView::contentsMousePressEvent(event);
02028 }
02029 
02030 void KOAgenda::setTypeAheadReceiver( QObject *o )
02031 {
02032   mTypeAheadReceiver = o;
02033 }
02034 
02035 QObject *KOAgenda::typeAheadReceiver() const
02036 {
02037   return mTypeAheadReceiver;
02038 }
KDE Home | KDE Accessibility Home | Description of Access Keys