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