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( 0/*ResourceCalendar*/, QString()/*subResource*/);
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( 0/*ResourceCalendar*/, QString()/*subResource*/);
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   mItemMoved = mItemMoved && !( mStartCell.x() == mEndCell.x() &&
01079                                 mStartCell.y() == mEndCell.y() );
01080 
01081   if ( mItemMoved ) {
01082     bool modify = true;
01083     if ( mActionItem->incidence()->doesRecur() ) {
01084       int res = KOMessageBox::fourBtnMsgBox( this, QMessageBox::Question,
01085           i18n("The item you try to change is a recurring item. Shall the changes "
01086                "be applied only to this single occurrence, only to the future items, "
01087                "or to all items in the recurrence?"),
01088           i18n("Changing Recurring Item"),
01089           i18n("Only &This Item"), i18n("Only &Future Items"), i18n("&All Occurrences") );
01090       switch ( res ) {
01091         case KMessageBox::Ok: // All occurrences
01092             // Moving the whole sequene of events is handled by the itemModified below.
01093             modify = true;
01094             break;
01095         case KMessageBox::Yes: { // Just this occurrence
01096             // Dissociate this occurrence:
01097             // create clone of event, set relation to old event, set cloned event
01098             // for mActionItem, add exception date to old event, changeIncidence
01099             // for the old event, remove the recurrence from the new copy and then just
01100             // go on with the newly adjusted mActionItem and let the usual code take
01101             // care of the new time!
01102             modify = true;
01103             multiModify = true;
01104             emit startMultiModify( i18n("Dissociate event from recurrence") );
01105             Incidence* oldInc = mActionItem->incidence();
01106             Incidence* oldIncSaved = mActionItem->incidence()->clone();
01107             Incidence* newInc = mCalendar->dissociateOccurrence(
01108               oldInc, mActionItem->itemDate() );
01109             if ( newInc ) {
01110               // don't recreate items, they already have the correct position
01111               emit enableAgendaUpdate( false );
01112               mActionItem->dissociateFromMultiItem();
01113               mActionItem->setIncidence( newInc );
01114               mChanger->addIncidence( newInc, 0, QString(), this );
01115               emit enableAgendaUpdate( true );
01116               mChanger->changeIncidence( oldIncSaved, oldInc,
01117                                          KOGlobals::RECURRENCE_MODIFIED_ONE_ONLY, this );
01118             } else {
01119               KMessageBox::sorry( this, i18n("Unable to add the exception item to the "
01120                   "calendar. No change will be done."), i18n("Error Occurred") );
01121             }
01122             delete oldIncSaved;
01123             break; }
01124         case KMessageBox::No/*Future*/: { // All future occurrences
01125             // Dissociate this occurrence:
01126             // create clone of event, set relation to old event, set cloned event
01127             // for mActionItem, add recurrence end date to old event, changeIncidence
01128             // for the old event, adjust the recurrence for the new copy and then just
01129             // go on with the newly adjusted mActionItem and let the usual code take
01130             // care of the new time!
01131             modify = true;
01132             multiModify = true;
01133             emit startMultiModify( i18n("Split future recurrences") );
01134             Incidence* oldInc = mActionItem->incidence();
01135             Incidence* oldIncSaved = mActionItem->incidence()->clone();
01136             Incidence* newInc = mCalendar->dissociateOccurrence(
01137                 oldInc, mActionItem->itemDate(), false );
01138             if ( newInc ) {
01139               emit enableAgendaUpdate( false );
01140               mActionItem->dissociateFromMultiItem();
01141               mActionItem->setIncidence( newInc );
01142               mChanger->addIncidence( newInc, 0, QString(), this );
01143               emit enableAgendaUpdate( true );
01144               mChanger->changeIncidence( oldIncSaved, oldInc,
01145                                          KOGlobals::RECURRENCE_MODIFIED_ALL_FUTURE, this );
01146             } else {
01147               KMessageBox::sorry( this, i18n("Unable to add the future items to the "
01148                   "calendar. No change will be done."), i18n("Error Occurred") );
01149             }
01150             delete oldIncSaved;
01151             break; }
01152         default:
01153           modify = false;
01154           mActionItem->resetMove();
01155           placeSubCells( mActionItem );
01156       }
01157     }
01158 
01159     if ( modify ) {
01160       mActionItem->endMove();
01161       KOAgendaItem *placeItem = mActionItem->firstMultiItem();
01162       if  ( !placeItem ) {
01163         placeItem = mActionItem;
01164       }
01165 
01166       KOAgendaItem *modif = placeItem;
01167 
01168       QPtrList<KOAgendaItem> oldconflictItems = placeItem->conflictItems();
01169       KOAgendaItem *item;
01170       for ( item = oldconflictItems.first(); item != 0;
01171             item = oldconflictItems.next() ) {
01172         placeSubCells( item );
01173       }
01174       while ( placeItem ) {
01175         placeSubCells( placeItem );
01176         placeItem = placeItem->nextMultiItem();
01177       }
01178 
01179       // Notify about change
01180       // the agenda view will apply the changes to the actual Incidence*!
01181       mChanger->endChange( inc );
01182       emit itemModified( modif );
01183     } else {
01184       // the item was moved, but not further modified, since it's not recurring
01185       // make sure the view updates anyhow, with the right item
01186       mChanger->endChange( inc );
01187       emit itemModified( mActionItem );
01188     }
01189   } else {
01190     mChanger->endChange( inc );
01191   }
01192 
01193   mActionItem = 0;
01194   mItemMoved = false;
01195 
01196   if ( multiModify ) emit endMultiModify();
01197 
01198   kdDebug(5850) << "KOAgenda::endItemAction() done" << endl;
01199 }
01200 
01201 void KOAgenda::setActionCursor( int actionType, bool acting )
01202 {
01203   switch ( actionType ) {
01204     case MOVE:
01205       if (acting) setCursor( sizeAllCursor );
01206       else setCursor( arrowCursor );
01207       break;
01208     case RESIZETOP:
01209     case RESIZEBOTTOM:
01210       setCursor( sizeVerCursor );
01211       break;
01212     case RESIZELEFT:
01213     case RESIZERIGHT:
01214       setCursor( sizeHorCursor );
01215       break;
01216     default:
01217       setCursor( arrowCursor );
01218   }
01219 }
01220 
01221 void KOAgenda::setNoActionCursor( KOAgendaItem *moveItem, const QPoint& viewportPos )
01222 {
01223 //  kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
01224 //  QPoint point = viewport()->mapToGlobal(viewportPos);
01225 //  kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
01226 //  point = clipper()->mapFromGlobal(point);
01227 //  kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
01228 
01229   QPoint pos = viewportToContents( viewportPos );
01230   bool noResize = (moveItem && moveItem->incidence() &&
01231       moveItem->incidence()->type() == "Todo");
01232 
01233   KOAgenda::MouseActionType resizeType = MOVE;
01234   if ( !noResize ) resizeType = isInResizeArea( mAllDayMode, pos , moveItem);
01235   setActionCursor( resizeType );
01236 }
01237 
01238 
01241 double KOAgenda::calcSubCellWidth( KOAgendaItem *item )
01242 {
01243   QPoint pt, pt1;
01244   pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01245   pt1 = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) +
01246                         QPoint( 1, 1 ) );
01247   pt1 -= pt;
01248   int maxSubCells = item->subCells();
01249   double newSubCellWidth;
01250   if ( mAllDayMode ) {
01251     newSubCellWidth = double( pt1.y() ) / maxSubCells;
01252   } else {
01253     newSubCellWidth = double( pt1.x() ) / maxSubCells;
01254   }
01255   return newSubCellWidth;
01256 }
01257 
01258 void KOAgenda::adjustItemPosition( KOAgendaItem *item )
01259 {
01260   if (!item) return;
01261   item->resize( int( mGridSpacingX * item->cellWidth() ),
01262                 int( mGridSpacingY * item->cellHeight() ) );
01263   int clXLeft = item->cellXLeft();
01264   if ( KOGlobals::self()->reverseLayout() )
01265     clXLeft = item->cellXRight() + 1;
01266   QPoint cpos = gridToContents( QPoint( clXLeft, item->cellYTop() ) );
01267   moveChild( item, cpos.x(), cpos.y() );
01268 }
01269 
01270 void KOAgenda::placeAgendaItem( KOAgendaItem *item, double subCellWidth )
01271 {
01272 //  kdDebug(5850) << "KOAgenda::placeAgendaItem(): " << item->incidence()->summary()
01273 //            << " subCellWidth: " << subCellWidth << endl;
01274 
01275   // "left" upper corner, no subcells yet, RTL layouts have right/left switched, widths are negative then
01276   QPoint pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01277   // right lower corner
01278   QPoint pt1 = gridToContents( QPoint( item->cellXLeft() + item->cellWidth(),
01279       item->cellYBottom()+1 ) );
01280 
01281   double subCellPos = item->subCell() * subCellWidth;
01282 
01283   // we need to add 0.01 to make sure we don't loose one pixed due to
01284   // numerics (i.e. if it would be x.9998, we want the integer, not rounded down.
01285   double delta=0.01;
01286   if (subCellWidth<0) delta=-delta;
01287   int height, width, xpos, ypos;
01288   if (mAllDayMode) {
01289     width = pt1.x()-pt.x();
01290     height = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01291     xpos = pt.x();
01292     ypos = pt.y() + int( subCellPos );
01293   } else {
01294     width = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01295     height = pt1.y()-pt.y();
01296     xpos = pt.x() + int( subCellPos );
01297     ypos = pt.y();
01298   }
01299   if ( KOGlobals::self()->reverseLayout() ) { // RTL language/layout
01300     xpos += width;
01301     width = -width;
01302   }
01303   if ( height<0 ) { // BTT (bottom-to-top) layout ?!?
01304     ypos += height;
01305     height = -height;
01306   }
01307   item->resize( width, height );
01308   moveChild( item, xpos, ypos );
01309 }
01310 
01311 /*
01312   Place item in cell and take care that multiple items using the same cell do
01313   not overlap. This method is not yet optimal. It doesn't use the maximum space
01314   it can get in all cases.
01315   At the moment the method has a bug: When an item is placed only the sub cell
01316   widths of the items are changed, which are within the Y region the item to
01317   place spans. When the sub cell width change of one of this items affects a
01318   cell, where other items are, which do not overlap in Y with the item to place,
01319   the display gets corrupted, although the corruption looks quite nice.
01320 */
01321 void KOAgenda::placeSubCells( KOAgendaItem *placeItem )
01322 {
01323 #if 0
01324   kdDebug(5850) << "KOAgenda::placeSubCells()" << endl;
01325   if ( placeItem ) {
01326     Incidence *event = placeItem->incidence();
01327     if ( !event ) {
01328       kdDebug(5850) << "  event is 0" << endl;
01329     } else {
01330       kdDebug(5850) << "  event: " << event->summary() << endl;
01331     }
01332   } else {
01333     kdDebug(5850) << "  placeItem is 0" << endl;
01334   }
01335   kdDebug(5850) << "KOAgenda::placeSubCells()..." << endl;
01336 #endif
01337 
01338   QPtrList<KOrg::CellItem> cells;
01339   KOAgendaItem *item;
01340   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01341     cells.append( item );
01342   }
01343 
01344   QPtrList<KOrg::CellItem> items = KOrg::CellItem::placeItem( cells,
01345                                                               placeItem );
01346 
01347   placeItem->setConflictItems( QPtrList<KOAgendaItem>() );
01348   double newSubCellWidth = calcSubCellWidth( placeItem );
01349   KOrg::CellItem *i;
01350   for ( i = items.first(); i; i = items.next() ) {
01351     item = static_cast<KOAgendaItem *>( i );
01352     placeAgendaItem( item, newSubCellWidth );
01353     item->addConflictItem( placeItem );
01354     placeItem->addConflictItem( item );
01355   }
01356   if ( items.isEmpty() ) {
01357     placeAgendaItem( placeItem, newSubCellWidth );
01358   }
01359   placeItem->update();
01360 }
01361 
01362 int KOAgenda::columnWidth( int column )
01363 {
01364   int start = gridToContents( QPoint( column, 0 ) ).x();
01365   if (KOGlobals::self()->reverseLayout() )
01366     column--;
01367   else
01368     column++;
01369   int end = gridToContents( QPoint( column, 0 ) ).x();
01370   return end - start;
01371 }
01372 /*
01373   Draw grid in the background of the agenda.
01374 */
01375 void KOAgenda::drawContents(QPainter* p, int cx, int cy, int cw, int ch)
01376 {
01377   QPixmap db(cw, ch);
01378   db.fill(KOPrefs::instance()->mAgendaBgColor);
01379   QPainter dbp(&db);
01380   dbp.translate(-cx,-cy);
01381 
01382 //  kdDebug(5850) << "KOAgenda::drawContents()" << endl;
01383   double lGridSpacingY = mGridSpacingY*2;
01384 
01385   // Highlight working hours
01386   if (mWorkingHoursEnable) {
01387     QPoint pt1( cx, mWorkingHoursYTop );
01388     QPoint pt2( cx+cw, mWorkingHoursYBottom );
01389     if ( pt2.x() >= pt1.x() /*&& pt2.y() >= pt1.y()*/) {
01390       int gxStart = contentsToGrid( pt1 ).x();
01391       int gxEnd = contentsToGrid( pt2 ).x();
01392       // correct start/end for rtl layouts
01393       if ( gxStart > gxEnd ) {
01394         int tmp = gxStart;
01395         gxStart = gxEnd;
01396         gxEnd = tmp;
01397       }
01398       int xoffset = ( KOGlobals::self()->reverseLayout()?1:0 );
01399       while( gxStart <= gxEnd ) {
01400         int xStart = gridToContents( QPoint( gxStart+xoffset, 0 ) ).x();
01401         int xWidth = columnWidth( gxStart ) + 1;
01402         if ( pt2.y() < pt1.y() ) {
01403           // overnight working hours
01404           if ( ( (gxStart==0) && !mHolidayMask->at(mHolidayMask->count()-1) ) ||
01405                ( (gxStart>0) && (gxStart<int(mHolidayMask->count())) && (!mHolidayMask->at(gxStart-1) ) ) ) {
01406             if ( pt2.y() > cy ) {
01407               dbp.fillRect( xStart, cy, xWidth, pt2.y() - cy + 1,
01408                             KOPrefs::instance()->mWorkingHoursColor);
01409             }
01410           }
01411           if ( (gxStart < int(mHolidayMask->count()-1)) && (!mHolidayMask->at(gxStart)) ) {
01412             if ( pt1.y() < cy + ch - 1 ) {
01413               dbp.fillRect( xStart, pt1.y(), xWidth, cy + ch - pt1.y() + 1,
01414                             KOPrefs::instance()->mWorkingHoursColor);
01415             }
01416           }
01417         } else {
01418           // last entry in holiday mask denotes the previous day not visible (needed for overnight shifts)
01419           if ( gxStart < int(mHolidayMask->count()-1) && !mHolidayMask->at(gxStart)) {
01420             dbp.fillRect( xStart, pt1.y(), xWidth, pt2.y() - pt1.y() + 1,
01421                           KOPrefs::instance()->mWorkingHoursColor );
01422           }
01423         }
01424         ++gxStart;
01425       }
01426     }
01427   }
01428 
01429   // draw selection
01430   if ( mHasSelection ) {
01431     QPoint pt, pt1;
01432 
01433     if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
01434       // draw start day
01435       pt = gridToContents( mSelectionStartCell );
01436       pt1 = gridToContents( QPoint( mSelectionStartCell.x() + 1, mRows + 1 ) );
01437       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01438       // draw all other days between the start day and the day of the selection end
01439       for ( int c = mSelectionStartCell.x() + 1; c < mSelectionEndCell.x(); ++c ) {
01440         pt = gridToContents( QPoint( c, 0 ) );
01441         pt1 = gridToContents( QPoint( c + 1, mRows + 1 ) );
01442         dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01443       }
01444       // draw end day
01445       pt = gridToContents( QPoint( mSelectionEndCell.x(), 0 ) );
01446       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01447       dbp.fillRect( QRect( pt, pt1), KOPrefs::instance()->mHighlightColor );
01448     }  else { // single day selection
01449       pt = gridToContents( mSelectionStartCell );
01450       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01451       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01452     }
01453   }
01454 
01455   QPen hourPen( KOPrefs::instance()->mAgendaBgColor.dark( 150 ) );
01456   QPen halfHourPen( KOPrefs::instance()->mAgendaBgColor.dark( 125 ) );
01457   dbp.setPen( hourPen );
01458 
01459   // Draw vertical lines of grid, start with the last line not yet visible
01460   //  kdDebug(5850) << "drawContents cx: " << cx << " cy: " << cy << " cw: " << cw << " ch: " << ch << endl;
01461   double x = ( int( cx / mGridSpacingX ) ) * mGridSpacingX;
01462   while (x < cx + cw) {
01463     dbp.drawLine( int( x ), cy, int( x ), cy + ch );
01464     x+=mGridSpacingX;
01465   }
01466 
01467   // Draw horizontal lines of grid
01468   double y = ( int( cy / (2*lGridSpacingY) ) ) * 2 * lGridSpacingY;
01469   while (y < cy + ch) {
01470 //    kdDebug(5850) << " y: " << y << endl;
01471     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01472     y += 2 * lGridSpacingY;
01473   }
01474   y = ( 2 * int( cy / (2*lGridSpacingY) ) + 1) * lGridSpacingY;
01475   dbp.setPen( halfHourPen );
01476   while (y < cy + ch) {
01477 //    kdDebug(5850) << " y: " << y << endl;
01478     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01479     y+=2*lGridSpacingY;
01480   }
01481   p->drawPixmap(cx,cy, db);
01482 }
01483 
01484 /*
01485   Convert srcollview contents coordinates to agenda grid coordinates.
01486 */
01487 QPoint KOAgenda::contentsToGrid ( const QPoint &pos ) const
01488 {
01489   int gx = int( KOGlobals::self()->reverseLayout() ?
01490         mColumns - pos.x()/mGridSpacingX : pos.x()/mGridSpacingX );
01491   int gy = int( pos.y()/mGridSpacingY );
01492   return QPoint( gx, gy );
01493 }
01494 
01495 /*
01496   Convert agenda grid coordinates to scrollview contents coordinates.
01497 */
01498 QPoint KOAgenda::gridToContents( const QPoint &gpos ) const
01499 {
01500   int x = int( KOGlobals::self()->reverseLayout() ?
01501              (mColumns - gpos.x())*mGridSpacingX : gpos.x()*mGridSpacingX );
01502   int y = int( gpos.y()*mGridSpacingY );
01503   return QPoint( x, y );
01504 }
01505 
01506 
01507 /*
01508   Return Y coordinate corresponding to time. Coordinates are rounded to fit into
01509   the grid.
01510 */
01511 int KOAgenda::timeToY(const QTime &time)
01512 {
01513 //  kdDebug(5850) << "Time: " << time.toString() << endl;
01514   int minutesPerCell = 24 * 60 / mRows;
01515 //  kdDebug(5850) << "minutesPerCell: " << minutesPerCell << endl;
01516   int timeMinutes = time.hour() * 60 + time.minute();
01517 //  kdDebug(5850) << "timeMinutes: " << timeMinutes << endl;
01518   int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
01519 //  kdDebug(5850) << "y: " << Y << endl;
01520 //  kdDebug(5850) << "\n" << endl;
01521   return Y;
01522 }
01523 
01524 
01525 /*
01526   Return time corresponding to cell y coordinate. Coordinates are rounded to
01527   fit into the grid.
01528 */
01529 QTime KOAgenda::gyToTime(int gy)
01530 {
01531 //  kdDebug(5850) << "gyToTime: " << gy << endl;
01532   int secondsPerCell = 24 * 60 * 60/ mRows;
01533 
01534   int timeSeconds = secondsPerCell * gy;
01535 
01536   QTime time( 0, 0, 0 );
01537   if ( timeSeconds < 24 * 60 * 60 ) {
01538     time = time.addSecs(timeSeconds);
01539   } else {
01540     time.setHMS( 23, 59, 59 );
01541   }
01542 //  kdDebug(5850) << "  gyToTime: " << time.toString() << endl;
01543 
01544   return time;
01545 }
01546 
01547 QMemArray<int> KOAgenda::minContentsY()
01548 {
01549   QMemArray<int> minArray;
01550   minArray.fill( timeToY( QTime(23, 59) ), mSelectedDates.count() );
01551   for ( KOAgendaItem *item = mItems.first();
01552         item != 0; item = mItems.next() ) {
01553     int ymin = item->cellYTop();
01554     int index = item->cellXLeft();
01555     if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
01556       if ( ymin < minArray[index] && mItemsToDelete.findRef( item ) == -1 )
01557         minArray[index] = ymin;
01558     }
01559   }
01560 
01561   return minArray;
01562 }
01563 
01564 QMemArray<int> KOAgenda::maxContentsY()
01565 {
01566   QMemArray<int> maxArray;
01567   maxArray.fill( timeToY( QTime(0, 0) ), mSelectedDates.count() );
01568   for ( KOAgendaItem *item = mItems.first();
01569         item != 0; item = mItems.next() ) {
01570     int ymax = item->cellYBottom();
01571     int index = item->cellXLeft();
01572     if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
01573       if ( ymax > maxArray[index] && mItemsToDelete.findRef( item ) == -1 )
01574         maxArray[index] = ymax;
01575     }
01576   }
01577 
01578   return maxArray;
01579 }
01580 
01581 void KOAgenda::setStartTime( const QTime &startHour )
01582 {
01583   double startPos = ( startHour.hour()/24. + startHour.minute()/1440. +
01584                       startHour.second()/86400. ) * mRows * gridSpacingY();
01585   setContentsPos( 0, int( startPos ) );
01586 }
01587 
01588 
01589 /*
01590   Insert KOAgendaItem into agenda.
01591 */
01592 KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const QDate &qd, int X,
01593                                     int YTop, int YBottom )
01594 {
01595 #if 0
01596   kdDebug(5850) << "KOAgenda::insertItem:" << incidence->summary() << "-"
01597                 << qd.toString() << " ;top, bottom:" << YTop << "," << YBottom
01598                 << endl;
01599 #endif
01600 
01601   if ( mAllDayMode ) {
01602     kdDebug(5850) << "KOAgenda: calling insertItem in all-day mode is illegal." << endl;
01603     return 0;
01604   }
01605 
01606 
01607   mActionType = NOP;
01608 
01609   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, incidence, qd, viewport() );
01610   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem * ) ),
01611            SLOT( removeAgendaItem( KOAgendaItem * ) ) );
01612   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem * ) ),
01613            SLOT( showAgendaItem( KOAgendaItem * ) ) );
01614 
01615   if ( YBottom <= YTop ) {
01616     kdDebug(5850) << "KOAgenda::insertItem(): Text: " << agendaItem->text() << " YSize<0" << endl;
01617     YBottom = YTop;
01618   }
01619 
01620   agendaItem->resize( int( ( X + 1 ) * mGridSpacingX ) -
01621                       int( X * mGridSpacingX ),
01622                       int( YTop * mGridSpacingY ) -
01623                       int( ( YBottom + 1 ) * mGridSpacingY ) );
01624   agendaItem->setCellXY( X, YTop, YBottom );
01625   agendaItem->setCellXRight( X );
01626   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, incidence ) );
01627   agendaItem->installEventFilter( this );
01628 
01629   addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
01630   mItems.append( agendaItem );
01631 
01632   placeSubCells( agendaItem );
01633 
01634   agendaItem->show();
01635 
01636   marcus_bains();
01637 
01638   return agendaItem;
01639 }
01640 
01641 /*
01642   Insert all-day KOAgendaItem into agenda.
01643 */
01644 KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const QDate &qd,
01645                                           int XBegin, int XEnd )
01646 {
01647   if ( !mAllDayMode ) {
01648     kdDebug(5850) << "KOAgenda: calling insertAllDayItem in non all-day mode is illegal." << endl;
01649     return 0;
01650   }
01651 
01652   mActionType = NOP;
01653 
01654   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, event, qd, viewport() );
01655   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem* ) ),
01656            SLOT( removeAgendaItem( KOAgendaItem* ) ) );
01657   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem* ) ),
01658            SLOT( showAgendaItem( KOAgendaItem* ) ) );
01659 
01660   agendaItem->setCellXY( XBegin, 0, 0 );
01661   agendaItem->setCellXRight( XEnd );
01662 
01663   double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
01664   double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
01665                                    agendaItem->cellXLeft() );
01666 
01667   agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
01668 
01669   agendaItem->installEventFilter( this );
01670   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
01671   addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
01672   mItems.append( agendaItem );
01673 
01674   placeSubCells( agendaItem );
01675 
01676   agendaItem->show();
01677 
01678   return agendaItem;
01679 }
01680 
01681 
01682 void KOAgenda::insertMultiItem (Event *event,const QDate &qd,int XBegin,int XEnd,
01683                                 int YTop,int YBottom)
01684 {
01685   if (mAllDayMode) {
01686     kdDebug(5850) << "KOAgenda: calling insertMultiItem in all-day mode is illegal." << endl;
01687     return;
01688   }
01689   mActionType = NOP;
01690 
01691   int cellX,cellYTop,cellYBottom;
01692   QString newtext;
01693   int width = XEnd - XBegin + 1;
01694   int count = 0;
01695   KOAgendaItem *current = 0;
01696   QPtrList<KOAgendaItem> multiItems;
01697   int visibleCount = mSelectedDates.first().daysTo(mSelectedDates.last());
01698   for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
01699     ++count;
01700     //Only add the items that are visible.
01701     if( cellX >=0 && cellX <= visibleCount ) {
01702       if ( cellX == XBegin ) cellYTop = YTop;
01703       else cellYTop = 0;
01704       if ( cellX == XEnd ) cellYBottom = YBottom;
01705       else cellYBottom = rows() - 1;
01706       newtext = QString("(%1/%2): ").arg( count ).arg( width );
01707       newtext.append( event->summary() );
01708 
01709       current = insertItem( event, qd, cellX, cellYTop, cellYBottom );
01710       current->setText( newtext );
01711       multiItems.append( current );
01712     }
01713   }
01714 
01715   KOAgendaItem *next = 0;
01716   KOAgendaItem *prev = 0;
01717   KOAgendaItem *last = multiItems.last();
01718   KOAgendaItem *first = multiItems.first();
01719   KOAgendaItem *setFirst,*setLast;
01720   current = first;
01721   while (current) {
01722     next = multiItems.next();
01723     if (current == first) setFirst = 0;
01724     else setFirst = first;
01725     if (current == last) setLast = 0;
01726     else setLast = last;
01727 
01728     current->setMultiItem(setFirst, prev, next, setLast);
01729     prev=current;
01730     current = next;
01731   }
01732 
01733   marcus_bains();
01734 }
01735 
01736 void KOAgenda::removeIncidence( Incidence *incidence )
01737 {
01738   // First find all items to be deleted and store them
01739   // in its own list. Otherwise removeAgendaItem will reset
01740   // the current position and mess this up.
01741   QPtrList<KOAgendaItem> itemsToRemove;
01742 
01743   KOAgendaItem *item = mItems.first();
01744   while ( item ) {
01745     if ( item->incidence() == incidence ) {
01746       itemsToRemove.append( item );
01747     }
01748     item = mItems.next();
01749   }
01750   item = itemsToRemove.first();
01751   while ( item ) {
01752     removeAgendaItem( item );
01753     item = itemsToRemove.next();
01754   }
01755 }
01756 
01757 void KOAgenda::showAgendaItem( KOAgendaItem *agendaItem )
01758 {
01759   if ( !agendaItem ) return;
01760   agendaItem->hide();
01761   addChild( agendaItem );
01762   if ( !mItems.containsRef( agendaItem ) )
01763     mItems.append( agendaItem );
01764   placeSubCells( agendaItem );
01765 
01766   agendaItem->show();
01767 }
01768 
01769 bool KOAgenda::removeAgendaItem( KOAgendaItem *item )
01770 {
01771   // we found the item. Let's remove it and update the conflicts
01772   bool taken = false;
01773   KOAgendaItem *thisItem = item;
01774   QPtrList<KOAgendaItem> conflictItems = thisItem->conflictItems();
01775   removeChild( thisItem );
01776   int pos = mItems.find( thisItem );
01777   if ( pos>=0 ) {
01778     mItems.take( pos );
01779     taken = true;
01780   }
01781 
01782   KOAgendaItem *confitem;
01783   for ( confitem = conflictItems.first(); confitem != 0;
01784         confitem = conflictItems.next() ) {
01785     // the item itself is also in its own conflictItems list!
01786     if ( confitem != thisItem ) placeSubCells(confitem);
01787 
01788   }
01789   mItemsToDelete.append( thisItem );
01790   QTimer::singleShot( 0, this, SLOT( deleteItemsToDelete() ) );
01791   return taken;
01792 }
01793 
01794 void KOAgenda::deleteItemsToDelete()
01795 {
01796   mItemsToDelete.clear();
01797 }
01798 
01799 /*QSizePolicy KOAgenda::sizePolicy() const
01800 {
01801   // Thought this would make the all-day event agenda minimum size and the
01802   // normal agenda take the remaining space. But it doesnt work. The QSplitter
01803   // dont seem to think that an Expanding widget needs more space than a
01804   // Preferred one.
01805   // But it doesnt hurt, so it stays.
01806   if (mAllDayMode) {
01807     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
01808   } else {
01809     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
01810   }
01811 }
01812 */
01813 
01814 /*
01815   Overridden from QScrollView to provide proper resizing of KOAgendaItems.
01816 */
01817 void KOAgenda::resizeEvent ( QResizeEvent *ev )
01818 {
01819 //  kdDebug(5850) << "KOAgenda::resizeEvent" << endl;
01820 
01821   QSize newSize( ev->size() );
01822   if (mAllDayMode) {
01823     mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
01824     mGridSpacingY = newSize.height() - 2 * frameWidth();
01825   } else {
01826     int scrollbarWidth = vScrollBarMode() != AlwaysOff ? verticalScrollBar()->width() : 0;
01827     mGridSpacingX = double( newSize.width() - scrollbarWidth - 2 * frameWidth()) / double(mColumns);
01828     // make sure that there are not more than 24 per day
01829     mGridSpacingY = double(newSize.height() - 2 * frameWidth()) / double(mRows);
01830     if ( mGridSpacingY < mDesiredGridSpacingY )
01831       mGridSpacingY = mDesiredGridSpacingY;
01832   }
01833   calculateWorkingHours();
01834   QTimer::singleShot( 0, this, SLOT( resizeAllContents() ) );
01835   emit gridSpacingYChanged( mGridSpacingY * 4 );
01836   QScrollView::resizeEvent(ev);
01837 }
01838 
01839 void KOAgenda::resizeAllContents()
01840 {
01841   double subCellWidth;
01842   if ( mItems.count() > 0 ) {
01843     KOAgendaItem *item;
01844     if (mAllDayMode) {
01845       for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01846         subCellWidth = calcSubCellWidth( item );
01847         placeAgendaItem( item, subCellWidth );
01848       }
01849     } else {
01850       for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01851         subCellWidth = calcSubCellWidth( item );
01852         placeAgendaItem( item, subCellWidth );
01853       }
01854     }
01855   }
01856   checkScrollBoundaries();
01857   marcus_bains();
01858 }
01859 
01860 void KOAgenda::scrollUp()
01861 {
01862   scrollBy(0,-mScrollOffset);
01863 }
01864 
01865 
01866 void KOAgenda::scrollDown()
01867 {
01868   scrollBy(0,mScrollOffset);
01869 }
01870 
01871 
01872 /*
01873   Calculates the minimum width
01874 */
01875 int KOAgenda::minimumWidth() const
01876 {
01877   // FIXME:: develop a way to dynamically determine the minimum width
01878   int min = 100;
01879 
01880   return min;
01881 }
01882 
01883 void KOAgenda::updateConfig()
01884 {
01885   double oldGridSpacingY = mGridSpacingY;
01886 
01887   mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
01888   if ( mDesiredGridSpacingY < 4 || mDesiredGridSpacingY > 30 ) {
01889     mDesiredGridSpacingY = 10;
01890   }
01891 
01892   // make sure that there are not more than 24 per day
01893   mGridSpacingY = (double)height() / (double)mRows;
01894   if ( mGridSpacingY < mDesiredGridSpacingY ) {
01895     mGridSpacingY = mDesiredGridSpacingY;
01896   }
01897 
01898   //can be two doubles equal?, it's better to compare them with an epsilon
01899   if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 ) {
01900     resizeContents( int( mGridSpacingX * mColumns ),
01901                     int( mGridSpacingY * mRows ) );
01902   }
01903 
01904   calculateWorkingHours();
01905 
01906   marcus_bains();
01907 }
01908 
01909 void KOAgenda::checkScrollBoundaries()
01910 {
01911   // Invalidate old values to force update
01912   mOldLowerScrollValue = -1;
01913   mOldUpperScrollValue = -1;
01914 
01915   checkScrollBoundaries(verticalScrollBar()->value());
01916 }
01917 
01918 void KOAgenda::checkScrollBoundaries( int v )
01919 {
01920   int yMin = int( (v) / mGridSpacingY );
01921   int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
01922 
01923 //  kdDebug(5850) << "--- yMin: " << yMin << "  yMax: " << yMax << endl;
01924 
01925   if ( yMin != mOldLowerScrollValue ) {
01926     mOldLowerScrollValue = yMin;
01927     emit lowerYChanged(yMin);
01928   }
01929   if ( yMax != mOldUpperScrollValue ) {
01930     mOldUpperScrollValue = yMax;
01931     emit upperYChanged(yMax);
01932   }
01933 }
01934 
01935 int KOAgenda::visibleContentsYMin()
01936 {
01937   int v = verticalScrollBar()->value();
01938   return int( v / mGridSpacingY );
01939 }
01940 
01941 int KOAgenda::visibleContentsYMax()
01942 {
01943   int v = verticalScrollBar()->value();
01944   return int( ( v + visibleHeight() ) / mGridSpacingY );
01945 }
01946 
01947 void KOAgenda::deselectItem()
01948 {
01949   if (mSelectedItem.isNull()) return;
01950   mSelectedItem->select(false);
01951   mSelectedItem = 0;
01952 }
01953 
01954 void KOAgenda::selectItem(KOAgendaItem *item)
01955 {
01956   if ((KOAgendaItem *)mSelectedItem == item) return;
01957   deselectItem();
01958   if (item == 0) {
01959     emit incidenceSelected( 0, QDate() );
01960     return;
01961   }
01962   mSelectedItem = item;
01963   mSelectedItem->select();
01964   assert( mSelectedItem->incidence() );
01965   mSelectedUid = mSelectedItem->incidence()->uid();
01966   emit incidenceSelected( mSelectedItem->incidence(), mSelectedItem->itemDate() );
01967 }
01968 
01969 void KOAgenda::selectItemByUID( const QString& uid )
01970 {
01971   KOAgendaItem *item;
01972   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01973     if( item->incidence() && item->incidence()->uid() == uid ) {
01974       selectItem( item );
01975       break;
01976     }
01977   }
01978 }
01979 
01980 // This function seems never be called.
01981 void KOAgenda::keyPressEvent( QKeyEvent *kev )
01982 {
01983   switch(kev->key()) {
01984     case Key_PageDown:
01985       verticalScrollBar()->addPage();
01986       break;
01987     case Key_PageUp:
01988       verticalScrollBar()->subtractPage();
01989       break;
01990     case Key_Down:
01991       verticalScrollBar()->addLine();
01992       break;
01993     case Key_Up:
01994       verticalScrollBar()->subtractLine();
01995       break;
01996     default:
01997       ;
01998   }
01999 }
02000 
02001 void KOAgenda::calculateWorkingHours()
02002 {
02003   mWorkingHoursEnable = !mAllDayMode;
02004 
02005   QTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
02006   mWorkingHoursYTop = int( 4 * mGridSpacingY *
02007                            ( tmp.hour() + tmp.minute() / 60. +
02008                              tmp.second() / 3600. ) );
02009   tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
02010   mWorkingHoursYBottom = int( 4 * mGridSpacingY *
02011                               ( tmp.hour() + tmp.minute() / 60. +
02012                                 tmp.second() / 3600. ) - 1 );
02013 }
02014 
02015 
02016 DateList KOAgenda::dateList() const
02017 {
02018     return mSelectedDates;
02019 }
02020 
02021 void KOAgenda::setDateList(const DateList &selectedDates)
02022 {
02023     mSelectedDates = selectedDates;
02024     marcus_bains();
02025 }
02026 
02027 void KOAgenda::setHolidayMask(QMemArray<bool> *mask)
02028 {
02029   mHolidayMask = mask;
02030 
02031 }
02032 
02033 void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
02034 {
02035   kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
02036   QScrollView::contentsMousePressEvent(event);
02037 }
02038 
02039 void KOAgenda::setTypeAheadReceiver( QObject *o )
02040 {
02041   mTypeAheadReceiver = o;
02042 }
02043 
02044 QObject *KOAgenda::typeAheadReceiver() const
02045 {
02046   return mTypeAheadReceiver;
02047 }
KDE Home | KDE Accessibility Home | Description of Access Keys