korganizer

koagenda.cpp

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