korganizer

koagenda.cpp

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