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