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