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   int mins = tim.hour()*60 + tim.minute();
00131   int minutesPerCell = 24 * 60 / agenda->rows();
00132   int y = int( mins * agenda->gridSpacingY() / minutesPerCell );
00133   int today = recalculate ? todayColumn() : mOldToday;
00134   int x = int( agenda->gridSpacingX() * today );
00135 
00136   mOldTime = tim;
00137   mOldToday = today;
00138 
00139   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   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 
01250 double KOAgenda::calcSubCellWidth( KOAgendaItem::GPtr item )
01251 {
01252   if ( !item ) {
01253     return 0;
01254   }
01255 
01256   QPoint pt, pt1;
01257   pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01258   pt1 = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) +
01259                         QPoint( 1, 1 ) );
01260   pt1 -= pt;
01261   int maxSubCells = item->subCells();
01262   double newSubCellWidth;
01263   if ( mAllDayMode ) {
01264     newSubCellWidth = double( pt1.y() ) / maxSubCells;
01265   } else {
01266     newSubCellWidth = double( pt1.x() ) / maxSubCells;
01267   }
01268   return newSubCellWidth;
01269 }
01270 
01271 void KOAgenda::adjustItemPosition( KOAgendaItem::GPtr item )
01272 {
01273   if (!item) return;
01274   item->resize( int( mGridSpacingX * item->cellWidth() ),
01275                 int( mGridSpacingY * item->cellHeight() ) );
01276   int clXLeft = item->cellXLeft();
01277   if ( KOGlobals::self()->reverseLayout() )
01278     clXLeft = item->cellXRight() + 1;
01279   QPoint cpos = gridToContents( QPoint( clXLeft, item->cellYTop() ) );
01280   moveChild( item, cpos.x(), cpos.y() );
01281 }
01282 
01283 void KOAgenda::placeAgendaItem( KOAgendaItem::GPtr item, double subCellWidth )
01284 {
01285 //  kdDebug(5850) << "KOAgenda::placeAgendaItem(): " << item->incidence()->summary()
01286 //            << " subCellWidth: " << subCellWidth << endl;
01287 
01288   if ( !item ) {
01289     return;
01290   }
01291 
01292   // "left" upper corner, no subcells yet, RTL layouts have right/left switched, widths are negative then
01293   QPoint pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01294   // right lower corner
01295   QPoint pt1 = gridToContents( QPoint( item->cellXLeft() + item->cellWidth(),
01296                                    item->cellYBottom()+1 ) );
01297 
01298   double subCellPos = item->subCell() * subCellWidth;
01299 
01300   // we need to add 0.01 to make sure we don't loose one pixed due to
01301   // numerics (i.e. if it would be x.9998, we want the integer, not rounded down.
01302   double delta=0.01;
01303   if (subCellWidth<0) delta=-delta;
01304   int height, width, xpos, ypos;
01305   if (mAllDayMode) {
01306     width = pt1.x()-pt.x();
01307     height = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01308     xpos = pt.x();
01309     ypos = pt.y() + int( subCellPos );
01310   } else {
01311     width = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01312     height = pt1.y()-pt.y();
01313     xpos = pt.x() + int( subCellPos );
01314     ypos = pt.y();
01315   }
01316   if ( KOGlobals::self()->reverseLayout() ) { // RTL language/layout
01317     xpos += width;
01318     width = -width;
01319   }
01320   if ( height<0 ) { // BTT (bottom-to-top) layout ?!?
01321     ypos += height;
01322     height = -height;
01323   }
01324   item->resize( width, height );
01325   moveChild( item, xpos, ypos );
01326 }
01327 
01328 /*
01329   Place item in cell and take care that multiple items using the same cell do
01330   not overlap. This method is not yet optimal. It doesn't use the maximum space
01331   it can get in all cases.
01332   At the moment the method has a bug: When an item is placed only the sub cell
01333   widths of the items are changed, which are within the Y region the item to
01334   place spans. When the sub cell width change of one of this items affects a
01335   cell, where other items are, which do not overlap in Y with the item to place,
01336   the display gets corrupted, although the corruption looks quite nice.
01337 */
01338 void KOAgenda::placeSubCells( KOAgendaItem::GPtr placeItem )
01339 {
01340 #if 0
01341   kdDebug(5850) << "KOAgenda::placeSubCells()" << endl;
01342   if ( placeItem ) {
01343     Incidence *event = placeItem->incidence();
01344     if ( !event ) {
01345       kdDebug(5850) << "  event is 0" << endl;
01346     } else {
01347       kdDebug(5850) << "  event: " << event->summary() << endl;
01348     }
01349   } else {
01350     kdDebug(5850) << "  placeItem is 0" << endl;
01351   }
01352   kdDebug(5850) << "KOAgenda::placeSubCells()..." << endl;
01353 #endif
01354 
01355   if ( !placeItem ) {
01356     return;
01357   }
01358 
01359   QPtrList<KOrg::CellItem> cells;
01360   AgendaItemList::Iterator it;
01361   for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01362     if ( *it ) {
01363       cells.append( *it );
01364     }
01365   }
01366 
01367   QPtrList<KOrg::CellItem> items = KOrg::CellItem::placeItem( cells,
01368                                                               placeItem );
01369 
01370   placeItem->setConflictItems( AgendaItemList() );
01371   double newSubCellWidth = calcSubCellWidth( placeItem );
01372   KOrg::CellItem *i;
01373   for ( i = items.first(); i; i = items.next() ) {
01374     KOAgendaItem *item = static_cast<KOAgendaItem *>( i );
01375     placeAgendaItem( item, newSubCellWidth );
01376     item->addConflictItem( placeItem );
01377     placeItem->addConflictItem( item );
01378   }
01379   if ( items.isEmpty() ) {
01380     placeAgendaItem( placeItem, newSubCellWidth );
01381   }
01382   placeItem->update();
01383 }
01384 
01385 int KOAgenda::columnWidth( int column )
01386 {
01387   int start = gridToContents( QPoint( column, 0 ) ).x();
01388   if (KOGlobals::self()->reverseLayout() )
01389     column--;
01390   else
01391     column++;
01392   int end = gridToContents( QPoint( column, 0 ) ).x();
01393   return end - start;
01394 }
01395 /*
01396   Draw grid in the background of the agenda.
01397 */
01398 void KOAgenda::drawContents(QPainter* p, int cx, int cy, int cw, int ch)
01399 {
01400   QPixmap db(cw, ch);
01401   db.fill(KOPrefs::instance()->mAgendaBgColor);
01402   QPainter dbp(&db);
01403   dbp.translate(-cx,-cy);
01404 
01405 //  kdDebug(5850) << "KOAgenda::drawContents()" << endl;
01406   double lGridSpacingY = mGridSpacingY*2;
01407 
01408   // Highlight working hours
01409   if (mWorkingHoursEnable) {
01410     QPoint pt1( cx, mWorkingHoursYTop );
01411     QPoint pt2( cx+cw, mWorkingHoursYBottom );
01412     if ( pt2.x() >= pt1.x() /*&& pt2.y() >= pt1.y()*/) {
01413       int gxStart = contentsToGrid( pt1 ).x();
01414       int gxEnd = contentsToGrid( pt2 ).x();
01415       // correct start/end for rtl layouts
01416       if ( gxStart > gxEnd ) {
01417         int tmp = gxStart;
01418         gxStart = gxEnd;
01419         gxEnd = tmp;
01420       }
01421       int xoffset = ( KOGlobals::self()->reverseLayout()?1:0 );
01422       while( gxStart <= gxEnd ) {
01423         int xStart = gridToContents( QPoint( gxStart+xoffset, 0 ) ).x();
01424         int xWidth = columnWidth( gxStart ) + 1;
01425         if ( pt2.y() < pt1.y() ) {
01426           // overnight working hours
01427           if ( ( (gxStart==0) && !mHolidayMask->at(mHolidayMask->count()-1) ) ||
01428                ( (gxStart>0) && (gxStart<int(mHolidayMask->count())) && (!mHolidayMask->at(gxStart-1) ) ) ) {
01429             if ( pt2.y() > cy ) {
01430               dbp.fillRect( xStart, cy, xWidth, pt2.y() - cy + 1,
01431                             KOPrefs::instance()->mWorkingHoursColor);
01432             }
01433           }
01434           if ( (gxStart < int(mHolidayMask->count()-1)) && (!mHolidayMask->at(gxStart)) ) {
01435             if ( pt1.y() < cy + ch - 1 ) {
01436               dbp.fillRect( xStart, pt1.y(), xWidth, cy + ch - pt1.y() + 1,
01437                             KOPrefs::instance()->mWorkingHoursColor);
01438             }
01439           }
01440         } else {
01441           // last entry in holiday mask denotes the previous day not visible (needed for overnight shifts)
01442           if ( gxStart < int(mHolidayMask->count()-1) && !mHolidayMask->at(gxStart)) {
01443             dbp.fillRect( xStart, pt1.y(), xWidth, pt2.y() - pt1.y() + 1,
01444                           KOPrefs::instance()->mWorkingHoursColor );
01445           }
01446         }
01447         ++gxStart;
01448       }
01449     }
01450   }
01451 
01452   // draw selection
01453   if ( mHasSelection ) {
01454     QPoint pt, pt1;
01455 
01456     if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
01457       // draw start day
01458       pt = gridToContents( mSelectionStartCell );
01459       pt1 = gridToContents( QPoint( mSelectionStartCell.x() + 1, mRows + 1 ) );
01460       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01461       // draw all other days between the start day and the day of the selection end
01462       for ( int c = mSelectionStartCell.x() + 1; c < mSelectionEndCell.x(); ++c ) {
01463         pt = gridToContents( QPoint( c, 0 ) );
01464         pt1 = gridToContents( QPoint( c + 1, mRows + 1 ) );
01465         dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01466       }
01467       // draw end day
01468       pt = gridToContents( QPoint( mSelectionEndCell.x(), 0 ) );
01469       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01470       dbp.fillRect( QRect( pt, pt1), KOPrefs::instance()->mHighlightColor );
01471     }  else { // single day selection
01472       pt = gridToContents( mSelectionStartCell );
01473       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01474       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01475     }
01476   }
01477 
01478   QPen hourPen( KOPrefs::instance()->mAgendaBgColor.dark( 150 ) );
01479   QPen halfHourPen( KOPrefs::instance()->mAgendaBgColor.dark( 125 ) );
01480   dbp.setPen( hourPen );
01481 
01482   // Draw vertical lines of grid, start with the last line not yet visible
01483   //  kdDebug(5850) << "drawContents cx: " << cx << " cy: " << cy << " cw: " << cw << " ch: " << ch << endl;
01484   double x = ( int( cx / mGridSpacingX ) ) * mGridSpacingX;
01485   while (x < cx + cw) {
01486     dbp.drawLine( int( x ), cy, int( x ), cy + ch );
01487     x+=mGridSpacingX;
01488   }
01489 
01490   // Draw horizontal lines of grid
01491   double y = ( int( cy / (2*lGridSpacingY) ) ) * 2 * lGridSpacingY;
01492   while (y < cy + ch) {
01493 //    kdDebug(5850) << " y: " << y << endl;
01494     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01495     y += 2 * lGridSpacingY;
01496   }
01497   y = ( 2 * int( cy / (2*lGridSpacingY) ) + 1) * lGridSpacingY;
01498   dbp.setPen( halfHourPen );
01499   while (y < cy + ch) {
01500 //    kdDebug(5850) << " y: " << y << endl;
01501     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01502     y+=2*lGridSpacingY;
01503   }
01504   p->drawPixmap(cx,cy, db);
01505 }
01506 
01507 /*
01508   Convert srcollview contents coordinates to agenda grid coordinates.
01509 */
01510 QPoint KOAgenda::contentsToGrid ( const QPoint &pos ) const
01511 {
01512   int gx = int( KOGlobals::self()->reverseLayout() ?
01513         mColumns - pos.x()/mGridSpacingX : pos.x()/mGridSpacingX );
01514   int gy = int( pos.y()/mGridSpacingY );
01515   return QPoint( gx, gy );
01516 }
01517 
01518 /*
01519   Convert agenda grid coordinates to scrollview contents coordinates.
01520 */
01521 QPoint KOAgenda::gridToContents( const QPoint &gpos ) const
01522 {
01523   int x = int( KOGlobals::self()->reverseLayout() ?
01524              (mColumns - gpos.x())*mGridSpacingX : gpos.x()*mGridSpacingX );
01525   int y = int( gpos.y()*mGridSpacingY );
01526   return QPoint( x, y );
01527 }
01528 
01529 
01530 /*
01531   Return Y coordinate corresponding to time. Coordinates are rounded to fit into
01532   the grid.
01533 */
01534 int KOAgenda::timeToY(const QTime &time)
01535 {
01536 //  kdDebug(5850) << "Time: " << time.toString() << endl;
01537   int minutesPerCell = 24 * 60 / mRows;
01538 //  kdDebug(5850) << "minutesPerCell: " << minutesPerCell << endl;
01539   int timeMinutes = time.hour() * 60 + time.minute();
01540 //  kdDebug(5850) << "timeMinutes: " << timeMinutes << endl;
01541   int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
01542 //  kdDebug(5850) << "y: " << Y << endl;
01543 //  kdDebug(5850) << "\n" << endl;
01544   return Y;
01545 }
01546 
01547 
01548 /*
01549   Return time corresponding to cell y coordinate. Coordinates are rounded to
01550   fit into the grid.
01551 */
01552 QTime KOAgenda::gyToTime(int gy)
01553 {
01554 //  kdDebug(5850) << "gyToTime: " << gy << endl;
01555   int secondsPerCell = 24 * 60 * 60/ mRows;
01556 
01557   int timeSeconds = secondsPerCell * gy;
01558 
01559   QTime time( 0, 0, 0 );
01560   if ( timeSeconds < 24 * 60 * 60 ) {
01561     time = time.addSecs(timeSeconds);
01562   } else {
01563     time.setHMS( 23, 59, 59 );
01564   }
01565 //  kdDebug(5850) << "  gyToTime: " << time.toString() << endl;
01566 
01567   return time;
01568 }
01569 
01570 QMemArray<int> KOAgenda::minContentsY()
01571 {
01572   QMemArray<int> minArray;
01573   minArray.fill( timeToY( QTime(23, 59) ), mSelectedDates.count() );
01574 
01575   AgendaItemList::Iterator it;
01576   for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01577     if ( *it ) {
01578       const int ymin = (*it)->cellYTop();
01579       const int index = (*it)->cellXLeft();
01580       if ( index >= 0 && index < static_cast<int>( mSelectedDates.count() ) ) {
01581         if ( ymin < minArray[index] && !mItemsToDelete.contains( *it ) )
01582           minArray[index] = ymin;
01583       }
01584     }
01585   }
01586 
01587   return minArray;
01588 }
01589 
01590 QMemArray<int> KOAgenda::maxContentsY()
01591 {
01592   QMemArray<int> maxArray;
01593   maxArray.fill( timeToY( QTime(0, 0) ), mSelectedDates.count() );
01594   AgendaItemList::Iterator it;
01595   for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01596     if ( *it ) {
01597       const int ymax = (*it)->cellYBottom();
01598       const int index = (*it)->cellXLeft();
01599       if ( index >= 0 && index < static_cast<int>( mSelectedDates.count() ) ) {
01600         if ( ymax > maxArray[index] && !mItemsToDelete.contains( *it ) )
01601           maxArray[index] = ymax;
01602       }
01603     }
01604   }
01605 
01606   return maxArray;
01607 }
01608 
01609 void KOAgenda::setStartTime( const QTime &startHour )
01610 {
01611   const double startPos = ( startHour.hour()/24. + startHour.minute()/1440. +
01612                             startHour.second()/86400. ) * mRows * gridSpacingY();
01613   setContentsPos( 0, static_cast<int>( startPos ) );
01614 }
01615 
01616 
01617 /*
01618   Insert KOAgendaItem into agenda.
01619 */
01620 KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const QDate &qd, int X,
01621                                     int YTop, int YBottom, int itemPos, int itemCount )
01622 {
01623   if ( mAllDayMode ) {
01624     kdDebug(5850) << "KOAgenda: calling insertItem in all-day mode is illegal." << endl;
01625     return 0;
01626   }
01627 
01628   mActionType = NOP;
01629 
01630   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, incidence, qd, viewport(), itemPos, itemCount );
01631   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem::GPtr  ) ),
01632            SLOT( removeAgendaItem( KOAgendaItem::GPtr  ) ) );
01633   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem::GPtr  ) ),
01634            SLOT( showAgendaItem( KOAgendaItem::GPtr  ) ) );
01635 
01636   if ( YBottom <= YTop ) {
01637     kdDebug(5850) << "KOAgenda::insertItem(): Text: " << agendaItem->text() << " YSize<0" << endl;
01638     YBottom = YTop;
01639   }
01640 
01641   agendaItem->resize( int( ( X + 1 ) * mGridSpacingX ) -
01642                       int( X * mGridSpacingX ),
01643                       int( YTop * mGridSpacingY ) -
01644                       int( ( YBottom + 1 ) * mGridSpacingY ) );
01645   agendaItem->setCellXY( X, YTop, YBottom );
01646   agendaItem->setCellXRight( X );
01647   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, incidence ) );
01648   agendaItem->installEventFilter( this );
01649 
01650   addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
01651   mItems.append( agendaItem );
01652 
01653   placeSubCells( agendaItem );
01654 
01655   agendaItem->show();
01656 
01657   marcus_bains();
01658 
01659   return agendaItem;
01660 }
01661 
01662 /*
01663   Insert all-day KOAgendaItem into agenda.
01664 */
01665 KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const QDate &qd,
01666                                           int XBegin, int XEnd )
01667 {
01668   if ( !mAllDayMode ) {
01669     kdDebug(5850) << "KOAgenda: calling insertAllDayItem in non all-day mode is illegal." << endl;
01670     return 0;
01671   }
01672 
01673   mActionType = NOP;
01674 
01675   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, event, qd, viewport(), 1, 1 );
01676   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem::GPtr ) ),
01677            SLOT( removeAgendaItem( KOAgendaItem::GPtr ) ) );
01678   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem::GPtr ) ),
01679            SLOT( showAgendaItem( KOAgendaItem::GPtr ) ) );
01680 
01681   agendaItem->setCellXY( XBegin, 0, 0 );
01682   agendaItem->setCellXRight( XEnd );
01683 
01684   double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
01685   double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
01686                                    agendaItem->cellXLeft() );
01687 
01688   agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
01689 
01690   agendaItem->installEventFilter( this );
01691   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
01692   addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
01693   mItems.append( agendaItem );
01694 
01695   placeSubCells( agendaItem );
01696 
01697   agendaItem->show();
01698 
01699   return agendaItem;
01700 }
01701 
01702 
01703 void KOAgenda::insertMultiItem( Event *event, const QDate &qd, int XBegin, int XEnd,
01704                                 int YTop, int YBottom )
01705 {
01706   if ( mAllDayMode ) {
01707     kdDebug(5850) << "KOAgenda: calling insertMultiItem in all-day mode is illegal." << endl;
01708     return;
01709   }
01710   mActionType = NOP;
01711 
01712   int cellX,cellYTop,cellYBottom;
01713   QString newtext;
01714   int width = XEnd - XBegin + 1;
01715   int count = 0;
01716   KOAgendaItem *current = 0;
01717   QPtrList<KOAgendaItem> multiItems;
01718   const int visibleCount = mSelectedDates.first().daysTo( mSelectedDates.last() );
01719   for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
01720     ++count;
01721     //Only add the items that are visible.
01722     if( cellX >= 0 && cellX <= visibleCount ) {
01723       if ( cellX == XBegin ) {
01724         cellYTop = YTop;
01725       } else {
01726         cellYTop = 0;
01727       }
01728 
01729       if ( cellX == XEnd ) {
01730         cellYBottom = YBottom;
01731       } else {
01732         cellYBottom = rows() - 1;
01733       }
01734 
01735       newtext = QString("(%1/%2): ").arg( count ).arg( width );
01736       newtext.append( event->summary() );
01737 
01738       current = insertItem( event, qd, cellX, cellYTop, cellYBottom, count, width );
01739       current->setText( newtext );
01740       multiItems.append( current );
01741     }
01742   }
01743   QPtrList<KOAgendaItem>::iterator it = multiItems.begin();
01744   QPtrList<KOAgendaItem>::iterator e = multiItems.end();
01745 
01746   if ( it != e ) { // .first asserts if the list is empty
01747     KOAgendaItem *first = multiItems.first();
01748     KOAgendaItem *last = multiItems.last();
01749     KOAgendaItem *prev = 0, *next = 0;
01750 
01751     while ( it != e ) {
01752       KOAgendaItem *item = *it;
01753       ++it;
01754       next = ( it == e ) ? 0 : (*it);
01755       if ( item ) {
01756         item->setMultiItem( ( item == first ) ? 0 : first,
01757                             prev, next,
01758                             ( item == last ) ? 0 : last );
01759       }
01760       prev = item;
01761     }
01762   }
01763 
01764   marcus_bains();
01765 }
01766 
01767 void KOAgenda::removeIncidence( Incidence *incidence )
01768 {
01769   // First find all items to be deleted and store them
01770   // in its own list. Otherwise removeAgendaItem will reset
01771   // the current position and mess this up.
01772   QValueList<KOAgendaItem::GPtr > itemsToRemove;
01773 
01774   AgendaItemList::Iterator it;
01775   for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01776     if ( *it ) {
01777       if ( (*it)->incidence() == incidence ) {
01778         itemsToRemove.append( *it );
01779       }
01780     }
01781   }
01782 
01783   for ( it = itemsToRemove.begin(); it != itemsToRemove.end(); ++it ) {
01784     removeAgendaItem( *it );
01785   }
01786 }
01787 
01788 void KOAgenda::showAgendaItem( KOAgendaItem::GPtr agendaItem )
01789 {
01790   if ( !agendaItem ) {
01791     return;
01792   }
01793 
01794   agendaItem->hide();
01795   addChild( agendaItem );
01796   if ( !mItems.contains( agendaItem ) ) {
01797     mItems.append( agendaItem );
01798   }
01799   placeSubCells( agendaItem );
01800 
01801   agendaItem->show();
01802 }
01803 
01804 bool KOAgenda::removeAgendaItem( KOAgendaItem::GPtr item )
01805 {
01806   // we found the item. Let's remove it and update the conflicts
01807   bool taken = false;
01808   KOAgendaItem::GPtr thisItem = item;
01809   AgendaItemList conflictItems = thisItem->conflictItems();
01810   removeChild( thisItem );
01811 
01812   AgendaItemList::Iterator it = mItems.find( thisItem );
01813   if ( it != mItems.end() ) {
01814     mItems.remove( it ); // Don't free memory, QPtrList::take() was used here.
01815     taken = true;
01816   }
01817 
01818   for ( it = conflictItems.begin(); it != conflictItems.end(); ++it ) {
01819     // the item itself is also in its own conflictItems list!
01820     if ( *it != thisItem ) {
01821       placeSubCells( *it );
01822     }
01823   }
01824   mItemsToDelete.append( thisItem );
01825   QTimer::singleShot( 0, this, SLOT( deleteItemsToDelete() ) );
01826   return taken;
01827 }
01828 
01829 void KOAgenda::deleteItemsToDelete()
01830 {
01831   freeItemList( mItemsToDelete );
01832   mItemsToDelete.clear();
01833 }
01834 
01835 /*QSizePolicy KOAgenda::sizePolicy() const
01836 {
01837   // Thought this would make the all-day event agenda minimum size and the
01838   // normal agenda take the remaining space. But it doesnt work. The QSplitter
01839   // dont seem to think that an Expanding widget needs more space than a
01840   // Preferred one.
01841   // But it doesnt hurt, so it stays.
01842   if (mAllDayMode) {
01843     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
01844   } else {
01845     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
01846   }
01847 }
01848 */
01849 
01850 /*
01851   Overridden from QScrollView to provide proper resizing of KOAgendaItems.
01852 */
01853 void KOAgenda::resizeEvent ( QResizeEvent *ev )
01854 {
01855 //  kdDebug(5850) << "KOAgenda::resizeEvent" << endl;
01856 
01857   QSize newSize( ev->size() );
01858   if (mAllDayMode) {
01859     mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
01860     mGridSpacingY = newSize.height() - 2 * frameWidth();
01861   } else {
01862     int scrollbarWidth = vScrollBarMode() != AlwaysOff ? verticalScrollBar()->width() : 0;
01863     mGridSpacingX = double( newSize.width() - scrollbarWidth - 2 * frameWidth()) / double(mColumns);
01864     // make sure that there are not more than 24 per day
01865     mGridSpacingY = double(newSize.height() - 2 * frameWidth()) / double(mRows);
01866     if ( mGridSpacingY < mDesiredGridSpacingY )
01867       mGridSpacingY = mDesiredGridSpacingY;
01868   }
01869   calculateWorkingHours();
01870   QTimer::singleShot( 0, this, SLOT( resizeAllContents() ) );
01871   emit gridSpacingYChanged( mGridSpacingY * 4 );
01872   QScrollView::resizeEvent(ev);
01873 }
01874 
01875 void KOAgenda::resizeAllContents()
01876 {
01877   double subCellWidth;
01878   if ( mItems.count() > 0 ) {
01879     KOAgendaItem::GPtr item;
01880     if ( mAllDayMode ) {
01881       AgendaItemList::Iterator it;
01882       for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01883         if ( *it ) {
01884           subCellWidth = calcSubCellWidth( *it );
01885           placeAgendaItem( *it, subCellWidth );
01886         }
01887       }
01888     } else {
01889       AgendaItemList::Iterator it;
01890       for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01891         if ( *it ) {
01892           subCellWidth = calcSubCellWidth( *it );
01893           placeAgendaItem( *it, subCellWidth );
01894         }
01895       }
01896     }
01897   }
01898   checkScrollBoundaries();
01899   marcus_bains();
01900 }
01901 
01902 void KOAgenda::scrollUp()
01903 {
01904   scrollBy(0,-mScrollOffset);
01905 }
01906 
01907 
01908 void KOAgenda::scrollDown()
01909 {
01910   scrollBy(0,mScrollOffset);
01911 }
01912 
01913 
01914 /*
01915   Calculates the minimum width
01916 */
01917 int KOAgenda::minimumWidth() const
01918 {
01919   // FIXME:: develop a way to dynamically determine the minimum width
01920   int min = 100;
01921 
01922   return min;
01923 }
01924 
01925 void KOAgenda::updateConfig()
01926 {
01927   double oldGridSpacingY = mGridSpacingY;
01928 
01929   mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
01930   if ( mDesiredGridSpacingY < 4 || mDesiredGridSpacingY > 30 ) {
01931     mDesiredGridSpacingY = 10;
01932   }
01933 
01934   // make sure that there are not more than 24 per day
01935   mGridSpacingY = (double)height() / (double)mRows;
01936   if ( mGridSpacingY < mDesiredGridSpacingY ) {
01937     mGridSpacingY = mDesiredGridSpacingY;
01938   }
01939 
01940   //can be two doubles equal?, it's better to compare them with an epsilon
01941   if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 ) {
01942     resizeContents( int( mGridSpacingX * mColumns ),
01943                     int( mGridSpacingY * mRows ) );
01944   }
01945 
01946   calculateWorkingHours();
01947 
01948   marcus_bains();
01949 }
01950 
01951 void KOAgenda::checkScrollBoundaries()
01952 {
01953   // Invalidate old values to force update
01954   mOldLowerScrollValue = -1;
01955   mOldUpperScrollValue = -1;
01956 
01957   checkScrollBoundaries(verticalScrollBar()->value());
01958 }
01959 
01960 void KOAgenda::checkScrollBoundaries( int v )
01961 {
01962   int yMin = int( (v) / mGridSpacingY );
01963   int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
01964 
01965 //  kdDebug(5850) << "--- yMin: " << yMin << "  yMax: " << yMax << endl;
01966 
01967   if ( yMin != mOldLowerScrollValue ) {
01968     mOldLowerScrollValue = yMin;
01969     emit lowerYChanged(yMin);
01970   }
01971   if ( yMax != mOldUpperScrollValue ) {
01972     mOldUpperScrollValue = yMax;
01973     emit upperYChanged(yMax);
01974   }
01975 }
01976 
01977 int KOAgenda::visibleContentsYMin()
01978 {
01979   int v = verticalScrollBar()->value();
01980   return int( v / mGridSpacingY );
01981 }
01982 
01983 int KOAgenda::visibleContentsYMax()
01984 {
01985   int v = verticalScrollBar()->value();
01986   return int( ( v + visibleHeight() ) / mGridSpacingY );
01987 }
01988 
01989 void KOAgenda::deselectItem()
01990 {
01991   if ( mSelectedItem.isNull() ) {
01992     return;
01993   }
01994   mSelectedItem->select(false);
01995   mSelectedItem = 0;
01996 }
01997 
01998 void KOAgenda::selectItem( KOAgendaItem::GPtr item )
01999 {
02000   if ((KOAgendaItem *)mSelectedItem == item) return;
02001   deselectItem();
02002   if ( !item ) {
02003     emit incidenceSelected( 0, QDate() );
02004     return;
02005   }
02006   mSelectedItem = item;
02007   mSelectedItem->select();
02008   assert( mSelectedItem->incidence() );
02009   mSelectedUid = mSelectedItem->incidence()->uid();
02010   emit incidenceSelected( mSelectedItem->incidence(), mSelectedItem->itemDate() );
02011 }
02012 
02013 void KOAgenda::selectItemByUID( const QString& uid )
02014 {
02015   KOAgendaItem::GPtr item;
02016   AgendaItemList::Iterator it;
02017   for ( it = mItems.begin(); it != mItems.end(); ++it ) {
02018     if ( *it && (*it)->incidence() && (*it)->incidence()->uid() == uid ) {
02019       selectItem( *it );
02020       break;
02021     }
02022   }
02023 }
02024 
02025 // This function seems never be called.
02026 void KOAgenda::keyPressEvent( QKeyEvent *kev )
02027 {
02028   switch(kev->key()) {
02029     case Key_PageDown:
02030       verticalScrollBar()->addPage();
02031       break;
02032     case Key_PageUp:
02033       verticalScrollBar()->subtractPage();
02034       break;
02035     case Key_Down:
02036       verticalScrollBar()->addLine();
02037       break;
02038     case Key_Up:
02039       verticalScrollBar()->subtractLine();
02040       break;
02041     default:
02042       ;
02043   }
02044 }
02045 
02046 void KOAgenda::calculateWorkingHours()
02047 {
02048   mWorkingHoursEnable = !mAllDayMode;
02049 
02050   QTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
02051   mWorkingHoursYTop = int( 4 * mGridSpacingY *
02052                            ( tmp.hour() + tmp.minute() / 60. +
02053                              tmp.second() / 3600. ) );
02054   tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
02055   mWorkingHoursYBottom = int( 4 * mGridSpacingY *
02056                               ( tmp.hour() + tmp.minute() / 60. +
02057                                 tmp.second() / 3600. ) - 1 );
02058 }
02059 
02060 
02061 DateList KOAgenda::dateList() const
02062 {
02063     return mSelectedDates;
02064 }
02065 
02066 void KOAgenda::setDateList(const DateList &selectedDates)
02067 {
02068     mSelectedDates = selectedDates;
02069     marcus_bains();
02070 }
02071 
02072 void KOAgenda::setHolidayMask(QMemArray<bool> *mask)
02073 {
02074   mHolidayMask = mask;
02075 
02076 }
02077 
02078 void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
02079 {
02080   kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
02081   QScrollView::contentsMousePressEvent(event);
02082 }
02083 
02084 void KOAgenda::setTypeAheadReceiver( QObject *o )
02085 {
02086   mTypeAheadReceiver = o;
02087 }
02088 
02089 QObject *KOAgenda::typeAheadReceiver() const
02090 {
02091   return mTypeAheadReceiver;
02092 }
KDE Home | KDE Accessibility Home | Description of Access Keys