korganizer

koagenda.cpp

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