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   double lGridSpacingY = mGridSpacingY*2;
01406 
01407   // If work day, use work color
01408   // If busy day, use busy color
01409   // if work and busy day, mix both, and busy color has alpha
01410 
01411   const QMemArray<bool> busyDayMask = mAgendaView->busyDayMask();
01412 
01413   if ( KOPrefs::instance()->mColorBusyDaysEnabled && !mAllDayMode ) {
01414     for ( int i = 0; i < busyDayMask.count(); ++i ) {
01415       if ( busyDayMask[i] ) {
01416         const QPoint pt1( cx + mGridSpacingX * i, 0 );
01417         // const QPoint pt2( cx + mGridSpacingX * ( i+1 ), ch );
01418         dbp.fillRect( pt1.x(), pt1.y(), mGridSpacingX, cy + ch, KOPrefs::instance()->mAgendaMonthBgBusyColor );
01419       }
01420     }
01421   }
01422   // Highlight working hours
01423   if ( mWorkingHoursEnable ) {
01424 
01425     QColor workAndBusyColor;
01426 
01427     if ( KOPrefs::instance()->mColorBusyDaysEnabled ) {
01428       workAndBusyColor = KOHelper::mixColors( KOPrefs::instance()->mAgendaMonthBgBusyColor, 0.60, KOPrefs::instance()->mWorkingHoursColor );
01429     } else {
01430       workAndBusyColor = KOPrefs::instance()->mWorkingHoursColor;
01431     }
01432 
01433     const QPoint pt1( cx, mWorkingHoursYTop );
01434     const QPoint pt2( cx+cw, mWorkingHoursYBottom );
01435     if ( pt2.x() >= pt1.x() /*&& pt2.y() >= pt1.y()*/) {
01436       int gxStart = contentsToGrid( pt1 ).x();
01437       int gxEnd = contentsToGrid( pt2 ).x();
01438       // correct start/end for rtl layouts
01439       if ( gxStart > gxEnd ) {
01440         const int tmp = gxStart;
01441         gxStart = gxEnd;
01442         gxEnd = tmp;
01443       }
01444       const int xoffset = ( KOGlobals::self()->reverseLayout() ? 1 : 0 );
01445       while( gxStart <= gxEnd ) {
01446         const int xStart = gridToContents( QPoint( gxStart+xoffset, 0 ) ).x();
01447         const int xWidth = columnWidth( gxStart ) + 1;
01448         QColor color;
01449         if ( gxStart < busyDayMask.count() && busyDayMask[gxStart] ) {
01450           color = workAndBusyColor;
01451         } else {
01452           color = KOPrefs::instance()->mWorkingHoursColor;
01453         }
01454         if ( pt2.y() < pt1.y() ) {
01455           // overnight working hours
01456           if ( ( (gxStart==0) && !mHolidayMask->at(mHolidayMask->count()-1) ) ||
01457                ( (gxStart>0) && (gxStart<int(mHolidayMask->count())) && (!mHolidayMask->at(gxStart-1) ) ) ) {
01458             if ( pt2.y() > cy ) {
01459               dbp.fillRect( xStart, cy, xWidth, pt2.y() - cy + 1, color );
01460             }
01461           }
01462           if ( (gxStart < int(mHolidayMask->count()-1)) && (!mHolidayMask->at(gxStart)) ) {
01463             if ( pt1.y() < cy + ch - 1 ) {
01464               dbp.fillRect( xStart, pt1.y(), xWidth, cy + ch - pt1.y() + 1,
01465                             color );
01466             }
01467           }
01468         } else {
01469           // last entry in holiday mask denotes the previous day not visible (needed for overnight shifts)
01470           if ( gxStart < int(mHolidayMask->count()-1) && !mHolidayMask->at(gxStart)) {
01471             dbp.fillRect( xStart, pt1.y(), xWidth, pt2.y() - pt1.y() + 1,
01472                           color );
01473           }
01474         }
01475         ++gxStart;
01476       }
01477     }
01478   }
01479 
01480 
01481 
01482   // draw selection
01483   if ( mHasSelection ) {
01484     QPoint pt, pt1;
01485 
01486     if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
01487       // draw start day
01488       pt = gridToContents( mSelectionStartCell );
01489       pt1 = gridToContents( QPoint( mSelectionStartCell.x() + 1, mRows + 1 ) );
01490       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01491       // draw all other days between the start day and the day of the selection end
01492       for ( int c = mSelectionStartCell.x() + 1; c < mSelectionEndCell.x(); ++c ) {
01493         pt = gridToContents( QPoint( c, 0 ) );
01494         pt1 = gridToContents( QPoint( c + 1, mRows + 1 ) );
01495         dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01496       }
01497       // draw end day
01498       pt = gridToContents( QPoint( mSelectionEndCell.x(), 0 ) );
01499       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01500       dbp.fillRect( QRect( pt, pt1), KOPrefs::instance()->mHighlightColor );
01501     }  else { // single day selection
01502       pt = gridToContents( mSelectionStartCell );
01503       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01504       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01505     }
01506   }
01507 
01508   QPen hourPen( KOPrefs::instance()->mAgendaBgColor.dark( 150 ) );
01509   QPen halfHourPen( KOPrefs::instance()->mAgendaBgColor.dark( 125 ) );
01510   dbp.setPen( hourPen );
01511 
01512   // Draw vertical lines of grid, start with the last line not yet visible
01513   //  kdDebug(5850) << "drawContents cx: " << cx << " cy: " << cy << " cw: " << cw << " ch: " << ch << endl;
01514   double x = ( int( cx / mGridSpacingX ) ) * mGridSpacingX;
01515   while (x < cx + cw) {
01516     dbp.drawLine( int( x ), cy, int( x ), cy + ch );
01517     x+=mGridSpacingX;
01518   }
01519 
01520   // Draw horizontal lines of grid
01521   double y = ( int( cy / (2*lGridSpacingY) ) ) * 2 * lGridSpacingY;
01522   while (y < cy + ch) {
01523 //    kdDebug(5850) << " y: " << y << endl;
01524     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01525     y += 2 * lGridSpacingY;
01526   }
01527   y = ( 2 * int( cy / (2*lGridSpacingY) ) + 1) * lGridSpacingY;
01528   dbp.setPen( halfHourPen );
01529   while (y < cy + ch) {
01530 //    kdDebug(5850) << " y: " << y << endl;
01531     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01532     y+=2*lGridSpacingY;
01533   }
01534   p->drawPixmap(cx,cy, db);
01535 }
01536 
01537 /*
01538   Convert srcollview contents coordinates to agenda grid coordinates.
01539 */
01540 QPoint KOAgenda::contentsToGrid ( const QPoint &pos ) const
01541 {
01542   int gx = int( KOGlobals::self()->reverseLayout() ?
01543         mColumns - pos.x()/mGridSpacingX : pos.x()/mGridSpacingX );
01544   int gy = int( pos.y()/mGridSpacingY );
01545   return QPoint( gx, gy );
01546 }
01547 
01548 /*
01549   Convert agenda grid coordinates to scrollview contents coordinates.
01550 */
01551 QPoint KOAgenda::gridToContents( const QPoint &gpos ) const
01552 {
01553   int x = int( KOGlobals::self()->reverseLayout() ?
01554              (mColumns - gpos.x())*mGridSpacingX : gpos.x()*mGridSpacingX );
01555   int y = int( gpos.y()*mGridSpacingY );
01556   return QPoint( x, y );
01557 }
01558 
01559 
01560 /*
01561   Return Y coordinate corresponding to time. Coordinates are rounded to fit into
01562   the grid.
01563 */
01564 int KOAgenda::timeToY(const QTime &time)
01565 {
01566 //  kdDebug(5850) << "Time: " << time.toString() << endl;
01567   int minutesPerCell = 24 * 60 / mRows;
01568 //  kdDebug(5850) << "minutesPerCell: " << minutesPerCell << endl;
01569   int timeMinutes = time.hour() * 60 + time.minute();
01570 //  kdDebug(5850) << "timeMinutes: " << timeMinutes << endl;
01571   int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
01572 //  kdDebug(5850) << "y: " << Y << endl;
01573 //  kdDebug(5850) << "\n" << endl;
01574   return Y;
01575 }
01576 
01577 
01578 /*
01579   Return time corresponding to cell y coordinate. Coordinates are rounded to
01580   fit into the grid.
01581 */
01582 QTime KOAgenda::gyToTime(int gy)
01583 {
01584 //  kdDebug(5850) << "gyToTime: " << gy << endl;
01585   int secondsPerCell = 24 * 60 * 60/ mRows;
01586 
01587   int timeSeconds = secondsPerCell * gy;
01588 
01589   QTime time( 0, 0, 0 );
01590   if ( timeSeconds < 24 * 60 * 60 ) {
01591     time = time.addSecs(timeSeconds);
01592   } else {
01593     time.setHMS( 23, 59, 59 );
01594   }
01595 //  kdDebug(5850) << "  gyToTime: " << time.toString() << endl;
01596 
01597   return time;
01598 }
01599 
01600 QMemArray<int> KOAgenda::minContentsY()
01601 {
01602   QMemArray<int> minArray;
01603   minArray.fill( timeToY( QTime(23, 59) ), mSelectedDates.count() );
01604 
01605   AgendaItemList::Iterator it;
01606   for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01607     if ( *it ) {
01608       const int ymin = (*it)->cellYTop();
01609       const int index = (*it)->cellXLeft();
01610       if ( index >= 0 && index < static_cast<int>( mSelectedDates.count() ) ) {
01611         if ( ymin < minArray[index] && !mItemsToDelete.contains( *it ) )
01612           minArray[index] = ymin;
01613       }
01614     }
01615   }
01616 
01617   return minArray;
01618 }
01619 
01620 QMemArray<int> KOAgenda::maxContentsY()
01621 {
01622   QMemArray<int> maxArray;
01623   maxArray.fill( timeToY( QTime(0, 0) ), mSelectedDates.count() );
01624   AgendaItemList::Iterator it;
01625   for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01626     if ( *it ) {
01627       const int ymax = (*it)->cellYBottom();
01628       const int index = (*it)->cellXLeft();
01629       if ( index >= 0 && index < static_cast<int>( mSelectedDates.count() ) ) {
01630         if ( ymax > maxArray[index] && !mItemsToDelete.contains( *it ) )
01631           maxArray[index] = ymax;
01632       }
01633     }
01634   }
01635 
01636   return maxArray;
01637 }
01638 
01639 void KOAgenda::setStartTime( const QTime &startHour )
01640 {
01641   const double startPos = ( startHour.hour()/24. + startHour.minute()/1440. +
01642                             startHour.second()/86400. ) * mRows * gridSpacingY();
01643   setContentsPos( 0, static_cast<int>( startPos ) );
01644 }
01645 
01646 
01647 /*
01648   Insert KOAgendaItem into agenda.
01649 */
01650 KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const QDate &qd, int X,
01651                                     int YTop, int YBottom, int itemPos, int itemCount )
01652 {
01653   if ( mAllDayMode ) {
01654     kdDebug(5850) << "KOAgenda: calling insertItem in all-day mode is illegal." << endl;
01655     return 0;
01656   }
01657 
01658   mActionType = NOP;
01659 
01660   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, incidence, qd, viewport(), itemPos, itemCount );
01661   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem::GPtr  ) ),
01662            SLOT( removeAgendaItem( KOAgendaItem::GPtr  ) ) );
01663   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem::GPtr  ) ),
01664            SLOT( showAgendaItem( KOAgendaItem::GPtr  ) ) );
01665 
01666   if ( YBottom <= YTop ) {
01667     kdDebug(5850) << "KOAgenda::insertItem(): Text: " << agendaItem->text() << " YSize<0" << endl;
01668     YBottom = YTop;
01669   }
01670 
01671   agendaItem->resize( int( ( X + 1 ) * mGridSpacingX ) -
01672                       int( X * mGridSpacingX ),
01673                       int( YTop * mGridSpacingY ) -
01674                       int( ( YBottom + 1 ) * mGridSpacingY ) );
01675   agendaItem->setCellXY( X, YTop, YBottom );
01676   agendaItem->setCellXRight( X );
01677   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, incidence ) );
01678   agendaItem->installEventFilter( this );
01679 
01680   addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
01681   mItems.append( agendaItem );
01682 
01683   placeSubCells( agendaItem );
01684 
01685   agendaItem->show();
01686 
01687   marcus_bains();
01688 
01689   return agendaItem;
01690 }
01691 
01692 /*
01693   Insert all-day KOAgendaItem into agenda.
01694 */
01695 KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const QDate &qd,
01696                                           int XBegin, int XEnd )
01697 {
01698   if ( !mAllDayMode ) {
01699     kdWarning(5850) << "KOAgenda: calling insertAllDayItem in non all-day mode is illegal." << endl;
01700     return 0;
01701   }
01702 
01703   mActionType = NOP;
01704 
01705   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, event, qd, viewport(), 1, 1 );
01706   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem::GPtr ) ),
01707            SLOT( removeAgendaItem( KOAgendaItem::GPtr ) ) );
01708   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem::GPtr ) ),
01709            SLOT( showAgendaItem( KOAgendaItem::GPtr ) ) );
01710 
01711   { // Start hack
01712 
01713     // Fixes https://issues.kolab.org/issue3933 -
01714     // issue3933: A large wholeday event is not shown at all days in dayview.(rt#5884)
01715     // ( Some 32k pixel limit Qt3 has. )
01716     if ( event->doesFloat() && XBegin * mGridSpacingX <= -32767/*32k*/ + mGridSpacingX ) {
01717 
01718       // Our event starts _many_ days before dt, so we lie to agenda and make
01719       // it think the event starts one day before.
01720       // That part of the event won't be visible, so the lie won't have any effect
01721       // for the user
01722       XBegin = -1;
01723     }
01724   } // End hack
01725 
01726   agendaItem->setCellXY( XBegin, 0, 0 );
01727   agendaItem->setCellXRight( XEnd );
01728 
01729   const double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
01730   const double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
01731                                          agendaItem->cellXLeft() );
01732 
01733   agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
01734 
01735   agendaItem->installEventFilter( this );
01736   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
01737   addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
01738   mItems.append( agendaItem );
01739 
01740   placeSubCells( agendaItem );
01741 
01742   agendaItem->show();
01743 
01744   return agendaItem;
01745 }
01746 
01747 
01748 void KOAgenda::insertMultiItem( Event *event, const QDate &qd, int XBegin, int XEnd,
01749                                 int YTop, int YBottom )
01750 {
01751   if ( mAllDayMode ) {
01752     kdDebug(5850) << "KOAgenda: calling insertMultiItem in all-day mode is illegal." << endl;
01753     return;
01754   }
01755   mActionType = NOP;
01756 
01757   int cellX,cellYTop,cellYBottom;
01758   QString newtext;
01759   int width = XEnd - XBegin + 1;
01760   int count = 0;
01761   KOAgendaItem *current = 0;
01762   QPtrList<KOAgendaItem> multiItems;
01763   const int visibleCount = mSelectedDates.first().daysTo( mSelectedDates.last() );
01764   for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
01765     ++count;
01766     //Only add the items that are visible.
01767     if( cellX >= 0 && cellX <= visibleCount ) {
01768       if ( cellX == XBegin ) {
01769         cellYTop = YTop;
01770       } else {
01771         cellYTop = 0;
01772       }
01773 
01774       if ( cellX == XEnd ) {
01775         cellYBottom = YBottom;
01776       } else {
01777         cellYBottom = rows() - 1;
01778       }
01779 
01780       newtext = QString("(%1/%2): ").arg( count ).arg( width );
01781       newtext.append( event->summary() );
01782 
01783       current = insertItem( event, qd, cellX, cellYTop, cellYBottom, count, width );
01784       current->setText( newtext );
01785       multiItems.append( current );
01786     }
01787   }
01788   QPtrList<KOAgendaItem>::iterator it = multiItems.begin();
01789   QPtrList<KOAgendaItem>::iterator e = multiItems.end();
01790 
01791   if ( it != e ) { // .first asserts if the list is empty
01792     KOAgendaItem *first = multiItems.first();
01793     KOAgendaItem *last = multiItems.last();
01794     KOAgendaItem *prev = 0, *next = 0;
01795 
01796     while ( it != e ) {
01797       KOAgendaItem *item = *it;
01798       ++it;
01799       next = ( it == e ) ? 0 : (*it);
01800       if ( item ) {
01801         item->setMultiItem( ( item == first ) ? 0 : first,
01802                             prev, next,
01803                             ( item == last ) ? 0 : last );
01804       }
01805       prev = item;
01806     }
01807   }
01808 
01809   marcus_bains();
01810 }
01811 
01812 void KOAgenda::removeIncidence( Incidence *incidence )
01813 {
01814   // First find all items to be deleted and store them
01815   // in its own list. Otherwise removeAgendaItem will reset
01816   // the current position and mess this up.
01817   QValueList<KOAgendaItem::GPtr > itemsToRemove;
01818 
01819   AgendaItemList::Iterator it;
01820   for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01821     if ( *it ) {
01822       if ( (*it)->incidence() == incidence ) {
01823         itemsToRemove.append( *it );
01824       }
01825     }
01826   }
01827 
01828   for ( it = itemsToRemove.begin(); it != itemsToRemove.end(); ++it ) {
01829     removeAgendaItem( *it );
01830   }
01831 }
01832 
01833 void KOAgenda::showAgendaItem( KOAgendaItem::GPtr agendaItem )
01834 {
01835   if ( !agendaItem ) {
01836     return;
01837   }
01838 
01839   agendaItem->hide();
01840   addChild( agendaItem );
01841   if ( !mItems.contains( agendaItem ) ) {
01842     mItems.append( agendaItem );
01843   }
01844   placeSubCells( agendaItem );
01845 
01846   agendaItem->show();
01847 }
01848 
01849 bool KOAgenda::removeAgendaItem( KOAgendaItem::GPtr item )
01850 {
01851   // we found the item. Let's remove it and update the conflicts
01852   bool taken = false;
01853   KOAgendaItem::GPtr thisItem = item;
01854   AgendaItemList conflictItems = thisItem->conflictItems();
01855   removeChild( thisItem );
01856 
01857   AgendaItemList::Iterator it = mItems.find( thisItem );
01858   if ( it != mItems.end() ) {
01859     mItems.remove( it ); // Don't free memory, QPtrList::take() was used here.
01860     taken = true;
01861   }
01862 
01863   for ( it = conflictItems.begin(); it != conflictItems.end(); ++it ) {
01864     // the item itself is also in its own conflictItems list!
01865     if ( *it != thisItem ) {
01866       placeSubCells( *it );
01867     }
01868   }
01869   mItemsToDelete.append( thisItem );
01870   QTimer::singleShot( 0, this, SLOT( deleteItemsToDelete() ) );
01871   return taken;
01872 }
01873 
01874 void KOAgenda::deleteItemsToDelete()
01875 {
01876   freeItemList( mItemsToDelete );
01877   mItemsToDelete.clear();
01878 }
01879 
01880 /*QSizePolicy KOAgenda::sizePolicy() const
01881 {
01882   // Thought this would make the all-day event agenda minimum size and the
01883   // normal agenda take the remaining space. But it doesnt work. The QSplitter
01884   // dont seem to think that an Expanding widget needs more space than a
01885   // Preferred one.
01886   // But it doesnt hurt, so it stays.
01887   if (mAllDayMode) {
01888     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
01889   } else {
01890     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
01891   }
01892 }
01893 */
01894 
01895 /*
01896   Overridden from QScrollView to provide proper resizing of KOAgendaItems.
01897 */
01898 void KOAgenda::resizeEvent ( QResizeEvent *ev )
01899 {
01900 //  kdDebug(5850) << "KOAgenda::resizeEvent" << endl;
01901 
01902   QSize newSize( ev->size() );
01903   if (mAllDayMode) {
01904     mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
01905     mGridSpacingY = newSize.height() - 2 * frameWidth();
01906   } else {
01907     int scrollbarWidth = vScrollBarMode() != AlwaysOff ? verticalScrollBar()->width() : 0;
01908     mGridSpacingX = double( newSize.width() - scrollbarWidth - 2 * frameWidth()) / double(mColumns);
01909     // make sure that there are not more than 24 per day
01910     mGridSpacingY = double(newSize.height() - 2 * frameWidth()) / double(mRows);
01911     if ( mGridSpacingY < mDesiredGridSpacingY )
01912       mGridSpacingY = mDesiredGridSpacingY;
01913   }
01914   calculateWorkingHours();
01915   QTimer::singleShot( 0, this, SLOT( resizeAllContents() ) );
01916   emit gridSpacingYChanged( mGridSpacingY * 4 );
01917   QScrollView::resizeEvent(ev);
01918 }
01919 
01920 void KOAgenda::resizeAllContents()
01921 {
01922   double subCellWidth;
01923   if ( mItems.count() > 0 ) {
01924     KOAgendaItem::GPtr item;
01925     if ( mAllDayMode ) {
01926       AgendaItemList::Iterator it;
01927       for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01928         if ( *it ) {
01929           subCellWidth = calcSubCellWidth( *it );
01930           placeAgendaItem( *it, subCellWidth );
01931         }
01932       }
01933     } else {
01934       AgendaItemList::Iterator it;
01935       for ( it = mItems.begin(); it != mItems.end(); ++it ) {
01936         if ( *it ) {
01937           subCellWidth = calcSubCellWidth( *it );
01938           placeAgendaItem( *it, subCellWidth );
01939         }
01940       }
01941     }
01942   }
01943   checkScrollBoundaries();
01944   marcus_bains();
01945 }
01946 
01947 void KOAgenda::scrollUp()
01948 {
01949   scrollBy(0,-mScrollOffset);
01950 }
01951 
01952 
01953 void KOAgenda::scrollDown()
01954 {
01955   scrollBy(0,mScrollOffset);
01956 }
01957 
01958 
01959 /*
01960   Calculates the minimum width
01961 */
01962 int KOAgenda::minimumWidth() const
01963 {
01964   // FIXME:: develop a way to dynamically determine the minimum width
01965   int min = 100;
01966 
01967   return min;
01968 }
01969 
01970 void KOAgenda::updateConfig()
01971 {
01972   double oldGridSpacingY = mGridSpacingY;
01973 
01974   mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
01975   if ( mDesiredGridSpacingY < 4 || mDesiredGridSpacingY > 30 ) {
01976     mDesiredGridSpacingY = 10;
01977   }
01978 
01979   // make sure that there are not more than 24 per day
01980   mGridSpacingY = (double)height() / (double)mRows;
01981   if ( mGridSpacingY < mDesiredGridSpacingY ) {
01982     mGridSpacingY = mDesiredGridSpacingY;
01983   }
01984 
01985   //can be two doubles equal?, it's better to compare them with an epsilon
01986   if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 ) {
01987     resizeContents( int( mGridSpacingX * mColumns ),
01988                     int( mGridSpacingY * mRows ) );
01989   }
01990 
01991   calculateWorkingHours();
01992 
01993   marcus_bains();
01994 }
01995 
01996 void KOAgenda::checkScrollBoundaries()
01997 {
01998   // Invalidate old values to force update
01999   mOldLowerScrollValue = -1;
02000   mOldUpperScrollValue = -1;
02001 
02002   checkScrollBoundaries(verticalScrollBar()->value());
02003 }
02004 
02005 void KOAgenda::checkScrollBoundaries( int v )
02006 {
02007   int yMin = int( (v) / mGridSpacingY );
02008   int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
02009 
02010 //  kdDebug(5850) << "--- yMin: " << yMin << "  yMax: " << yMax << endl;
02011 
02012   if ( yMin != mOldLowerScrollValue ) {
02013     mOldLowerScrollValue = yMin;
02014     emit lowerYChanged(yMin);
02015   }
02016   if ( yMax != mOldUpperScrollValue ) {
02017     mOldUpperScrollValue = yMax;
02018     emit upperYChanged(yMax);
02019   }
02020 }
02021 
02022 int KOAgenda::visibleContentsYMin()
02023 {
02024   int v = verticalScrollBar()->value();
02025   return int( v / mGridSpacingY );
02026 }
02027 
02028 int KOAgenda::visibleContentsYMax()
02029 {
02030   int v = verticalScrollBar()->value();
02031   return int( ( v + visibleHeight() ) / mGridSpacingY );
02032 }
02033 
02034 void KOAgenda::deselectItem()
02035 {
02036   if ( mSelectedItem.isNull() ) {
02037     return;
02038   }
02039   mSelectedItem->select(false);
02040   mSelectedItem = 0;
02041 }
02042 
02043 void KOAgenda::selectItem( KOAgendaItem::GPtr item )
02044 {
02045   if ((KOAgendaItem *)mSelectedItem == item) return;
02046   deselectItem();
02047   if ( !item ) {
02048     emit incidenceSelected( 0, QDate() );
02049     return;
02050   }
02051   mSelectedItem = item;
02052   mSelectedItem->select();
02053   assert( mSelectedItem->incidence() );
02054   mSelectedUid = mSelectedItem->incidence()->uid();
02055   emit incidenceSelected( mSelectedItem->incidence(), mSelectedItem->itemDate() );
02056 }
02057 
02058 void KOAgenda::selectItemByUID( const QString& uid )
02059 {
02060   KOAgendaItem::GPtr item;
02061   AgendaItemList::Iterator it;
02062   for ( it = mItems.begin(); it != mItems.end(); ++it ) {
02063     if ( *it && (*it)->incidence() && (*it)->incidence()->uid() == uid ) {
02064       selectItem( *it );
02065       break;
02066     }
02067   }
02068 }
02069 
02070 // This function seems never be called.
02071 void KOAgenda::keyPressEvent( QKeyEvent *kev )
02072 {
02073   switch(kev->key()) {
02074     case Key_PageDown:
02075       verticalScrollBar()->addPage();
02076       break;
02077     case Key_PageUp:
02078       verticalScrollBar()->subtractPage();
02079       break;
02080     case Key_Down:
02081       verticalScrollBar()->addLine();
02082       break;
02083     case Key_Up:
02084       verticalScrollBar()->subtractLine();
02085       break;
02086     default:
02087       ;
02088   }
02089 }
02090 
02091 void KOAgenda::calculateWorkingHours()
02092 {
02093   mWorkingHoursEnable = !mAllDayMode;
02094 
02095   QTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
02096   mWorkingHoursYTop = int( 4 * mGridSpacingY *
02097                            ( tmp.hour() + tmp.minute() / 60. +
02098                              tmp.second() / 3600. ) );
02099   tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
02100   mWorkingHoursYBottom = int( 4 * mGridSpacingY *
02101                               ( tmp.hour() + tmp.minute() / 60. +
02102                                 tmp.second() / 3600. ) - 1 );
02103 }
02104 
02105 
02106 DateList KOAgenda::dateList() const
02107 {
02108     return mSelectedDates;
02109 }
02110 
02111 void KOAgenda::setDateList(const DateList &selectedDates)
02112 {
02113     mSelectedDates = selectedDates;
02114     marcus_bains();
02115 }
02116 
02117 void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
02118 {
02119   kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
02120   QScrollView::contentsMousePressEvent(event);
02121 }
02122 
02123 void KOAgenda::setTypeAheadReceiver( QObject *o )
02124 {
02125   mTypeAheadReceiver = o;
02126 }
02127 
02128 QObject *KOAgenda::typeAheadReceiver() const
02129 {
02130   return mTypeAheadReceiver;
02131 }
02132 
02133 void KOAgenda::setHolidayMask(QMemArray<bool> *mask)
02134 {
02135   mHolidayMask = mask;
02136 }
KDE Home | KDE Accessibility Home | Description of Access Keys