korganizer

koagenda.cpp

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