korganizer

koagenda.cpp

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