korganizer

koagenda.cpp

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