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(0);
00071   connect(minutes, SIGNAL(timeout()), this, SLOT(updateLocation(bool)));
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( mCalendar,
00582                                            mClickedItem->incidence(),
00583                                            mClickedItem->itemDate() );
00584           }
00585         } else {
00586           KOAgendaItem* item = dynamic_cast<KOAgendaItem *>(object);
00587           if (item) {
00588             Incidence *incidence = item->incidence();
00589             if ( incidence->isReadOnly() ) {
00590               mActionItem = 0;
00591             } else {
00592               mActionItem = item;
00593               startItemAction(viewportPos);
00594             }
00595             // Warning: do selectItem() as late as possible, since all
00596             // sorts of things happen during this call. Some can lead to
00597             // this filter being run again and mActionItem being set to
00598             // null.
00599             selectItem( item );
00600           }
00601         }
00602       } else {
00603         if (me->button() == RightButton)
00604         {
00605           // if mouse pointer is not in selection, select the cell below the cursor
00606           QPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
00607           if ( !ptInSelection( gpos ) ) {
00608             mSelectionStartCell = gpos;
00609             mSelectionEndCell = gpos;
00610             mHasSelection = true;
00611             emit newStartSelectSignal();
00612             emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
00613             updateContents();
00614           }
00615           showNewEventPopupSignal();
00616         }
00617         else
00618         {
00619           // if mouse pointer is in selection, don't change selection
00620           QPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
00621           if ( !ptInSelection( gpos ) ) {
00622             selectItem(0);
00623             mActionItem = 0;
00624             setCursor(arrowCursor);
00625             startSelectAction(viewportPos);
00626           }
00627         }
00628       }
00629       break;
00630 
00631     case QEvent::MouseButtonRelease:
00632       if (mActionItem) {
00633         endItemAction();
00634       } else if ( mActionType == SELECT ) {
00635         endSelectAction( viewportPos );
00636       }
00637       // This nasty gridToContents(contentsToGrid(..)) is needed to
00638       // avoid an offset of a few pixels. Don't ask me why...
00639       emit mousePosSignal( gridToContents(contentsToGrid(
00640                            viewportToContents( viewportPos ) ) ));
00641       break;
00642 
00643     case QEvent::MouseMove: {
00644       // This nasty gridToContents(contentsToGrid(..)) is needed to
00645       // avoid an offset of a few pixels. Don't ask me why...
00646       QPoint indicatorPos = gridToContents(contentsToGrid(
00647                                           viewportToContents( viewportPos )));
00648       if (object != viewport()) {
00649         KOAgendaItem *moveItem = dynamic_cast<KOAgendaItem *>(object);
00650         if (moveItem && !moveItem->incidence()->isReadOnly() ) {
00651           if (!mActionItem)
00652             setNoActionCursor(moveItem,viewportPos);
00653           else {
00654             performItemAction(viewportPos);
00655 
00656             if ( mActionType == MOVE ) {
00657               // show cursor at the current begin of the item
00658               KOAgendaItem *firstItem = mActionItem->firstMultiItem();
00659               if (!firstItem) firstItem = mActionItem;
00660               indicatorPos = gridToContents( QPoint( firstItem->cellXLeft(),
00661                                                      firstItem->cellYTop() ) );
00662 
00663             } else if ( mActionType == RESIZEBOTTOM ) {
00664               // RESIZETOP is handled correctly, only resizebottom works differently
00665               indicatorPos = gridToContents( QPoint( mActionItem->cellXLeft(),
00666                                                      mActionItem->cellYBottom()+1 ) );
00667             }
00668 
00669           } // If we have an action item
00670         } // If move item && !read only
00671       } else {
00672           if ( mActionType == SELECT ) {
00673             performSelectAction( viewportPos );
00674 
00675             // show cursor at end of timespan
00676             if ( ((mStartCell.y() < mEndCell.y()) && (mEndCell.x() >= mStartCell.x())) ||
00677                  (mEndCell.x() > mStartCell.x()) )
00678               indicatorPos = gridToContents( QPoint(mEndCell.x(), mEndCell.y()+1) );
00679             else
00680               indicatorPos = gridToContents( mEndCell );
00681           }
00682         }
00683       emit mousePosSignal( indicatorPos );
00684       break; }
00685 
00686     case QEvent::MouseButtonDblClick:
00687       if (object == viewport()) {
00688         selectItem(0);
00689         emit newEventSignal();
00690       } else {
00691         KOAgendaItem *doubleClickedItem = dynamic_cast<KOAgendaItem *>(object);
00692         if (doubleClickedItem) {
00693           selectItem(doubleClickedItem);
00694           emit editIncidenceSignal(doubleClickedItem->incidence());
00695         }
00696       }
00697       break;
00698 
00699     default:
00700       break;
00701   }
00702 
00703   return true;
00704 }
00705 
00706 bool KOAgenda::ptInSelection( QPoint gpos ) const
00707 {
00708   if ( !mHasSelection ) {
00709     return false;
00710   } else if ( gpos.x()<mSelectionStartCell.x() || gpos.x()>mSelectionEndCell.x() ) {
00711     return false;
00712   } else if ( (gpos.x()==mSelectionStartCell.x()) && (gpos.y()<mSelectionStartCell.y()) ) {
00713     return false;
00714   } else if ( (gpos.x()==mSelectionEndCell.x()) && (gpos.y()>mSelectionEndCell.y()) ) {
00715     return false;
00716   }
00717   return true;
00718 }
00719 
00720 void KOAgenda::startSelectAction( const QPoint &viewportPos )
00721 {
00722   emit newStartSelectSignal();
00723 
00724   mActionType = SELECT;
00725   mSelectionStartPoint = viewportPos;
00726   mHasSelection = true;
00727 
00728   QPoint pos = viewportToContents( viewportPos );
00729   QPoint gpos = contentsToGrid( pos );
00730 
00731   // Store new selection
00732   mStartCell = gpos;
00733   mEndCell = gpos;
00734   mSelectionStartCell = gpos;
00735   mSelectionEndCell = gpos;
00736 
00737   updateContents();
00738 }
00739 
00740 void KOAgenda::performSelectAction(const QPoint& viewportPos)
00741 {
00742   QPoint pos = viewportToContents( viewportPos );
00743   QPoint gpos = contentsToGrid( pos );
00744 
00745   QPoint clipperPos = clipper()->
00746                       mapFromGlobal(viewport()->mapToGlobal(viewportPos));
00747 
00748   // Scroll if cursor was moved to upper or lower end of agenda.
00749   if (clipperPos.y() < mScrollBorderWidth) {
00750     mScrollUpTimer.start(mScrollDelay);
00751   } else if (visibleHeight() - clipperPos.y() <
00752              mScrollBorderWidth) {
00753     mScrollDownTimer.start(mScrollDelay);
00754   } else {
00755     mScrollUpTimer.stop();
00756     mScrollDownTimer.stop();
00757   }
00758 
00759   if ( gpos != mEndCell ) {
00760     mEndCell = gpos;
00761     if ( mStartCell.x()>mEndCell.x() ||
00762          ( mStartCell.x()==mEndCell.x() && mStartCell.y()>mEndCell.y() ) ) {
00763       // backward selection
00764       mSelectionStartCell = mEndCell;
00765       mSelectionEndCell = mStartCell;
00766     } else {
00767       mSelectionStartCell = mStartCell;
00768       mSelectionEndCell = mEndCell;
00769     }
00770 
00771     updateContents();
00772   }
00773 }
00774 
00775 void KOAgenda::endSelectAction( const QPoint &currentPos )
00776 {
00777   mScrollUpTimer.stop();
00778   mScrollDownTimer.stop();
00779 
00780   mActionType = NOP;
00781 
00782   emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
00783 
00784   if ( KOPrefs::instance()->mSelectionStartsEditor ) {
00785     if ( ( mSelectionStartPoint - currentPos ).manhattanLength() >
00786          QApplication::startDragDistance() ) {
00787        emitNewEventForSelection();
00788     }
00789   }
00790 }
00791 
00792 KOAgenda::MouseActionType KOAgenda::isInResizeArea( bool horizontal,
00793     const QPoint &pos, KOAgendaItem*item )
00794 {
00795   if (!item) return NOP;
00796   QPoint gridpos = contentsToGrid( pos );
00797   QPoint contpos = gridToContents( gridpos +
00798       QPoint( (KOGlobals::self()->reverseLayout())?1:0, 0 ) );
00799 
00800 //kdDebug(5850)<<"contpos="<<contpos<<", pos="<<pos<<", gpos="<<gpos<<endl;
00801 //kdDebug(5850)<<"clXLeft="<<clXLeft<<", clXRight="<<clXRight<<endl;
00802 
00803   if ( horizontal ) {
00804     int clXLeft = item->cellXLeft();
00805     int clXRight = item->cellXRight();
00806     if ( KOGlobals::self()->reverseLayout() ) {
00807       int tmp = clXLeft;
00808       clXLeft = clXRight;
00809       clXRight = tmp;
00810     }
00811     int gridDistanceX = int( pos.x() - contpos.x() );
00812     if (gridDistanceX < mResizeBorderWidth && clXLeft == gridpos.x() ) {
00813       if ( KOGlobals::self()->reverseLayout() ) return RESIZERIGHT;
00814       else return RESIZELEFT;
00815     } else if ((mGridSpacingX - gridDistanceX) < mResizeBorderWidth &&
00816                clXRight == gridpos.x() ) {
00817       if ( KOGlobals::self()->reverseLayout() ) return RESIZELEFT;
00818       else return RESIZERIGHT;
00819     } else {
00820       return MOVE;
00821     }
00822   } else {
00823     int gridDistanceY = int( pos.y() - contpos.y() );
00824     if (gridDistanceY < mResizeBorderWidth &&
00825         item->cellYTop() == gridpos.y() &&
00826         !item->firstMultiItem() ) {
00827       return RESIZETOP;
00828     } else if ((mGridSpacingY - gridDistanceY) < mResizeBorderWidth &&
00829                item->cellYBottom() == gridpos.y() &&
00830                !item->lastMultiItem() )  {
00831       return RESIZEBOTTOM;
00832     } else {
00833       return MOVE;
00834     }
00835   }
00836 }
00837 
00838 void KOAgenda::startItemAction(const QPoint& viewportPos)
00839 {
00840   QPoint pos = viewportToContents( viewportPos );
00841   mStartCell = contentsToGrid( pos );
00842   mEndCell = mStartCell;
00843 
00844   bool noResize = ( mActionItem->incidence()->type() == "Todo");
00845 
00846   mActionType = MOVE;
00847   if ( !noResize ) {
00848     mActionType = isInResizeArea( mAllDayMode, pos, mActionItem );
00849   }
00850 
00851 
00852   mActionItem->startMove();
00853   setActionCursor( mActionType, true );
00854 }
00855 
00856 void KOAgenda::performItemAction(const QPoint& viewportPos)
00857 {
00858 //  kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
00859 //  QPoint point = viewport()->mapToGlobal(viewportPos);
00860 //  kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
00861 //  point = clipper()->mapFromGlobal(point);
00862 //  kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
00863 //  kdDebug(5850) << "visible height: " << visibleHeight() << endl;
00864   QPoint pos = viewportToContents( viewportPos );
00865 //  kdDebug(5850) << "contents: " << x << "," << y << "\n" << endl;
00866   QPoint gpos = contentsToGrid( pos );
00867   QPoint clipperPos = clipper()->
00868                       mapFromGlobal(viewport()->mapToGlobal(viewportPos));
00869 
00870   // Cursor left active agenda area.
00871   // This starts a drag.
00872   if ( clipperPos.y() < 0 || clipperPos.y() > visibleHeight() ||
00873        clipperPos.x() < 0 || clipperPos.x() > visibleWidth() ) {
00874     if ( mActionType == MOVE ) {
00875       mScrollUpTimer.stop();
00876       mScrollDownTimer.stop();
00877       mActionItem->resetMove();
00878       placeSubCells( mActionItem );
00879       emit startDragSignal( mActionItem->incidence() );
00880       setCursor( arrowCursor );
00881       mActionItem = 0;
00882       mActionType = NOP;
00883       mItemMoved = false;
00884       if ( mItemMoved && mChanger )
00885         mChanger->endChange( mActionItem->incidence() );
00886       return;
00887     }
00888   } else {
00889     setActionCursor( mActionType );
00890   }
00891 
00892   // Scroll if item was moved to upper or lower end of agenda.
00893   if (clipperPos.y() < mScrollBorderWidth) {
00894     mScrollUpTimer.start(mScrollDelay);
00895   } else if (visibleHeight() - clipperPos.y() <
00896              mScrollBorderWidth) {
00897     mScrollDownTimer.start(mScrollDelay);
00898   } else {
00899     mScrollUpTimer.stop();
00900     mScrollDownTimer.stop();
00901   }
00902 
00903   // Move or resize item if necessary
00904   if ( mEndCell != gpos ) {
00905     if ( !mItemMoved ) {
00906       if ( !mChanger || !mChanger->beginChange( mActionItem->incidence() ) ) {
00907         KMessageBox::information( this, i18n("Unable to lock item for "
00908                              "modification. You cannot make any changes."),
00909                              i18n("Locking Failed"), "AgendaLockingFailed" );
00910         mScrollUpTimer.stop();
00911         mScrollDownTimer.stop();
00912         mActionItem->resetMove();
00913         placeSubCells( mActionItem );
00914         setCursor( arrowCursor );
00915         mActionItem = 0;
00916         mActionType = NOP;
00917         mItemMoved = false;
00918         return;
00919       }
00920       mItemMoved = true;
00921     }
00922     mActionItem->raise();
00923     if (mActionType == MOVE) {
00924       // Move all items belonging to a multi item
00925       KOAgendaItem *firstItem = mActionItem->firstMultiItem();
00926       if (!firstItem) firstItem = mActionItem;
00927       KOAgendaItem *lastItem = mActionItem->lastMultiItem();
00928       if (!lastItem) lastItem = mActionItem;
00929       QPoint deltapos = gpos - mEndCell;
00930       KOAgendaItem *moveItem = firstItem;
00931       while (moveItem) {
00932         bool changed=false;
00933         if ( deltapos.x()!=0 ) {
00934           moveItem->moveRelative( deltapos.x(), 0 );
00935           changed=true;
00936         }
00937         // in agenda's all day view don't try to move multi items, since there are none
00938         if ( moveItem==firstItem && !mAllDayMode ) { // is the first item
00939           int newY = deltapos.y() + moveItem->cellYTop();
00940           // If event start moved earlier than 0:00, it starts the previous day
00941           if ( newY<0 ) {
00942             moveItem->expandTop( -moveItem->cellYTop() );
00943             // prepend a new item at ( x-1, rows()+newY to rows() )
00944             KOAgendaItem *newFirst = firstItem->prevMoveItem();
00945             // cell's y values are first and last cell of the bar, so if newY=-1, they need to be the same
00946             if (newFirst) {
00947               newFirst->setCellXY(moveItem->cellXLeft()-1, rows()+newY, rows()-1);
00948               mItems.append( newFirst );
00949               moveItem->resize( int( mGridSpacingX * newFirst->cellWidth() ),
00950                                 int( mGridSpacingY * newFirst->cellHeight() ));
00951               QPoint cpos = gridToContents( QPoint( newFirst->cellXLeft(), newFirst->cellYTop() ) );
00952               addChild( newFirst, cpos.x(), cpos.y() );
00953             } else {
00954               newFirst = insertItem( moveItem->incidence(), moveItem->itemDate(),
00955                 moveItem->cellXLeft()-1, rows()+newY, rows()-1 ) ;
00956             }
00957             if (newFirst) newFirst->show();
00958             moveItem->prependMoveItem(newFirst);
00959             firstItem=newFirst;
00960           } else if ( newY>=rows() ) {
00961             // If event start is moved past 24:00, it starts the next day
00962             // erase current item (i.e. remove it from the multiItem list)
00963             firstItem = moveItem->nextMultiItem();
00964             moveItem->hide();
00965             mItems.take( mItems.find( moveItem ) );
00966             removeChild( moveItem );
00967             mActionItem->removeMoveItem(moveItem);
00968             moveItem=firstItem;
00969             // adjust next day's item
00970             if (moveItem) moveItem->expandTop( rows()-newY );
00971           } else {
00972             moveItem->expandTop(deltapos.y());
00973           }
00974           changed=true;
00975         }
00976         if ( !moveItem->lastMultiItem() && !mAllDayMode ) { // is the last item
00977           int newY = deltapos.y()+moveItem->cellYBottom();
00978           if (newY<0) {
00979             // erase current item
00980             lastItem = moveItem->prevMultiItem();
00981             moveItem->hide();
00982             mItems.take( mItems.find(moveItem) );
00983             removeChild( moveItem );
00984             moveItem->removeMoveItem( moveItem );
00985             moveItem = lastItem;
00986             moveItem->expandBottom(newY+1);
00987           } else if (newY>=rows()) {
00988             moveItem->expandBottom( rows()-moveItem->cellYBottom()-1 );
00989             // append item at ( x+1, 0 to newY-rows() )
00990             KOAgendaItem *newLast = lastItem->nextMoveItem();
00991             if (newLast) {
00992               newLast->setCellXY( moveItem->cellXLeft()+1, 0, newY-rows()-1 );
00993               mItems.append(newLast);
00994               moveItem->resize( int( mGridSpacingX * newLast->cellWidth() ),
00995                                 int( mGridSpacingY * newLast->cellHeight() ));
00996               QPoint cpos = gridToContents( QPoint( newLast->cellXLeft(), newLast->cellYTop() ) ) ;
00997               addChild( newLast, cpos.x(), cpos.y() );
00998             } else {
00999               newLast = insertItem( moveItem->incidence(), moveItem->itemDate(),
01000                 moveItem->cellXLeft()+1, 0, newY-rows()-1 ) ;
01001             }
01002             moveItem->appendMoveItem( newLast );
01003             newLast->show();
01004             lastItem = newLast;
01005           } else {
01006             moveItem->expandBottom( deltapos.y() );
01007           }
01008           changed=true;
01009         }
01010         if (changed) {
01011           adjustItemPosition( moveItem );
01012         }
01013         moveItem = moveItem->nextMultiItem();
01014       }
01015     } else if (mActionType == RESIZETOP) {
01016       if (mEndCell.y() <= mActionItem->cellYBottom()) {
01017         mActionItem->expandTop(gpos.y() - mEndCell.y());
01018         adjustItemPosition( mActionItem );
01019       }
01020     } else if (mActionType == RESIZEBOTTOM) {
01021       if (mEndCell.y() >= mActionItem->cellYTop()) {
01022         mActionItem->expandBottom(gpos.y() - mEndCell.y());
01023         adjustItemPosition( mActionItem );
01024       }
01025     } else if (mActionType == RESIZELEFT) {
01026       if (mEndCell.x() <= mActionItem->cellXRight()) {
01027         mActionItem->expandLeft( gpos.x() - mEndCell.x() );
01028         adjustItemPosition( mActionItem );
01029       }
01030     } else if (mActionType == RESIZERIGHT) {
01031       if (mEndCell.x() >= mActionItem->cellXLeft()) {
01032         mActionItem->expandRight(gpos.x() - mEndCell.x());
01033         adjustItemPosition( mActionItem );
01034       }
01035     }
01036     mEndCell = gpos;
01037   }
01038 }
01039 
01040 void KOAgenda::endItemAction()
01041 {
01042 //  kdDebug(5850) << "KOAgenda::endItemAction() " << endl;
01043   mActionType = NOP;
01044   mScrollUpTimer.stop();
01045   mScrollDownTimer.stop();
01046   setCursor( arrowCursor );
01047   bool multiModify = false;
01048   // FIXME: do the cloning here...
01049   Incidence* inc = mActionItem->incidence();
01050 
01051   if ( mItemMoved ) {
01052     bool modify = true;
01053     if ( mActionItem->incidence()->doesRecur() ) {
01054       int res = KOMessageBox::fourBtnMsgBox( this, QMessageBox::Question,
01055           i18n("The item you try to change is a recurring item. Shall the changes "
01056                "be applied only to this single occurrence, only to the future items, "
01057                "or to all items in the recurrence?"),
01058           i18n("Changing Recurring Item"),
01059           i18n("Only &This Item"), i18n("Only &Future Items"), i18n("&All Occurrences") );
01060       switch ( res ) {
01061         case KMessageBox::Ok: // All occurrences
01062             // Moving the whole sequene of events is handled by the itemModified below.
01063             modify = true;
01064             break;
01065         case KMessageBox::Yes: { // Just this occurrence
01066             // Dissociate this occurrence:
01067             // create clone of event, set relation to old event, set cloned event
01068             // for mActionItem, add exception date to old event, changeIncidence
01069             // for the old event, remove the recurrence from the new copy and then just
01070             // go on with the newly adjusted mActionItem and let the usual code take
01071             // care of the new time!
01072             modify = true;
01073             multiModify = true;
01074             emit startMultiModify( i18n("Dissociate event from recurrence") );
01075             Incidence* oldInc = mActionItem->incidence();
01076             Incidence* oldIncSaved = mActionItem->incidence()->clone();
01077             Incidence* newInc = mCalendar->dissociateOccurrence(
01078                 oldInc, mActionItem->itemDate() );
01079             if ( newInc ) {
01080               // don't recreate items, they already have the correct position
01081               emit enableAgendaUpdate( false );
01082               mActionItem->dissociateFromMultiItem();
01083               mActionItem->setIncidence( newInc );
01084               mChanger->addIncidence( newInc, this );
01085               emit enableAgendaUpdate( true );
01086               mChanger->changeIncidence( oldIncSaved, oldInc );
01087             } else {
01088               KMessageBox::sorry( this, i18n("Unable to add the exception item to the "
01089                   "calendar. No change will be done."), i18n("Error Occurred") );
01090             }
01091             delete oldIncSaved;
01092             break; }
01093         case KMessageBox::No/*Future*/: { // All future occurrences
01094             // Dissociate this occurrence:
01095             // create clone of event, set relation to old event, set cloned event
01096             // for mActionItem, add recurrence end date to old event, changeIncidence
01097             // for the old event, adjust the recurrence for the new copy and then just
01098             // go on with the newly adjusted mActionItem and let the usual code take
01099             // care of the new time!
01100             modify = true;
01101             multiModify = true;
01102             emit startMultiModify( i18n("Split future recurrences") );
01103             Incidence* oldInc = mActionItem->incidence();
01104             Incidence* oldIncSaved = mActionItem->incidence()->clone();
01105             Incidence* newInc = mCalendar->dissociateOccurrence(
01106                 oldInc, mActionItem->itemDate(), false );
01107             if ( newInc ) {
01108               emit enableAgendaUpdate( false );
01109               mActionItem->dissociateFromMultiItem();
01110               mActionItem->setIncidence( newInc );
01111               mChanger->addIncidence( newInc, this );
01112               emit enableAgendaUpdate( true );
01113               mChanger->changeIncidence( oldIncSaved, oldInc );
01114             } else {
01115               KMessageBox::sorry( this, i18n("Unable to add the future items to the "
01116                   "calendar. No change will be done."), i18n("Error Occurred") );
01117             }
01118             delete oldIncSaved;
01119             break; }
01120         default:
01121           modify = false;
01122           mActionItem->resetMove();
01123           placeSubCells( mActionItem );
01124       }
01125     }
01126 
01127     if ( modify ) {
01128       mActionItem->endMove();
01129       KOAgendaItem *placeItem = mActionItem->firstMultiItem();
01130       if  ( !placeItem ) {
01131         placeItem = mActionItem;
01132       }
01133 
01134       KOAgendaItem *modif = placeItem;
01135 
01136       QPtrList<KOAgendaItem> oldconflictItems = placeItem->conflictItems();
01137       KOAgendaItem *item;
01138       for ( item = oldconflictItems.first(); item != 0;
01139             item = oldconflictItems.next() ) {
01140         placeSubCells( item );
01141       }
01142       while ( placeItem ) {
01143         placeSubCells( placeItem );
01144         placeItem = placeItem->nextMultiItem();
01145       }
01146 
01147       // Notify about change
01148       // the agenda view will apply the changes to the actual Incidence*!
01149       emit itemModified( modif );
01150     }
01151     // FIXME: If the change failed, we need to update the view!
01152     mChanger->endChange( inc );
01153   }
01154 
01155   mActionItem = 0;
01156   mItemMoved = false;
01157 
01158   if ( multiModify ) emit endMultiModify();
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   KOAgendaItem *item;
01805   if (mAllDayMode) {
01806     for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01807       subCellWidth = calcSubCellWidth( item );
01808       placeAgendaItem( item, subCellWidth );
01809     }
01810   } else {
01811     for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01812       subCellWidth = calcSubCellWidth( item );
01813       placeAgendaItem( item, subCellWidth );
01814     }
01815   }
01816   checkScrollBoundaries();
01817   marcus_bains();
01818 }
01819 
01820 
01821 void KOAgenda::scrollUp()
01822 {
01823   scrollBy(0,-mScrollOffset);
01824 }
01825 
01826 
01827 void KOAgenda::scrollDown()
01828 {
01829   scrollBy(0,mScrollOffset);
01830 }
01831 
01832 
01833 /*
01834   Calculates the minimum width
01835 */
01836 int KOAgenda::minimumWidth() const
01837 {
01838   // FIXME:: develop a way to dynamically determine the minimum width
01839   int min = 100;
01840 
01841   return min;
01842 }
01843 
01844 void KOAgenda::updateConfig()
01845 {
01846   double oldGridSpacingY = mGridSpacingY;
01847   mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
01848  // make sure that there are not more than 24 per day
01849   mGridSpacingY = (double)height()/(double)mRows;
01850   if (mGridSpacingY<mDesiredGridSpacingY) mGridSpacingY=mDesiredGridSpacingY;
01851 
01852   //can be two doubles equal?, it's better to compare them with an epsilon
01853   if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 )
01854     resizeContents( int( mGridSpacingX * mColumns ),
01855                   int( mGridSpacingY * mRows ) );
01856 
01857   calculateWorkingHours();
01858 
01859   marcus_bains();
01860 }
01861 
01862 void KOAgenda::checkScrollBoundaries()
01863 {
01864   // Invalidate old values to force update
01865   mOldLowerScrollValue = -1;
01866   mOldUpperScrollValue = -1;
01867 
01868   checkScrollBoundaries(verticalScrollBar()->value());
01869 }
01870 
01871 void KOAgenda::checkScrollBoundaries( int v )
01872 {
01873   int yMin = int( (v) / mGridSpacingY );
01874   int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
01875 
01876 //  kdDebug(5850) << "--- yMin: " << yMin << "  yMax: " << yMax << endl;
01877 
01878   if ( yMin != mOldLowerScrollValue ) {
01879     mOldLowerScrollValue = yMin;
01880     emit lowerYChanged(yMin);
01881   }
01882   if ( yMax != mOldUpperScrollValue ) {
01883     mOldUpperScrollValue = yMax;
01884     emit upperYChanged(yMax);
01885   }
01886 }
01887 
01888 int KOAgenda::visibleContentsYMin()
01889 {
01890   int v = verticalScrollBar()->value();
01891   return int( v / mGridSpacingY );
01892 }
01893 
01894 int KOAgenda::visibleContentsYMax()
01895 {
01896   int v = verticalScrollBar()->value();
01897   return int( ( v + visibleHeight() ) / mGridSpacingY );
01898 }
01899 
01900 void KOAgenda::deselectItem()
01901 {
01902   if (mSelectedItem.isNull()) return;
01903   mSelectedItem->select(false);
01904   mSelectedItem = 0;
01905 }
01906 
01907 void KOAgenda::selectItem(KOAgendaItem *item)
01908 {
01909   if ((KOAgendaItem *)mSelectedItem == item) return;
01910   deselectItem();
01911   if (item == 0) {
01912     emit incidenceSelected( 0, QDate() );
01913     return;
01914   }
01915   mSelectedItem = item;
01916   mSelectedItem->select();
01917   assert( mSelectedItem->incidence() );
01918   mSelectedUid = mSelectedItem->incidence()->uid();
01919   emit incidenceSelected( mSelectedItem->incidence(), mSelectedItem->itemDate() );
01920 }
01921 
01922 void KOAgenda::selectItemByUID( const QString& uid )
01923 {
01924   KOAgendaItem *item;
01925   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01926     if( item->incidence() && item->incidence()->uid() == uid ) {
01927       selectItem( item );
01928       break;
01929     }
01930   }
01931 }
01932 
01933 // This function seems never be called.
01934 void KOAgenda::keyPressEvent( QKeyEvent *kev )
01935 {
01936   switch(kev->key()) {
01937     case Key_PageDown:
01938       verticalScrollBar()->addPage();
01939       break;
01940     case Key_PageUp:
01941       verticalScrollBar()->subtractPage();
01942       break;
01943     case Key_Down:
01944       verticalScrollBar()->addLine();
01945       break;
01946     case Key_Up:
01947       verticalScrollBar()->subtractLine();
01948       break;
01949     default:
01950       ;
01951   }
01952 }
01953 
01954 void KOAgenda::calculateWorkingHours()
01955 {
01956   mWorkingHoursEnable = !mAllDayMode;
01957 
01958   QTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
01959   mWorkingHoursYTop = int( 4 * mGridSpacingY *
01960                            ( tmp.hour() + tmp.minute() / 60. +
01961                              tmp.second() / 3600. ) );
01962   tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
01963   mWorkingHoursYBottom = int( 4 * mGridSpacingY *
01964                               ( tmp.hour() + tmp.minute() / 60. +
01965                                 tmp.second() / 3600. ) - 1 );
01966 }
01967 
01968 
01969 DateList KOAgenda::dateList() const
01970 {
01971     return mSelectedDates;
01972 }
01973 
01974 void KOAgenda::setDateList(const DateList &selectedDates)
01975 {
01976     mSelectedDates = selectedDates;
01977     marcus_bains();
01978 }
01979 
01980 void KOAgenda::setHolidayMask(QMemArray<bool> *mask)
01981 {
01982   mHolidayMask = mask;
01983 
01984 }
01985 
01986 void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
01987 {
01988   kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
01989   QScrollView::contentsMousePressEvent(event);
01990 }
01991 
01992 void KOAgenda::setTypeAheadReceiver( QObject *o )
01993 {
01994   mTypeAheadReceiver = o;
01995 }
01996 
01997 QObject *KOAgenda::typeAheadReceiver() const
01998 {
01999   return mTypeAheadReceiver;
02000 }
KDE Home | KDE Accessibility Home | Description of Access Keys