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           }
00618         } else {
00619           KOAgendaItem* item = dynamic_cast<KOAgendaItem *>(object);
00620           if (item) {
00621             Incidence *incidence = item->incidence();
00622             if ( incidence->isReadOnly() ) {
00623               mActionItem = 0;
00624               mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), QString() );
00625             } else {
00626               mActionItem = item;
00627               mResPair = CalHelper::incSubResourceCalendar( mCalendar, incidence );
00628               startItemAction(viewportPos);
00629             }
00630             // Warning: do selectItem() as late as possible, since all
00631             // sorts of things happen during this call. Some can lead to
00632             // this filter being run again and mActionItem being set to
00633             // null.
00634             selectItem( item );
00635           }
00636         }
00637       } else {
00638         if (me->button() == RightButton)
00639         {
00640           // if mouse pointer is not in selection, select the cell below the cursor
00641           QPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
00642           if ( !ptInSelection( gpos ) ) {
00643             mSelectionStartCell = gpos;
00644             mSelectionEndCell = gpos;
00645             mHasSelection = true;
00646             emit newStartSelectSignal();
00647             emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
00648             updateContents();
00649           }
00650           showNewEventPopupSignal();
00651         }
00652         else
00653         {
00654           // if mouse pointer is in selection, don't change selection
00655           QPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
00656           if ( !ptInSelection( gpos ) ) {
00657             selectItem(0);
00658             mActionItem = 0;
00659             mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), QString() );
00660             setCursor(arrowCursor);
00661             startSelectAction(viewportPos);
00662           }
00663         }
00664       }
00665       break;
00666 
00667     case QEvent::MouseButtonRelease:
00668       if (mActionItem) {
00669         endItemAction();
00670       } else if ( mActionType == SELECT ) {
00671         endSelectAction( viewportPos );
00672       }
00673       // This nasty gridToContents(contentsToGrid(..)) is needed to
00674       // avoid an offset of a few pixels. Don't ask me why...
00675       emit mousePosSignal( gridToContents(contentsToGrid(
00676                            viewportToContents( viewportPos ) ) ));
00677       break;
00678 
00679     case QEvent::MouseMove: {
00680       // This nasty gridToContents(contentsToGrid(..)) is needed to
00681       // avoid an offset of a few pixels. Don't ask me why...
00682       QPoint indicatorPos = gridToContents(contentsToGrid(
00683                                           viewportToContents( viewportPos )));
00684       if (object != viewport()) {
00685         KOAgendaItem *moveItem = dynamic_cast<KOAgendaItem *>(object);
00686         if (moveItem && !moveItem->incidence()->isReadOnly() ) {
00687           if (!mActionItem)
00688             setNoActionCursor(moveItem,viewportPos);
00689           else {
00690             performItemAction(viewportPos);
00691 
00692             if ( mActionType == MOVE ) {
00693               // show cursor at the current begin of the item
00694               KOAgendaItem *firstItem = mActionItem->firstMultiItem();
00695               if (!firstItem) firstItem = mActionItem;
00696               indicatorPos = gridToContents( QPoint( firstItem->cellXLeft(),
00697                                                      firstItem->cellYTop() ) );
00698 
00699             } else if ( mActionType == RESIZEBOTTOM ) {
00700               // RESIZETOP is handled correctly, only resizebottom works differently
00701               indicatorPos = gridToContents( QPoint( mActionItem->cellXLeft(),
00702                                                      mActionItem->cellYBottom()+1 ) );
00703             }
00704 
00705           } // If we have an action item
00706         } // If move item && !read only
00707       } else {
00708         if ( mActionType == SELECT ) {
00709           performSelectAction( viewportPos );
00710 
00711           // show cursor at end of timespan
00712           if ( ((mStartCell.y() < mEndCell.y()) && (mEndCell.x() >= mStartCell.x())) ||
00713                (mEndCell.x() > mStartCell.x()) )
00714             indicatorPos = gridToContents( QPoint(mEndCell.x(), mEndCell.y()+1) );
00715           else
00716             indicatorPos = gridToContents( mEndCell );
00717         }
00718       }
00719       emit mousePosSignal( indicatorPos );
00720       break; }
00721 
00722     case QEvent::MouseButtonDblClick:
00723       if (object == viewport()) {
00724         selectItem(0);
00725         QPair<ResourceCalendar *, QString>p = mCalendarView->viewSubResourceCalendar();
00726         emit newEventSignal( p.first, p.second );
00727       } else {
00728         KOAgendaItem *doubleClickedItem = dynamic_cast<KOAgendaItem *>( object );
00729         if ( doubleClickedItem ) {
00730           selectItem( doubleClickedItem );
00731           emit editIncidenceSignal( doubleClickedItem->incidence(), doubleClickedItem->itemDate() );
00732         }
00733       }
00734       break;
00735 
00736     default:
00737       break;
00738   }
00739 
00740   return true;
00741 }
00742 
00743 bool KOAgenda::ptInSelection( QPoint gpos ) const
00744 {
00745   if ( !mHasSelection ) {
00746     return false;
00747   } else if ( gpos.x()<mSelectionStartCell.x() || gpos.x()>mSelectionEndCell.x() ) {
00748     return false;
00749   } else if ( (gpos.x()==mSelectionStartCell.x()) && (gpos.y()<mSelectionStartCell.y()) ) {
00750     return false;
00751   } else if ( (gpos.x()==mSelectionEndCell.x()) && (gpos.y()>mSelectionEndCell.y()) ) {
00752     return false;
00753   }
00754   return true;
00755 }
00756 
00757 void KOAgenda::startSelectAction( const QPoint &viewportPos )
00758 {
00759   emit newStartSelectSignal();
00760 
00761   mActionType = SELECT;
00762   mSelectionStartPoint = viewportPos;
00763   mHasSelection = true;
00764 
00765   QPoint pos = viewportToContents( viewportPos );
00766   QPoint gpos = contentsToGrid( pos );
00767 
00768   // Store new selection
00769   mStartCell = gpos;
00770   mEndCell = gpos;
00771   mSelectionStartCell = gpos;
00772   mSelectionEndCell = gpos;
00773 
00774   updateContents();
00775 }
00776 
00777 void KOAgenda::performSelectAction(const QPoint& viewportPos)
00778 {
00779   QPoint pos = viewportToContents( viewportPos );
00780   QPoint gpos = contentsToGrid( pos );
00781 
00782   QPoint clipperPos = clipper()->
00783                       mapFromGlobal(viewport()->mapToGlobal(viewportPos));
00784 
00785   // Scroll if cursor was moved to upper or lower end of agenda.
00786   if (clipperPos.y() < mScrollBorderWidth) {
00787     mScrollUpTimer.start(mScrollDelay);
00788   } else if (visibleHeight() - clipperPos.y() <
00789              mScrollBorderWidth) {
00790     mScrollDownTimer.start(mScrollDelay);
00791   } else {
00792     mScrollUpTimer.stop();
00793     mScrollDownTimer.stop();
00794   }
00795 
00796   if ( gpos != mEndCell ) {
00797     mEndCell = gpos;
00798     if ( mStartCell.x()>mEndCell.x() ||
00799          ( mStartCell.x()==mEndCell.x() && mStartCell.y()>mEndCell.y() ) ) {
00800       // backward selection
00801       mSelectionStartCell = mEndCell;
00802       mSelectionEndCell = mStartCell;
00803     } else {
00804       mSelectionStartCell = mStartCell;
00805       mSelectionEndCell = mEndCell;
00806     }
00807 
00808     updateContents();
00809   }
00810 }
00811 
00812 void KOAgenda::endSelectAction( const QPoint &currentPos )
00813 {
00814   mScrollUpTimer.stop();
00815   mScrollDownTimer.stop();
00816 
00817   mActionType = NOP;
00818 
00819   emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
00820 
00821   if ( KOPrefs::instance()->mSelectionStartsEditor ) {
00822     if ( ( mSelectionStartPoint - currentPos ).manhattanLength() >
00823          QApplication::startDragDistance() ) {
00824        emitNewEventForSelection();
00825     }
00826   }
00827 }
00828 
00829 KOAgenda::MouseActionType KOAgenda::isInResizeArea( bool horizontal,
00830     const QPoint &pos, KOAgendaItem*item )
00831 {
00832   if (!item) return NOP;
00833   QPoint gridpos = contentsToGrid( pos );
00834   QPoint contpos = gridToContents( gridpos +
00835       QPoint( (KOGlobals::self()->reverseLayout())?1:0, 0 ) );
00836 
00837 //kdDebug(5850)<<"contpos="<<contpos<<", pos="<<pos<<", gpos="<<gpos<<endl;
00838 //kdDebug(5850)<<"clXLeft="<<clXLeft<<", clXRight="<<clXRight<<endl;
00839 
00840   if ( horizontal ) {
00841     int clXLeft = item->cellXLeft();
00842     int clXRight = item->cellXRight();
00843     if ( KOGlobals::self()->reverseLayout() ) {
00844       int tmp = clXLeft;
00845       clXLeft = clXRight;
00846       clXRight = tmp;
00847     }
00848     int gridDistanceX = int( pos.x() - contpos.x() );
00849     if (gridDistanceX < mResizeBorderWidth && clXLeft == gridpos.x() ) {
00850       if ( KOGlobals::self()->reverseLayout() ) return RESIZERIGHT;
00851       else return RESIZELEFT;
00852     } else if ((mGridSpacingX - gridDistanceX) < mResizeBorderWidth &&
00853                clXRight == gridpos.x() ) {
00854       if ( KOGlobals::self()->reverseLayout() ) return RESIZELEFT;
00855       else return RESIZERIGHT;
00856     } else {
00857       return MOVE;
00858     }
00859   } else {
00860     int gridDistanceY = int( pos.y() - contpos.y() );
00861     if (gridDistanceY < mResizeBorderWidth &&
00862         item->cellYTop() == gridpos.y() &&
00863         !item->firstMultiItem() ) {
00864       return RESIZETOP;
00865     } else if ((mGridSpacingY - gridDistanceY) < mResizeBorderWidth &&
00866                item->cellYBottom() == gridpos.y() &&
00867                !item->lastMultiItem() )  {
00868       return RESIZEBOTTOM;
00869     } else {
00870       return MOVE;
00871     }
00872   }
00873 }
00874 
00875 void KOAgenda::startItemAction(const QPoint& viewportPos)
00876 {
00877   QPoint pos = viewportToContents( viewportPos );
00878   mStartCell = contentsToGrid( pos );
00879   mEndCell = mStartCell;
00880 
00881   bool noResize = ( mActionItem->incidence()->type() == "Todo");
00882 
00883   mActionType = MOVE;
00884   if ( !noResize ) {
00885     mActionType = isInResizeArea( mAllDayMode, pos, mActionItem );
00886   }
00887 
00888 
00889   mActionItem->startMove();
00890   setActionCursor( mActionType, true );
00891 }
00892 
00893 void KOAgenda::performItemAction(const QPoint& viewportPos)
00894 {
00895 //  kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
00896 //  QPoint point = viewport()->mapToGlobal(viewportPos);
00897 //  kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
00898 //  point = clipper()->mapFromGlobal(point);
00899 //  kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
00900 //  kdDebug(5850) << "visible height: " << visibleHeight() << endl;
00901   QPoint pos = viewportToContents( viewportPos );
00902 //  kdDebug(5850) << "contents: " << x << "," << y << "\n" << endl;
00903   QPoint gpos = contentsToGrid( pos );
00904   QPoint clipperPos = clipper()->
00905                       mapFromGlobal(viewport()->mapToGlobal(viewportPos));
00906 
00907   // Cursor left active agenda area.
00908   // This starts a drag.
00909   if ( clipperPos.y() < 0 || clipperPos.y() > visibleHeight() ||
00910        clipperPos.x() < 0 || clipperPos.x() > visibleWidth() ) {
00911     if ( mActionType == MOVE ) {
00912       mScrollUpTimer.stop();
00913       mScrollDownTimer.stop();
00914       mActionItem->resetMove();
00915       placeSubCells( mActionItem );
00916       emit startDragSignal( mActionItem->incidence() );
00917       setCursor( arrowCursor );
00918       mActionItem = 0;
00919       mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), QString() );
00920       mActionType = NOP;
00921       mItemMoved = false;
00922       return;
00923     }
00924   } else {
00925     setActionCursor( mActionType );
00926   }
00927 
00928   // Scroll if item was moved to upper or lower end of agenda.
00929   if (clipperPos.y() < mScrollBorderWidth) {
00930     mScrollUpTimer.start(mScrollDelay);
00931   } else if (visibleHeight() - clipperPos.y() <
00932              mScrollBorderWidth) {
00933     mScrollDownTimer.start(mScrollDelay);
00934   } else {
00935     mScrollUpTimer.stop();
00936     mScrollDownTimer.stop();
00937   }
00938 
00939   // Move or resize item if necessary
00940   if ( mEndCell != gpos ) {
00941     if ( !mItemMoved ) {
00942       if ( !mChanger ||
00943            !mChanger->beginChange( mActionItem->incidence(), mResPair.first, mResPair.second ) ) {
00944         KMessageBox::information( this, i18n("Unable to lock item for "
00945                              "modification. You cannot make any changes."),
00946                              i18n("Locking Failed"), "AgendaLockingFailed" );
00947         mScrollUpTimer.stop();
00948         mScrollDownTimer.stop();
00949         mActionItem->resetMove();
00950         placeSubCells( mActionItem );
00951         setCursor( arrowCursor );
00952         mActionItem = 0;
00953         mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), QString() );
00954         mActionType = NOP;
00955         mItemMoved = false;
00956         return;
00957       }
00958       mItemMoved = true;
00959     }
00960     mActionItem->raise();
00961     if (mActionType == MOVE) {
00962       // Move all items belonging to a multi item
00963       KOAgendaItem *firstItem = mActionItem->firstMultiItem();
00964       if (!firstItem) firstItem = mActionItem;
00965       KOAgendaItem *lastItem = mActionItem->lastMultiItem();
00966       if (!lastItem) lastItem = mActionItem;
00967       QPoint deltapos = gpos - mEndCell;
00968       KOAgendaItem *moveItem = firstItem;
00969       while (moveItem) {
00970         bool changed=false;
00971         if ( deltapos.x()!=0 ) {
00972           moveItem->moveRelative( deltapos.x(), 0 );
00973           changed=true;
00974         }
00975         // in agenda's all day view don't try to move multi items, since there are none
00976         if ( moveItem==firstItem && !mAllDayMode ) { // is the first item
00977           int newY = deltapos.y() + moveItem->cellYTop();
00978           // If event start moved earlier than 0:00, it starts the previous day
00979           if ( newY<0 ) {
00980             moveItem->expandTop( -moveItem->cellYTop() );
00981             // prepend a new item at ( x-1, rows()+newY to rows() )
00982             KOAgendaItem *newFirst = firstItem->prevMoveItem();
00983             // cell's y values are first and last cell of the bar, so if newY=-1, they need to be the same
00984             if (newFirst) {
00985               newFirst->setCellXY(moveItem->cellXLeft()-1, rows()+newY, rows()-1);
00986               mItems.append( newFirst );
00987               moveItem->resize( int( mGridSpacingX * newFirst->cellWidth() ),
00988                                 int( mGridSpacingY * newFirst->cellHeight() ));
00989               QPoint cpos = gridToContents( QPoint( newFirst->cellXLeft(), newFirst->cellYTop() ) );
00990               addChild( newFirst, cpos.x(), cpos.y() );
00991             } else {
00992               newFirst = insertItem( moveItem->incidence(), moveItem->itemDate(),
00993                 moveItem->cellXLeft()-1, rows()+newY, rows()-1 ) ;
00994             }
00995             if (newFirst) newFirst->show();
00996             moveItem->prependMoveItem(newFirst);
00997             firstItem=newFirst;
00998           } else if ( newY>=rows() ) {
00999             // If event start is moved past 24:00, it starts the next day
01000             // erase current item (i.e. remove it from the multiItem list)
01001             firstItem = moveItem->nextMultiItem();
01002             moveItem->hide();
01003             mItems.take( mItems.find( moveItem ) );
01004             removeChild( moveItem );
01005             mActionItem->removeMoveItem(moveItem);
01006             moveItem=firstItem;
01007             // adjust next day's item
01008             if (moveItem) moveItem->expandTop( rows()-newY );
01009           } else {
01010             moveItem->expandTop(deltapos.y());
01011           }
01012           changed=true;
01013         }
01014         if ( !moveItem->lastMultiItem() && !mAllDayMode ) { // is the last item
01015           int newY = deltapos.y()+moveItem->cellYBottom();
01016           if (newY<0) {
01017             // erase current item
01018             lastItem = moveItem->prevMultiItem();
01019             moveItem->hide();
01020             mItems.take( mItems.find(moveItem) );
01021             removeChild( moveItem );
01022             moveItem->removeMoveItem( moveItem );
01023             moveItem = lastItem;
01024             moveItem->expandBottom(newY+1);
01025           } else if (newY>=rows()) {
01026             moveItem->expandBottom( rows()-moveItem->cellYBottom()-1 );
01027             // append item at ( x+1, 0 to newY-rows() )
01028             KOAgendaItem *newLast = lastItem->nextMoveItem();
01029             if (newLast) {
01030               newLast->setCellXY( moveItem->cellXLeft()+1, 0, newY-rows()-1 );
01031               mItems.append(newLast);
01032               moveItem->resize( int( mGridSpacingX * newLast->cellWidth() ),
01033                                 int( mGridSpacingY * newLast->cellHeight() ));
01034               QPoint cpos = gridToContents( QPoint( newLast->cellXLeft(), newLast->cellYTop() ) ) ;
01035               addChild( newLast, cpos.x(), cpos.y() );
01036             } else {
01037               newLast = insertItem( moveItem->incidence(), moveItem->itemDate(),
01038                 moveItem->cellXLeft()+1, 0, newY-rows()-1 ) ;
01039             }
01040             moveItem->appendMoveItem( newLast );
01041             newLast->show();
01042             lastItem = newLast;
01043           } else {
01044             moveItem->expandBottom( deltapos.y() );
01045           }
01046           changed=true;
01047         }
01048         if (changed) {
01049           adjustItemPosition( moveItem );
01050         }
01051         moveItem = moveItem->nextMultiItem();
01052       }
01053     } else if (mActionType == RESIZETOP) {
01054       if (mEndCell.y() <= mActionItem->cellYBottom()) {
01055         mActionItem->expandTop(gpos.y() - mEndCell.y());
01056         adjustItemPosition( mActionItem );
01057       }
01058     } else if (mActionType == RESIZEBOTTOM) {
01059       if (mEndCell.y() >= mActionItem->cellYTop()) {
01060         mActionItem->expandBottom(gpos.y() - mEndCell.y());
01061         adjustItemPosition( mActionItem );
01062       }
01063     } else if (mActionType == RESIZELEFT) {
01064       if (mEndCell.x() <= mActionItem->cellXRight()) {
01065         mActionItem->expandLeft( gpos.x() - mEndCell.x() );
01066         adjustItemPosition( mActionItem );
01067       }
01068     } else if (mActionType == RESIZERIGHT) {
01069       if (mEndCell.x() >= mActionItem->cellXLeft()) {
01070         mActionItem->expandRight(gpos.x() - mEndCell.x());
01071         adjustItemPosition( mActionItem );
01072       }
01073     }
01074     mEndCell = gpos;
01075   }
01076 }
01077 
01078 void KOAgenda::endItemAction()
01079 {
01080 //  kdDebug(5850) << "KOAgenda::endItemAction() " << endl;
01081   mActionType = NOP;
01082   mScrollUpTimer.stop();
01083   mScrollDownTimer.stop();
01084   setCursor( arrowCursor );
01085   bool multiModify = false;
01086   // FIXME: do the cloning here...
01087   Incidence *inc = mActionItem->incidence();
01088 
01089   if ( mStartCell.x() == mEndCell.x() && mStartCell.y() == mEndCell.y() ) {
01090     // not really moved, so stop any change
01091     if ( mItemMoved ) {
01092       mItemMoved = false;
01093       mChanger->endChange( inc, mResPair.first, mResPair.second );
01094     }
01095   }
01096 
01097   if ( mItemMoved ) {
01098     Incidence *incToChange = inc;
01099     if ( mActionItem->incidence()->doesRecur() ) {
01100 
01101       Incidence* oldIncSaved = inc->clone();
01102       KOGlobals::WhichOccurrences chosenOption;
01103       incToChange = mCalendarView->singleOccurrenceOrAll( inc,
01104                                                           KOGlobals::EDIT,
01105                                                           chosenOption,
01106                                                           mActionItem->itemDate() );
01107 
01108       if ( chosenOption == KOGlobals::ONLY_THIS_ONE ||
01109            chosenOption == KOGlobals::ONLY_FUTURE ) {
01110         multiModify = true;
01111         enableAgendaUpdate( false );
01112 
01113         mChanger->addIncidence( incToChange, 0, QString(), this );
01114         enableAgendaUpdate( true );
01115         KOGlobals::WhatChanged wc = chosenOption == KOGlobals::ONLY_THIS_ONE ?
01116                                     KOGlobals::RECURRENCE_MODIFIED_ONE_ONLY :
01117                                     KOGlobals::RECURRENCE_MODIFIED_ALL_FUTURE;
01118 
01119         mChanger->changeIncidence( oldIncSaved, inc, wc, this );
01120 
01121         mActionItem->dissociateFromMultiItem();
01122         mActionItem->setIncidence( incToChange );
01123       }
01124     }
01125 
01126     if ( incToChange ) {
01127       mActionItem->endMove();
01128       KOAgendaItem *placeItem = mActionItem->firstMultiItem();
01129       if  ( !placeItem ) {
01130         placeItem = mActionItem;
01131       }
01132 
01133       KOAgendaItem *modif = placeItem;
01134 
01135       QPtrList<KOAgendaItem> oldconflictItems = placeItem->conflictItems();
01136       KOAgendaItem *item;
01137       for ( item = oldconflictItems.first(); item != 0;
01138             item = oldconflictItems.next() ) {
01139         placeSubCells( item );
01140       }
01141       while ( placeItem ) {
01142         placeSubCells( placeItem );
01143         placeItem = placeItem->nextMultiItem();
01144       }
01145 
01146       // Notify about change
01147       // the agenda view will apply the changes to the actual Incidence*!
01148       mChanger->endChange( inc, mResPair.first, mResPair.second );
01149       emit itemModified( modif );
01150     } else {
01151 
01152       mActionItem->resetMove();
01153       placeSubCells( mActionItem );
01154 
01155       // the item was moved, but not further modified, since it's not recurring
01156       // make sure the view updates anyhow, with the right item
01157       mChanger->endChange( inc, mResPair.first, mResPair.second );
01158       emit itemModified( mActionItem );
01159     }
01160   }
01161 
01162   mActionItem = 0;
01163   mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), QString() );
01164   mItemMoved = false;
01165 
01166   if ( multiModify ) {
01167     emit endMultiModify();
01168   }
01169 
01170   kdDebug(5850) << "KOAgenda::endItemAction() done" << endl;
01171 }
01172 
01173 void KOAgenda::setActionCursor( int actionType, bool acting )
01174 {
01175   switch ( actionType ) {
01176     case MOVE:
01177       if (acting) setCursor( sizeAllCursor );
01178       else setCursor( arrowCursor );
01179       break;
01180     case RESIZETOP:
01181     case RESIZEBOTTOM:
01182       setCursor( sizeVerCursor );
01183       break;
01184     case RESIZELEFT:
01185     case RESIZERIGHT:
01186       setCursor( sizeHorCursor );
01187       break;
01188     default:
01189       setCursor( arrowCursor );
01190   }
01191 }
01192 
01193 void KOAgenda::setNoActionCursor( KOAgendaItem *moveItem, const QPoint& viewportPos )
01194 {
01195 //  kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
01196 //  QPoint point = viewport()->mapToGlobal(viewportPos);
01197 //  kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
01198 //  point = clipper()->mapFromGlobal(point);
01199 //  kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
01200 
01201   QPoint pos = viewportToContents( viewportPos );
01202   bool noResize = (moveItem && moveItem->incidence() &&
01203       moveItem->incidence()->type() == "Todo");
01204 
01205   KOAgenda::MouseActionType resizeType = MOVE;
01206   if ( !noResize ) resizeType = isInResizeArea( mAllDayMode, pos , moveItem);
01207   setActionCursor( resizeType );
01208 }
01209 
01210 
01213 double KOAgenda::calcSubCellWidth( KOAgendaItem *item )
01214 {
01215   QPoint pt, pt1;
01216   pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01217   pt1 = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) +
01218                         QPoint( 1, 1 ) );
01219   pt1 -= pt;
01220   int maxSubCells = item->subCells();
01221   double newSubCellWidth;
01222   if ( mAllDayMode ) {
01223     newSubCellWidth = double( pt1.y() ) / maxSubCells;
01224   } else {
01225     newSubCellWidth = double( pt1.x() ) / maxSubCells;
01226   }
01227   return newSubCellWidth;
01228 }
01229 
01230 void KOAgenda::adjustItemPosition( KOAgendaItem *item )
01231 {
01232   if (!item) return;
01233   item->resize( int( mGridSpacingX * item->cellWidth() ),
01234                 int( mGridSpacingY * item->cellHeight() ) );
01235   int clXLeft = item->cellXLeft();
01236   if ( KOGlobals::self()->reverseLayout() )
01237     clXLeft = item->cellXRight() + 1;
01238   QPoint cpos = gridToContents( QPoint( clXLeft, item->cellYTop() ) );
01239   moveChild( item, cpos.x(), cpos.y() );
01240 }
01241 
01242 void KOAgenda::placeAgendaItem( KOAgendaItem *item, double subCellWidth )
01243 {
01244 //  kdDebug(5850) << "KOAgenda::placeAgendaItem(): " << item->incidence()->summary()
01245 //            << " subCellWidth: " << subCellWidth << endl;
01246 
01247   // "left" upper corner, no subcells yet, RTL layouts have right/left switched, widths are negative then
01248   QPoint pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01249   // right lower corner
01250   QPoint pt1 = gridToContents( QPoint( item->cellXLeft() + item->cellWidth(),
01251       item->cellYBottom()+1 ) );
01252 
01253   double subCellPos = item->subCell() * subCellWidth;
01254 
01255   // we need to add 0.01 to make sure we don't loose one pixed due to
01256   // numerics (i.e. if it would be x.9998, we want the integer, not rounded down.
01257   double delta=0.01;
01258   if (subCellWidth<0) delta=-delta;
01259   int height, width, xpos, ypos;
01260   if (mAllDayMode) {
01261     width = pt1.x()-pt.x();
01262     height = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01263     xpos = pt.x();
01264     ypos = pt.y() + int( subCellPos );
01265   } else {
01266     width = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01267     height = pt1.y()-pt.y();
01268     xpos = pt.x() + int( subCellPos );
01269     ypos = pt.y();
01270   }
01271   if ( KOGlobals::self()->reverseLayout() ) { // RTL language/layout
01272     xpos += width;
01273     width = -width;
01274   }
01275   if ( height<0 ) { // BTT (bottom-to-top) layout ?!?
01276     ypos += height;
01277     height = -height;
01278   }
01279   item->resize( width, height );
01280   moveChild( item, xpos, ypos );
01281 }
01282 
01283 /*
01284   Place item in cell and take care that multiple items using the same cell do
01285   not overlap. This method is not yet optimal. It doesn't use the maximum space
01286   it can get in all cases.
01287   At the moment the method has a bug: When an item is placed only the sub cell
01288   widths of the items are changed, which are within the Y region the item to
01289   place spans. When the sub cell width change of one of this items affects a
01290   cell, where other items are, which do not overlap in Y with the item to place,
01291   the display gets corrupted, although the corruption looks quite nice.
01292 */
01293 void KOAgenda::placeSubCells( KOAgendaItem *placeItem )
01294 {
01295 #if 0
01296   kdDebug(5850) << "KOAgenda::placeSubCells()" << endl;
01297   if ( placeItem ) {
01298     Incidence *event = placeItem->incidence();
01299     if ( !event ) {
01300       kdDebug(5850) << "  event is 0" << endl;
01301     } else {
01302       kdDebug(5850) << "  event: " << event->summary() << endl;
01303     }
01304   } else {
01305     kdDebug(5850) << "  placeItem is 0" << endl;
01306   }
01307   kdDebug(5850) << "KOAgenda::placeSubCells()..." << endl;
01308 #endif
01309 
01310   QPtrList<KOrg::CellItem> cells;
01311   KOAgendaItem *item;
01312   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01313     cells.append( item );
01314   }
01315 
01316   QPtrList<KOrg::CellItem> items = KOrg::CellItem::placeItem( cells,
01317                                                               placeItem );
01318 
01319   placeItem->setConflictItems( QPtrList<KOAgendaItem>() );
01320   double newSubCellWidth = calcSubCellWidth( placeItem );
01321   KOrg::CellItem *i;
01322   for ( i = items.first(); i; i = items.next() ) {
01323     item = static_cast<KOAgendaItem *>( i );
01324     placeAgendaItem( item, newSubCellWidth );
01325     item->addConflictItem( placeItem );
01326     placeItem->addConflictItem( item );
01327   }
01328   if ( items.isEmpty() ) {
01329     placeAgendaItem( placeItem, newSubCellWidth );
01330   }
01331   placeItem->update();
01332 }
01333 
01334 int KOAgenda::columnWidth( int column )
01335 {
01336   int start = gridToContents( QPoint( column, 0 ) ).x();
01337   if (KOGlobals::self()->reverseLayout() )
01338     column--;
01339   else
01340     column++;
01341   int end = gridToContents( QPoint( column, 0 ) ).x();
01342   return end - start;
01343 }
01344 /*
01345   Draw grid in the background of the agenda.
01346 */
01347 void KOAgenda::drawContents(QPainter* p, int cx, int cy, int cw, int ch)
01348 {
01349   QPixmap db(cw, ch);
01350   db.fill(KOPrefs::instance()->mAgendaBgColor);
01351   QPainter dbp(&db);
01352   dbp.translate(-cx,-cy);
01353 
01354 //  kdDebug(5850) << "KOAgenda::drawContents()" << endl;
01355   double lGridSpacingY = mGridSpacingY*2;
01356 
01357   // Highlight working hours
01358   if (mWorkingHoursEnable) {
01359     QPoint pt1( cx, mWorkingHoursYTop );
01360     QPoint pt2( cx+cw, mWorkingHoursYBottom );
01361     if ( pt2.x() >= pt1.x() /*&& pt2.y() >= pt1.y()*/) {
01362       int gxStart = contentsToGrid( pt1 ).x();
01363       int gxEnd = contentsToGrid( pt2 ).x();
01364       // correct start/end for rtl layouts
01365       if ( gxStart > gxEnd ) {
01366         int tmp = gxStart;
01367         gxStart = gxEnd;
01368         gxEnd = tmp;
01369       }
01370       int xoffset = ( KOGlobals::self()->reverseLayout()?1:0 );
01371       while( gxStart <= gxEnd ) {
01372         int xStart = gridToContents( QPoint( gxStart+xoffset, 0 ) ).x();
01373         int xWidth = columnWidth( gxStart ) + 1;
01374         if ( pt2.y() < pt1.y() ) {
01375           // overnight working hours
01376           if ( ( (gxStart==0) && !mHolidayMask->at(mHolidayMask->count()-1) ) ||
01377                ( (gxStart>0) && (gxStart<int(mHolidayMask->count())) && (!mHolidayMask->at(gxStart-1) ) ) ) {
01378             if ( pt2.y() > cy ) {
01379               dbp.fillRect( xStart, cy, xWidth, pt2.y() - cy + 1,
01380                             KOPrefs::instance()->mWorkingHoursColor);
01381             }
01382           }
01383           if ( (gxStart < int(mHolidayMask->count()-1)) && (!mHolidayMask->at(gxStart)) ) {
01384             if ( pt1.y() < cy + ch - 1 ) {
01385               dbp.fillRect( xStart, pt1.y(), xWidth, cy + ch - pt1.y() + 1,
01386                             KOPrefs::instance()->mWorkingHoursColor);
01387             }
01388           }
01389         } else {
01390           // last entry in holiday mask denotes the previous day not visible (needed for overnight shifts)
01391           if ( gxStart < int(mHolidayMask->count()-1) && !mHolidayMask->at(gxStart)) {
01392             dbp.fillRect( xStart, pt1.y(), xWidth, pt2.y() - pt1.y() + 1,
01393                           KOPrefs::instance()->mWorkingHoursColor );
01394           }
01395         }
01396         ++gxStart;
01397       }
01398     }
01399   }
01400 
01401   // draw selection
01402   if ( mHasSelection ) {
01403     QPoint pt, pt1;
01404 
01405     if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
01406       // draw start day
01407       pt = gridToContents( mSelectionStartCell );
01408       pt1 = gridToContents( QPoint( mSelectionStartCell.x() + 1, mRows + 1 ) );
01409       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01410       // draw all other days between the start day and the day of the selection end
01411       for ( int c = mSelectionStartCell.x() + 1; c < mSelectionEndCell.x(); ++c ) {
01412         pt = gridToContents( QPoint( c, 0 ) );
01413         pt1 = gridToContents( QPoint( c + 1, mRows + 1 ) );
01414         dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01415       }
01416       // draw end day
01417       pt = gridToContents( QPoint( mSelectionEndCell.x(), 0 ) );
01418       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01419       dbp.fillRect( QRect( pt, pt1), KOPrefs::instance()->mHighlightColor );
01420     }  else { // single day selection
01421       pt = gridToContents( mSelectionStartCell );
01422       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01423       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01424     }
01425   }
01426 
01427   QPen hourPen( KOPrefs::instance()->mAgendaBgColor.dark( 150 ) );
01428   QPen halfHourPen( KOPrefs::instance()->mAgendaBgColor.dark( 125 ) );
01429   dbp.setPen( hourPen );
01430 
01431   // Draw vertical lines of grid, start with the last line not yet visible
01432   //  kdDebug(5850) << "drawContents cx: " << cx << " cy: " << cy << " cw: " << cw << " ch: " << ch << endl;
01433   double x = ( int( cx / mGridSpacingX ) ) * mGridSpacingX;
01434   while (x < cx + cw) {
01435     dbp.drawLine( int( x ), cy, int( x ), cy + ch );
01436     x+=mGridSpacingX;
01437   }
01438 
01439   // Draw horizontal lines of grid
01440   double y = ( int( cy / (2*lGridSpacingY) ) ) * 2 * lGridSpacingY;
01441   while (y < cy + ch) {
01442 //    kdDebug(5850) << " y: " << y << endl;
01443     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01444     y += 2 * lGridSpacingY;
01445   }
01446   y = ( 2 * int( cy / (2*lGridSpacingY) ) + 1) * lGridSpacingY;
01447   dbp.setPen( halfHourPen );
01448   while (y < cy + ch) {
01449 //    kdDebug(5850) << " y: " << y << endl;
01450     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01451     y+=2*lGridSpacingY;
01452   }
01453   p->drawPixmap(cx,cy, db);
01454 }
01455 
01456 /*
01457   Convert srcollview contents coordinates to agenda grid coordinates.
01458 */
01459 QPoint KOAgenda::contentsToGrid ( const QPoint &pos ) const
01460 {
01461   int gx = int( KOGlobals::self()->reverseLayout() ?
01462         mColumns - pos.x()/mGridSpacingX : pos.x()/mGridSpacingX );
01463   int gy = int( pos.y()/mGridSpacingY );
01464   return QPoint( gx, gy );
01465 }
01466 
01467 /*
01468   Convert agenda grid coordinates to scrollview contents coordinates.
01469 */
01470 QPoint KOAgenda::gridToContents( const QPoint &gpos ) const
01471 {
01472   int x = int( KOGlobals::self()->reverseLayout() ?
01473              (mColumns - gpos.x())*mGridSpacingX : gpos.x()*mGridSpacingX );
01474   int y = int( gpos.y()*mGridSpacingY );
01475   return QPoint( x, y );
01476 }
01477 
01478 
01479 /*
01480   Return Y coordinate corresponding to time. Coordinates are rounded to fit into
01481   the grid.
01482 */
01483 int KOAgenda::timeToY(const QTime &time)
01484 {
01485 //  kdDebug(5850) << "Time: " << time.toString() << endl;
01486   int minutesPerCell = 24 * 60 / mRows;
01487 //  kdDebug(5850) << "minutesPerCell: " << minutesPerCell << endl;
01488   int timeMinutes = time.hour() * 60 + time.minute();
01489 //  kdDebug(5850) << "timeMinutes: " << timeMinutes << endl;
01490   int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
01491 //  kdDebug(5850) << "y: " << Y << endl;
01492 //  kdDebug(5850) << "\n" << endl;
01493   return Y;
01494 }
01495 
01496 
01497 /*
01498   Return time corresponding to cell y coordinate. Coordinates are rounded to
01499   fit into the grid.
01500 */
01501 QTime KOAgenda::gyToTime(int gy)
01502 {
01503 //  kdDebug(5850) << "gyToTime: " << gy << endl;
01504   int secondsPerCell = 24 * 60 * 60/ mRows;
01505 
01506   int timeSeconds = secondsPerCell * gy;
01507 
01508   QTime time( 0, 0, 0 );
01509   if ( timeSeconds < 24 * 60 * 60 ) {
01510     time = time.addSecs(timeSeconds);
01511   } else {
01512     time.setHMS( 23, 59, 59 );
01513   }
01514 //  kdDebug(5850) << "  gyToTime: " << time.toString() << endl;
01515 
01516   return time;
01517 }
01518 
01519 QMemArray<int> KOAgenda::minContentsY()
01520 {
01521   QMemArray<int> minArray;
01522   minArray.fill( timeToY( QTime(23, 59) ), mSelectedDates.count() );
01523   for ( KOAgendaItem *item = mItems.first();
01524         item != 0; item = mItems.next() ) {
01525     int ymin = item->cellYTop();
01526     int index = item->cellXLeft();
01527     if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
01528       if ( ymin < minArray[index] && mItemsToDelete.findRef( item ) == -1 )
01529         minArray[index] = ymin;
01530     }
01531   }
01532 
01533   return minArray;
01534 }
01535 
01536 QMemArray<int> KOAgenda::maxContentsY()
01537 {
01538   QMemArray<int> maxArray;
01539   maxArray.fill( timeToY( QTime(0, 0) ), mSelectedDates.count() );
01540   for ( KOAgendaItem *item = mItems.first();
01541         item != 0; item = mItems.next() ) {
01542     int ymax = item->cellYBottom();
01543     int index = item->cellXLeft();
01544     if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
01545       if ( ymax > maxArray[index] && mItemsToDelete.findRef( item ) == -1 )
01546         maxArray[index] = ymax;
01547     }
01548   }
01549 
01550   return maxArray;
01551 }
01552 
01553 void KOAgenda::setStartTime( const QTime &startHour )
01554 {
01555   double startPos = ( startHour.hour()/24. + startHour.minute()/1440. +
01556                       startHour.second()/86400. ) * mRows * gridSpacingY();
01557   setContentsPos( 0, int( startPos ) );
01558 }
01559 
01560 
01561 /*
01562   Insert KOAgendaItem into agenda.
01563 */
01564 KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const QDate &qd, int X,
01565                                     int YTop, int YBottom )
01566 {
01567 #if 0
01568   kdDebug(5850) << "KOAgenda::insertItem:" << incidence->summary() << "-"
01569                 << qd.toString() << " ;top, bottom:" << YTop << "," << YBottom
01570                 << endl;
01571 #endif
01572 
01573   if ( mAllDayMode ) {
01574     kdDebug(5850) << "KOAgenda: calling insertItem in all-day mode is illegal." << endl;
01575     return 0;
01576   }
01577 
01578 
01579   mActionType = NOP;
01580 
01581   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, incidence, qd, viewport() );
01582   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem * ) ),
01583            SLOT( removeAgendaItem( KOAgendaItem * ) ) );
01584   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem * ) ),
01585            SLOT( showAgendaItem( KOAgendaItem * ) ) );
01586 
01587   if ( YBottom <= YTop ) {
01588     kdDebug(5850) << "KOAgenda::insertItem(): Text: " << agendaItem->text() << " YSize<0" << endl;
01589     YBottom = YTop;
01590   }
01591 
01592   agendaItem->resize( int( ( X + 1 ) * mGridSpacingX ) -
01593                       int( X * mGridSpacingX ),
01594                       int( YTop * mGridSpacingY ) -
01595                       int( ( YBottom + 1 ) * mGridSpacingY ) );
01596   agendaItem->setCellXY( X, YTop, YBottom );
01597   agendaItem->setCellXRight( X );
01598   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, incidence ) );
01599   agendaItem->installEventFilter( this );
01600 
01601   addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
01602   mItems.append( agendaItem );
01603 
01604   placeSubCells( agendaItem );
01605 
01606   agendaItem->show();
01607 
01608   marcus_bains();
01609 
01610   return agendaItem;
01611 }
01612 
01613 /*
01614   Insert all-day KOAgendaItem into agenda.
01615 */
01616 KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const QDate &qd,
01617                                           int XBegin, int XEnd )
01618 {
01619   if ( !mAllDayMode ) {
01620     kdDebug(5850) << "KOAgenda: calling insertAllDayItem in non all-day mode is illegal." << endl;
01621     return 0;
01622   }
01623 
01624   mActionType = NOP;
01625 
01626   KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, event, qd, viewport() );
01627   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem* ) ),
01628            SLOT( removeAgendaItem( KOAgendaItem* ) ) );
01629   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem* ) ),
01630            SLOT( showAgendaItem( KOAgendaItem* ) ) );
01631 
01632   agendaItem->setCellXY( XBegin, 0, 0 );
01633   agendaItem->setCellXRight( XEnd );
01634 
01635   double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
01636   double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
01637                                    agendaItem->cellXLeft() );
01638 
01639   agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
01640 
01641   agendaItem->installEventFilter( this );
01642   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
01643   addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
01644   mItems.append( agendaItem );
01645 
01646   placeSubCells( agendaItem );
01647 
01648   agendaItem->show();
01649 
01650   return agendaItem;
01651 }
01652 
01653 
01654 void KOAgenda::insertMultiItem (Event *event,const QDate &qd,int XBegin,int XEnd,
01655                                 int YTop,int YBottom)
01656 {
01657   if (mAllDayMode) {
01658     kdDebug(5850) << "KOAgenda: calling insertMultiItem in all-day mode is illegal." << endl;
01659     return;
01660   }
01661   mActionType = NOP;
01662 
01663   int cellX,cellYTop,cellYBottom;
01664   QString newtext;
01665   int width = XEnd - XBegin + 1;
01666   int count = 0;
01667   KOAgendaItem *current = 0;
01668   QPtrList<KOAgendaItem> multiItems;
01669   int visibleCount = mSelectedDates.first().daysTo(mSelectedDates.last());
01670   for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
01671     ++count;
01672     //Only add the items that are visible.
01673     if( cellX >=0 && cellX <= visibleCount ) {
01674       if ( cellX == XBegin ) cellYTop = YTop;
01675       else cellYTop = 0;
01676       if ( cellX == XEnd ) cellYBottom = YBottom;
01677       else cellYBottom = rows() - 1;
01678       newtext = QString("(%1/%2): ").arg( count ).arg( width );
01679       newtext.append( event->summary() );
01680 
01681       current = insertItem( event, qd, cellX, cellYTop, cellYBottom );
01682       current->setText( newtext );
01683       multiItems.append( current );
01684     }
01685   }
01686 
01687   KOAgendaItem *next = 0;
01688   KOAgendaItem *prev = 0;
01689   KOAgendaItem *last = multiItems.last();
01690   KOAgendaItem *first = multiItems.first();
01691   KOAgendaItem *setFirst,*setLast;
01692   current = first;
01693   while (current) {
01694     next = multiItems.next();
01695     if (current == first) setFirst = 0;
01696     else setFirst = first;
01697     if (current == last) setLast = 0;
01698     else setLast = last;
01699 
01700     current->setMultiItem(setFirst, prev, next, setLast);
01701     prev=current;
01702     current = next;
01703   }
01704 
01705   marcus_bains();
01706 }
01707 
01708 void KOAgenda::removeIncidence( Incidence *incidence )
01709 {
01710   // First find all items to be deleted and store them
01711   // in its own list. Otherwise removeAgendaItem will reset
01712   // the current position and mess this up.
01713   QPtrList<KOAgendaItem> itemsToRemove;
01714 
01715   KOAgendaItem *item = mItems.first();
01716   while ( item ) {
01717     if ( item->incidence() == incidence ) {
01718       itemsToRemove.append( item );
01719     }
01720     item = mItems.next();
01721   }
01722   item = itemsToRemove.first();
01723   while ( item ) {
01724     removeAgendaItem( item );
01725     item = itemsToRemove.next();
01726   }
01727 }
01728 
01729 void KOAgenda::showAgendaItem( KOAgendaItem *agendaItem )
01730 {
01731   if ( !agendaItem ) return;
01732   agendaItem->hide();
01733   addChild( agendaItem );
01734   if ( !mItems.containsRef( agendaItem ) )
01735     mItems.append( agendaItem );
01736   placeSubCells( agendaItem );
01737 
01738   agendaItem->show();
01739 }
01740 
01741 bool KOAgenda::removeAgendaItem( KOAgendaItem *item )
01742 {
01743   // we found the item. Let's remove it and update the conflicts
01744   bool taken = false;
01745   KOAgendaItem *thisItem = item;
01746   QPtrList<KOAgendaItem> conflictItems = thisItem->conflictItems();
01747   removeChild( thisItem );
01748   int pos = mItems.find( thisItem );
01749   if ( pos>=0 ) {
01750     mItems.take( pos );
01751     taken = true;
01752   }
01753 
01754   KOAgendaItem *confitem;
01755   for ( confitem = conflictItems.first(); confitem != 0;
01756         confitem = conflictItems.next() ) {
01757     // the item itself is also in its own conflictItems list!
01758     if ( confitem != thisItem ) placeSubCells(confitem);
01759 
01760   }
01761   mItemsToDelete.append( thisItem );
01762   QTimer::singleShot( 0, this, SLOT( deleteItemsToDelete() ) );
01763   return taken;
01764 }
01765 
01766 void KOAgenda::deleteItemsToDelete()
01767 {
01768   mItemsToDelete.clear();
01769 }
01770 
01771 /*QSizePolicy KOAgenda::sizePolicy() const
01772 {
01773   // Thought this would make the all-day event agenda minimum size and the
01774   // normal agenda take the remaining space. But it doesnt work. The QSplitter
01775   // dont seem to think that an Expanding widget needs more space than a
01776   // Preferred one.
01777   // But it doesnt hurt, so it stays.
01778   if (mAllDayMode) {
01779     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
01780   } else {
01781     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
01782   }
01783 }
01784 */
01785 
01786 /*
01787   Overridden from QScrollView to provide proper resizing of KOAgendaItems.
01788 */
01789 void KOAgenda::resizeEvent ( QResizeEvent *ev )
01790 {
01791 //  kdDebug(5850) << "KOAgenda::resizeEvent" << endl;
01792 
01793   QSize newSize( ev->size() );
01794   if (mAllDayMode) {
01795     mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
01796     mGridSpacingY = newSize.height() - 2 * frameWidth();
01797   } else {
01798     int scrollbarWidth = vScrollBarMode() != AlwaysOff ? verticalScrollBar()->width() : 0;
01799     mGridSpacingX = double( newSize.width() - scrollbarWidth - 2 * frameWidth()) / double(mColumns);
01800     // make sure that there are not more than 24 per day
01801     mGridSpacingY = double(newSize.height() - 2 * frameWidth()) / double(mRows);
01802     if ( mGridSpacingY < mDesiredGridSpacingY )
01803       mGridSpacingY = mDesiredGridSpacingY;
01804   }
01805   calculateWorkingHours();
01806   QTimer::singleShot( 0, this, SLOT( resizeAllContents() ) );
01807   emit gridSpacingYChanged( mGridSpacingY * 4 );
01808   QScrollView::resizeEvent(ev);
01809 }
01810 
01811 void KOAgenda::resizeAllContents()
01812 {
01813   double subCellWidth;
01814   if ( mItems.count() > 0 ) {
01815     KOAgendaItem *item;
01816     if (mAllDayMode) {
01817       for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01818         subCellWidth = calcSubCellWidth( item );
01819         placeAgendaItem( item, subCellWidth );
01820       }
01821     } else {
01822       for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01823         subCellWidth = calcSubCellWidth( item );
01824         placeAgendaItem( item, subCellWidth );
01825       }
01826     }
01827   }
01828   checkScrollBoundaries();
01829   marcus_bains();
01830 }
01831 
01832 void KOAgenda::scrollUp()
01833 {
01834   scrollBy(0,-mScrollOffset);
01835 }
01836 
01837 
01838 void KOAgenda::scrollDown()
01839 {
01840   scrollBy(0,mScrollOffset);
01841 }
01842 
01843 
01844 /*
01845   Calculates the minimum width
01846 */
01847 int KOAgenda::minimumWidth() const
01848 {
01849   // FIXME:: develop a way to dynamically determine the minimum width
01850   int min = 100;
01851 
01852   return min;
01853 }
01854 
01855 void KOAgenda::updateConfig()
01856 {
01857   double oldGridSpacingY = mGridSpacingY;
01858 
01859   mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
01860   if ( mDesiredGridSpacingY < 4 || mDesiredGridSpacingY > 30 ) {
01861     mDesiredGridSpacingY = 10;
01862   }
01863 
01864   // make sure that there are not more than 24 per day
01865   mGridSpacingY = (double)height() / (double)mRows;
01866   if ( mGridSpacingY < mDesiredGridSpacingY ) {
01867     mGridSpacingY = mDesiredGridSpacingY;
01868   }
01869 
01870   //can be two doubles equal?, it's better to compare them with an epsilon
01871   if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 ) {
01872     resizeContents( int( mGridSpacingX * mColumns ),
01873                     int( mGridSpacingY * mRows ) );
01874   }
01875 
01876   calculateWorkingHours();
01877 
01878   marcus_bains();
01879 }
01880 
01881 void KOAgenda::checkScrollBoundaries()
01882 {
01883   // Invalidate old values to force update
01884   mOldLowerScrollValue = -1;
01885   mOldUpperScrollValue = -1;
01886 
01887   checkScrollBoundaries(verticalScrollBar()->value());
01888 }
01889 
01890 void KOAgenda::checkScrollBoundaries( int v )
01891 {
01892   int yMin = int( (v) / mGridSpacingY );
01893   int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
01894 
01895 //  kdDebug(5850) << "--- yMin: " << yMin << "  yMax: " << yMax << endl;
01896 
01897   if ( yMin != mOldLowerScrollValue ) {
01898     mOldLowerScrollValue = yMin;
01899     emit lowerYChanged(yMin);
01900   }
01901   if ( yMax != mOldUpperScrollValue ) {
01902     mOldUpperScrollValue = yMax;
01903     emit upperYChanged(yMax);
01904   }
01905 }
01906 
01907 int KOAgenda::visibleContentsYMin()
01908 {
01909   int v = verticalScrollBar()->value();
01910   return int( v / mGridSpacingY );
01911 }
01912 
01913 int KOAgenda::visibleContentsYMax()
01914 {
01915   int v = verticalScrollBar()->value();
01916   return int( ( v + visibleHeight() ) / mGridSpacingY );
01917 }
01918 
01919 void KOAgenda::deselectItem()
01920 {
01921   if (mSelectedItem.isNull()) return;
01922   mSelectedItem->select(false);
01923   mSelectedItem = 0;
01924 }
01925 
01926 void KOAgenda::selectItem(KOAgendaItem *item)
01927 {
01928   if ((KOAgendaItem *)mSelectedItem == item) return;
01929   deselectItem();
01930   if (item == 0) {
01931     emit incidenceSelected( 0, QDate() );
01932     return;
01933   }
01934   mSelectedItem = item;
01935   mSelectedItem->select();
01936   assert( mSelectedItem->incidence() );
01937   mSelectedUid = mSelectedItem->incidence()->uid();
01938   emit incidenceSelected( mSelectedItem->incidence(), mSelectedItem->itemDate() );
01939 }
01940 
01941 void KOAgenda::selectItemByUID( const QString& uid )
01942 {
01943   KOAgendaItem *item;
01944   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01945     if( item->incidence() && item->incidence()->uid() == uid ) {
01946       selectItem( item );
01947       break;
01948     }
01949   }
01950 }
01951 
01952 // This function seems never be called.
01953 void KOAgenda::keyPressEvent( QKeyEvent *kev )
01954 {
01955   switch(kev->key()) {
01956     case Key_PageDown:
01957       verticalScrollBar()->addPage();
01958       break;
01959     case Key_PageUp:
01960       verticalScrollBar()->subtractPage();
01961       break;
01962     case Key_Down:
01963       verticalScrollBar()->addLine();
01964       break;
01965     case Key_Up:
01966       verticalScrollBar()->subtractLine();
01967       break;
01968     default:
01969       ;
01970   }
01971 }
01972 
01973 void KOAgenda::calculateWorkingHours()
01974 {
01975   mWorkingHoursEnable = !mAllDayMode;
01976 
01977   QTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
01978   mWorkingHoursYTop = int( 4 * mGridSpacingY *
01979                            ( tmp.hour() + tmp.minute() / 60. +
01980                              tmp.second() / 3600. ) );
01981   tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
01982   mWorkingHoursYBottom = int( 4 * mGridSpacingY *
01983                               ( tmp.hour() + tmp.minute() / 60. +
01984                                 tmp.second() / 3600. ) - 1 );
01985 }
01986 
01987 
01988 DateList KOAgenda::dateList() const
01989 {
01990     return mSelectedDates;
01991 }
01992 
01993 void KOAgenda::setDateList(const DateList &selectedDates)
01994 {
01995     mSelectedDates = selectedDates;
01996     marcus_bains();
01997 }
01998 
01999 void KOAgenda::setHolidayMask(QMemArray<bool> *mask)
02000 {
02001   mHolidayMask = mask;
02002 
02003 }
02004 
02005 void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
02006 {
02007   kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
02008   QScrollView::contentsMousePressEvent(event);
02009 }
02010 
02011 void KOAgenda::setTypeAheadReceiver( QObject *o )
02012 {
02013   mTypeAheadReceiver = o;
02014 }
02015 
02016 QObject *KOAgenda::typeAheadReceiver() const
02017 {
02018   return mTypeAheadReceiver;
02019 }
KDE Home | KDE Accessibility Home | Description of Access Keys