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   Incidence* inc = mActionItem->incidence();
01048 
01049   if ( mItemMoved ) {
01050     bool modify = true;
01051     if ( mActionItem->incidence()->doesRecur() ) {
01052       int res = KOMessageBox::fourBtnMsgBox( this, QMessageBox::Question,
01053           i18n("The item you try to change is a recurring item. Shall the changes "
01054                "be applied only to this single occurrence, only to the future items, "
01055                "or to all items in the recurrence?"),
01056           i18n("Changing Recurring Item"),
01057           i18n("Only &This Item"), i18n("Only &Future Items"), i18n("&All Occurrences") );
01058       switch ( res ) {
01059         case KMessageBox::Ok: // All occurrences
01060             // Moving the whole sequene of events is handled by the itemModified below.
01061             modify = true;
01062             break;
01063         case KMessageBox::Yes: { // Just this occurrence
01064             // Dissociate this occurrence:
01065             // create clone of event, set relation to old event, set cloned event
01066             // for mActionItem, add exception date to old event, changeIncidence
01067             // for the old event, remove the recurrence from the new copy and then just
01068             // go on with the newly adjusted mActionItem and let the usual code take
01069             // care of the new time!
01070             modify = true;
01071             multiModify = true;
01072             emit startMultiModify( i18n("Dissociate event from recurrence") );
01073             Incidence* oldInc = mActionItem->incidence();
01074             Incidence* oldIncSaved = mActionItem->incidence()->clone();
01075             Incidence* newInc = mCalendar->dissociateOccurrence(
01076                 oldInc, mActionItem->itemDate() );
01077             if ( newInc ) {
01078               // don't recreate items, they already have the correct position
01079               emit enableAgendaUpdate( false );
01080               mActionItem->dissociateFromMultiItem();
01081               mActionItem->setIncidence( newInc );
01082               mChanger->addIncidence( newInc, this );
01083               emit enableAgendaUpdate( true );
01084               mChanger->changeIncidence( oldIncSaved, oldInc );
01085             } else {
01086               KMessageBox::sorry( this, i18n("Unable to add the exception item to the "
01087                   "calendar. No change will be done."), i18n("Error Occurred") );
01088             }
01089             delete oldIncSaved;
01090             break; }
01091         case KMessageBox::No/*Future*/: { // All future occurrences
01092             // Dissociate this occurrence:
01093             // create clone of event, set relation to old event, set cloned event
01094             // for mActionItem, add recurrence end date to old event, changeIncidence
01095             // for the old event, adjust the recurrence for the new copy and then just
01096             // go on with the newly adjusted mActionItem and let the usual code take
01097             // care of the new time!
01098             modify = true;
01099             multiModify = true;
01100             emit startMultiModify( i18n("Split future recurrences") );
01101             Incidence* oldInc = mActionItem->incidence();
01102             Incidence* oldIncSaved = mActionItem->incidence()->clone();
01103             Incidence* newInc = mCalendar->dissociateOccurrence(
01104                 oldInc, mActionItem->itemDate(), false );
01105             if ( newInc ) {
01106               emit enableAgendaUpdate( false );
01107               mActionItem->dissociateFromMultiItem();
01108               mActionItem->setIncidence( newInc );
01109               mChanger->addIncidence( newInc, this );
01110               emit enableAgendaUpdate( true );
01111               mChanger->changeIncidence( oldIncSaved, oldInc );
01112             } else {
01113               KMessageBox::sorry( this, i18n("Unable to add the future items to the "
01114                   "calendar. No change will be done."), i18n("Error Occurred") );
01115             }
01116             delete oldIncSaved;
01117             break; }
01118         default:
01119           modify = false;
01120           mActionItem->resetMove();
01121           placeSubCells( mActionItem );
01122       }
01123     }
01124 
01125     if ( modify ) {
01126       mActionItem->endMove();
01127       KOAgendaItem *placeItem = mActionItem->firstMultiItem();
01128       if  ( !placeItem ) {
01129         placeItem = mActionItem;
01130       }
01131 
01132       KOAgendaItem *modif = placeItem;
01133 
01134       QPtrList<KOAgendaItem> oldconflictItems = placeItem->conflictItems();
01135       KOAgendaItem *item;
01136       for ( item = oldconflictItems.first(); item != 0;
01137             item = oldconflictItems.next() ) {
01138         placeSubCells( item );
01139       }
01140       while ( placeItem ) {
01141         placeSubCells( placeItem );
01142         placeItem = placeItem->nextMultiItem();
01143       }
01144 
01145       // Notify about change
01146       // the agenda view will apply the changes to the actual Incidence*!
01147       emit itemModified( modif );
01148     }
01149     // FIXME: If the change failed, we need to update the view!
01150     mChanger->endChange( inc );
01151   }
01152 
01153   mActionItem = 0;
01154   mItemMoved = false;
01155 
01156   if ( multiModify ) emit endMultiModify();
01157 
01158   kdDebug(5850) << "KOAgenda::endItemAction() done" << endl;
01159 }
01160 
01161 void KOAgenda::setActionCursor( int actionType, bool acting )
01162 {
01163   switch ( actionType ) {
01164     case MOVE:
01165       if (acting) setCursor( sizeAllCursor );
01166       else setCursor( arrowCursor );
01167       break;
01168     case RESIZETOP:
01169     case RESIZEBOTTOM:
01170       setCursor( sizeVerCursor );
01171       break;
01172     case RESIZELEFT:
01173     case RESIZERIGHT:
01174       setCursor( sizeHorCursor );
01175       break;
01176     default:
01177       setCursor( arrowCursor );
01178   }
01179 }
01180 
01181 void KOAgenda::setNoActionCursor( KOAgendaItem *moveItem, const QPoint& viewportPos )
01182 {
01183 //  kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
01184 //  QPoint point = viewport()->mapToGlobal(viewportPos);
01185 //  kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
01186 //  point = clipper()->mapFromGlobal(point);
01187 //  kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
01188 
01189   QPoint pos = viewportToContents( viewportPos );
01190   bool noResize = (moveItem && moveItem->incidence() &&
01191       moveItem->incidence()->type() == "Todo");
01192 
01193   KOAgenda::MouseActionType resizeType = MOVE;
01194   if ( !noResize ) resizeType = isInResizeArea( mAllDayMode, pos , moveItem);
01195   setActionCursor( resizeType );
01196 }
01197 
01198 
01201 double KOAgenda::calcSubCellWidth( KOAgendaItem *item )
01202 {
01203   QPoint pt, pt1;
01204   pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01205   pt1 = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) +
01206                         QPoint( 1, 1 ) );
01207   pt1 -= pt;
01208   int maxSubCells = item->subCells();
01209   double newSubCellWidth;
01210   if ( mAllDayMode ) {
01211     newSubCellWidth = double( pt1.y() ) / maxSubCells;
01212   } else {
01213     newSubCellWidth = double( pt1.x() ) / maxSubCells;
01214   }
01215   return newSubCellWidth;
01216 }
01217 
01218 void KOAgenda::adjustItemPosition( KOAgendaItem *item )
01219 {
01220   if (!item) return;
01221   item->resize( int( mGridSpacingX * item->cellWidth() ),
01222                 int( mGridSpacingY * item->cellHeight() ) );
01223   int clXLeft = item->cellXLeft();
01224   if ( KOGlobals::self()->reverseLayout() )
01225     clXLeft = item->cellXRight() + 1;
01226   QPoint cpos = gridToContents( QPoint( clXLeft, item->cellYTop() ) );
01227   moveChild( item, cpos.x(), cpos.y() );
01228 }
01229 
01230 void KOAgenda::placeAgendaItem( KOAgendaItem *item, double subCellWidth )
01231 {
01232 //  kdDebug(5850) << "KOAgenda::placeAgendaItem(): " << item->incidence()->summary()
01233 //            << " subCellWidth: " << subCellWidth << endl;
01234 
01235   // "left" upper corner, no subcells yet, RTL layouts have right/left switched, widths are negative then
01236   QPoint pt = gridToContents( QPoint( item->cellXLeft(), item->cellYTop() ) );
01237   // right lower corner
01238   QPoint pt1 = gridToContents( QPoint( item->cellXLeft() + item->cellWidth(),
01239       item->cellYBottom()+1 ) );
01240 
01241   double subCellPos = item->subCell() * subCellWidth;
01242 
01243   // we need to add 0.01 to make sure we don't loose one pixed due to
01244   // numerics (i.e. if it would be x.9998, we want the integer, not rounded down.
01245   double delta=0.01;
01246   if (subCellWidth<0) delta=-delta;
01247   int height, width, xpos, ypos;
01248   if (mAllDayMode) {
01249     width = pt1.x()-pt.x();
01250     height = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01251     xpos = pt.x();
01252     ypos = pt.y() + int( subCellPos );
01253   } else {
01254     width = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
01255     height = pt1.y()-pt.y();
01256     xpos = pt.x() + int( subCellPos );
01257     ypos = pt.y();
01258   }
01259   if ( KOGlobals::self()->reverseLayout() ) { // RTL language/layout
01260     xpos += width;
01261     width = -width;
01262   }
01263   if ( height<0 ) { // BTT (bottom-to-top) layout ?!?
01264     ypos += height;
01265     height = -height;
01266   }
01267   item->resize( width, height );
01268   moveChild( item, xpos, ypos );
01269 }
01270 
01271 /*
01272   Place item in cell and take care that multiple items using the same cell do
01273   not overlap. This method is not yet optimal. It doesn't use the maximum space
01274   it can get in all cases.
01275   At the moment the method has a bug: When an item is placed only the sub cell
01276   widths of the items are changed, which are within the Y region the item to
01277   place spans. When the sub cell width change of one of this items affects a
01278   cell, where other items are, which do not overlap in Y with the item to place,
01279   the display gets corrupted, although the corruption looks quite nice.
01280 */
01281 void KOAgenda::placeSubCells( KOAgendaItem *placeItem )
01282 {
01283 #if 0
01284   kdDebug(5850) << "KOAgenda::placeSubCells()" << endl;
01285   if ( placeItem ) {
01286     Incidence *event = placeItem->incidence();
01287     if ( !event ) {
01288       kdDebug(5850) << "  event is 0" << endl;
01289     } else {
01290       kdDebug(5850) << "  event: " << event->summary() << endl;
01291     }
01292   } else {
01293     kdDebug(5850) << "  placeItem is 0" << endl;
01294   }
01295   kdDebug(5850) << "KOAgenda::placeSubCells()..." << endl;
01296 #endif
01297 
01298   QPtrList<KOrg::CellItem> cells;
01299   KOAgendaItem *item;
01300   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01301     cells.append( item );
01302   }
01303 
01304   QPtrList<KOrg::CellItem> items = KOrg::CellItem::placeItem( cells,
01305                                                               placeItem );
01306 
01307   placeItem->setConflictItems( QPtrList<KOAgendaItem>() );
01308   double newSubCellWidth = calcSubCellWidth( placeItem );
01309   KOrg::CellItem *i;
01310   for ( i = items.first(); i; i = items.next() ) {
01311     item = static_cast<KOAgendaItem *>( i );
01312     placeAgendaItem( item, newSubCellWidth );
01313     item->addConflictItem( placeItem );
01314     placeItem->addConflictItem( item );
01315   }
01316   if ( items.isEmpty() ) {
01317     placeAgendaItem( placeItem, newSubCellWidth );
01318   }
01319   placeItem->update();
01320 }
01321 
01322 int KOAgenda::columnWidth( int column )
01323 {
01324   int start = gridToContents( QPoint( column, 0 ) ).x();
01325   if (KOGlobals::self()->reverseLayout() )
01326     column--;
01327   else
01328     column++;
01329   int end = gridToContents( QPoint( column, 0 ) ).x();
01330   return end - start;
01331 }
01332 /*
01333   Draw grid in the background of the agenda.
01334 */
01335 void KOAgenda::drawContents(QPainter* p, int cx, int cy, int cw, int ch)
01336 {
01337   QPixmap db(cw, ch);
01338   db.fill(KOPrefs::instance()->mAgendaBgColor);
01339   QPainter dbp(&db);
01340   dbp.translate(-cx,-cy);
01341 
01342 //  kdDebug(5850) << "KOAgenda::drawContents()" << endl;
01343   double lGridSpacingY = mGridSpacingY*2;
01344 
01345   // Highlight working hours
01346   if (mWorkingHoursEnable) {
01347     QPoint pt1( cx, mWorkingHoursYTop );
01348     QPoint pt2( cx+cw, mWorkingHoursYBottom );
01349     if ( pt2.x() >= pt1.x() /*&& pt2.y() >= pt1.y()*/) {
01350       int gxStart = contentsToGrid( pt1 ).x();
01351       int gxEnd = contentsToGrid( pt2 ).x();
01352       // correct start/end for rtl layouts
01353       if ( gxStart > gxEnd ) {
01354         int tmp = gxStart;
01355         gxStart = gxEnd;
01356         gxEnd = tmp;
01357       }
01358       int xoffset = ( KOGlobals::self()->reverseLayout()?1:0 );
01359       while( gxStart <= gxEnd ) {
01360         int xStart = gridToContents( QPoint( gxStart+xoffset, 0 ) ).x();
01361         int xWidth = columnWidth( gxStart ) + 1;
01362         if ( pt2.y() < pt1.y() ) {
01363           // overnight working hours
01364           if ( ( (gxStart==0) && !mHolidayMask->at(mHolidayMask->count()-1) ) ||
01365                ( (gxStart>0) && (gxStart<int(mHolidayMask->count())) && (!mHolidayMask->at(gxStart-1) ) ) ) {
01366             if ( pt2.y() > cy ) {
01367               dbp.fillRect( xStart, cy, xWidth, pt2.y() - cy + 1,
01368                             KOPrefs::instance()->mWorkingHoursColor);
01369             }
01370           }
01371           if ( (gxStart < int(mHolidayMask->count()-1)) && (!mHolidayMask->at(gxStart)) ) {
01372             if ( pt1.y() < cy + ch - 1 ) {
01373               dbp.fillRect( xStart, pt1.y(), xWidth, cy + ch - pt1.y() + 1,
01374                             KOPrefs::instance()->mWorkingHoursColor);
01375             }
01376           }
01377         } else {
01378           // last entry in holiday mask denotes the previous day not visible (needed for overnight shifts)
01379           if ( gxStart < int(mHolidayMask->count()-1) && !mHolidayMask->at(gxStart)) {
01380             dbp.fillRect( xStart, pt1.y(), xWidth, pt2.y() - pt1.y() + 1,
01381                           KOPrefs::instance()->mWorkingHoursColor );
01382           }
01383         }
01384         ++gxStart;
01385       }
01386     }
01387   }
01388 
01389   // draw selection
01390   if ( mHasSelection ) {
01391     QPoint pt, pt1;
01392 
01393     if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
01394       // draw start day
01395       pt = gridToContents( mSelectionStartCell );
01396       pt1 = gridToContents( QPoint( mSelectionStartCell.x() + 1, mRows + 1 ) );
01397       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01398       // draw all other days between the start day and the day of the selection end
01399       for ( int c = mSelectionStartCell.x() + 1; c < mSelectionEndCell.x(); ++c ) {
01400         pt = gridToContents( QPoint( c, 0 ) );
01401         pt1 = gridToContents( QPoint( c + 1, mRows + 1 ) );
01402         dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01403       }
01404       // draw end day
01405       pt = gridToContents( QPoint( mSelectionEndCell.x(), 0 ) );
01406       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01407       dbp.fillRect( QRect( pt, pt1), KOPrefs::instance()->mHighlightColor );
01408     }  else { // single day selection
01409       pt = gridToContents( mSelectionStartCell );
01410       pt1 = gridToContents( mSelectionEndCell + QPoint(1,1) );
01411       dbp.fillRect( QRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
01412     }
01413   }
01414 
01415   QPen hourPen( KOPrefs::instance()->mAgendaBgColor.dark( 150 ) );
01416   QPen halfHourPen( KOPrefs::instance()->mAgendaBgColor.dark( 125 ) );
01417   dbp.setPen( hourPen );
01418 
01419   // Draw vertical lines of grid, start with the last line not yet visible
01420   //  kdDebug(5850) << "drawContents cx: " << cx << " cy: " << cy << " cw: " << cw << " ch: " << ch << endl;
01421   double x = ( int( cx / mGridSpacingX ) ) * mGridSpacingX;
01422   while (x < cx + cw) {
01423     dbp.drawLine( int( x ), cy, int( x ), cy + ch );
01424     x+=mGridSpacingX;
01425   }
01426 
01427   // Draw horizontal lines of grid
01428   double y = ( int( cy / (2*lGridSpacingY) ) ) * 2 * lGridSpacingY;
01429   while (y < cy + ch) {
01430 //    kdDebug(5850) << " y: " << y << endl;
01431     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01432     y += 2 * lGridSpacingY;
01433   }
01434   y = ( 2 * int( cy / (2*lGridSpacingY) ) + 1) * lGridSpacingY;
01435   dbp.setPen( halfHourPen );
01436   while (y < cy + ch) {
01437 //    kdDebug(5850) << " y: " << y << endl;
01438     dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
01439     y+=2*lGridSpacingY;
01440   }
01441   p->drawPixmap(cx,cy, db);
01442 }
01443 
01444 /*
01445   Convert srcollview contents coordinates to agenda grid coordinates.
01446 */
01447 QPoint KOAgenda::contentsToGrid ( const QPoint &pos ) const
01448 {
01449   int gx = int( KOGlobals::self()->reverseLayout() ?
01450         mColumns - pos.x()/mGridSpacingX : pos.x()/mGridSpacingX );
01451   int gy = int( pos.y()/mGridSpacingY );
01452   return QPoint( gx, gy );
01453 }
01454 
01455 /*
01456   Convert agenda grid coordinates to scrollview contents coordinates.
01457 */
01458 QPoint KOAgenda::gridToContents( const QPoint &gpos ) const
01459 {
01460   int x = int( KOGlobals::self()->reverseLayout() ?
01461              (mColumns - gpos.x())*mGridSpacingX : gpos.x()*mGridSpacingX );
01462   int y = int( gpos.y()*mGridSpacingY );
01463   return QPoint( x, y );
01464 }
01465 
01466 
01467 /*
01468   Return Y coordinate corresponding to time. Coordinates are rounded to fit into
01469   the grid.
01470 */
01471 int KOAgenda::timeToY(const QTime &time)
01472 {
01473 //  kdDebug(5850) << "Time: " << time.toString() << endl;
01474   int minutesPerCell = 24 * 60 / mRows;
01475 //  kdDebug(5850) << "minutesPerCell: " << minutesPerCell << endl;
01476   int timeMinutes = time.hour() * 60 + time.minute();
01477 //  kdDebug(5850) << "timeMinutes: " << timeMinutes << endl;
01478   int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
01479 //  kdDebug(5850) << "y: " << Y << endl;
01480 //  kdDebug(5850) << "\n" << endl;
01481   return Y;
01482 }
01483 
01484 
01485 /*
01486   Return time corresponding to cell y coordinate. Coordinates are rounded to
01487   fit into the grid.
01488 */
01489 QTime KOAgenda::gyToTime(int gy)
01490 {
01491 //  kdDebug(5850) << "gyToTime: " << gy << endl;
01492   int secondsPerCell = 24 * 60 * 60/ mRows;
01493 
01494   int timeSeconds = secondsPerCell * gy;
01495 
01496   QTime time( 0, 0, 0 );
01497   if ( timeSeconds < 24 * 60 * 60 ) {
01498     time = time.addSecs(timeSeconds);
01499   } else {
01500     time.setHMS( 23, 59, 59 );
01501   }
01502 //  kdDebug(5850) << "  gyToTime: " << time.toString() << endl;
01503 
01504   return time;
01505 }
01506 
01507 QMemArray<int> KOAgenda::minContentsY()
01508 {
01509   QMemArray<int> minArray;
01510   minArray.fill( timeToY( QTime(23, 59) ), mSelectedDates.count() );
01511   for ( KOAgendaItem *item = mItems.first();
01512         item != 0; item = mItems.next() ) {
01513     int ymin = item->cellYTop();
01514     int index = item->cellXLeft();
01515     if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
01516       if ( ymin < minArray[index] && mItemsToDelete.findRef( item ) == -1 )
01517         minArray[index] = ymin;
01518     }
01519   }
01520 
01521   return minArray;
01522 }
01523 
01524 QMemArray<int> KOAgenda::maxContentsY()
01525 {
01526   QMemArray<int> maxArray;
01527   maxArray.fill( timeToY( QTime(0, 0) ), mSelectedDates.count() );
01528   for ( KOAgendaItem *item = mItems.first();
01529         item != 0; item = mItems.next() ) {
01530     int ymax = item->cellYBottom();
01531     int index = item->cellXLeft();
01532     if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
01533       if ( ymax > maxArray[index] && mItemsToDelete.findRef( item ) == -1 )
01534         maxArray[index] = ymax;
01535     }
01536   }
01537 
01538   return maxArray;
01539 }
01540 
01541 void KOAgenda::setStartTime( const QTime &startHour )
01542 {
01543   double startPos = ( startHour.hour()/24. + startHour.minute()/1440. +
01544                       startHour.second()/86400. ) * mRows * gridSpacingY();
01545   setContentsPos( 0, int( startPos ) );
01546 }
01547 
01548 
01549 /*
01550   Insert KOAgendaItem into agenda.
01551 */
01552 KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const QDate &qd, int X,
01553                                     int YTop, int YBottom )
01554 {
01555 #if 0
01556   kdDebug(5850) << "KOAgenda::insertItem:" << incidence->summary() << "-"
01557                 << qd.toString() << " ;top, bottom:" << YTop << "," << YBottom
01558                 << endl;
01559 #endif
01560 
01561   if ( mAllDayMode ) {
01562     kdDebug(5850) << "KOAgenda: calling insertItem in all-day mode is illegal." << endl;
01563     return 0;
01564   }
01565 
01566 
01567   mActionType = NOP;
01568 
01569   KOAgendaItem *agendaItem = new KOAgendaItem( incidence, qd, viewport() );
01570   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem * ) ),
01571            SLOT( removeAgendaItem( KOAgendaItem * ) ) );
01572   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem * ) ),
01573            SLOT( showAgendaItem( KOAgendaItem * ) ) );
01574 
01575   if ( YBottom <= YTop ) {
01576     kdDebug(5850) << "KOAgenda::insertItem(): Text: " << agendaItem->text() << " YSize<0" << endl;
01577     YBottom = YTop;
01578   }
01579 
01580   agendaItem->resize( int( ( X + 1 ) * mGridSpacingX ) -
01581                       int( X * mGridSpacingX ),
01582                       int( YTop * mGridSpacingY ) -
01583                       int( ( YBottom + 1 ) * mGridSpacingY ) );
01584   agendaItem->setCellXY( X, YTop, YBottom );
01585   agendaItem->setCellXRight( X );
01586   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, incidence ) );
01587   agendaItem->installEventFilter( this );
01588 
01589   addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
01590   mItems.append( agendaItem );
01591 
01592   placeSubCells( agendaItem );
01593 
01594   agendaItem->show();
01595 
01596   marcus_bains();
01597 
01598   return agendaItem;
01599 }
01600 
01601 /*
01602   Insert all-day KOAgendaItem into agenda.
01603 */
01604 KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const QDate &qd,
01605                                           int XBegin, int XEnd )
01606 {
01607   if ( !mAllDayMode ) {
01608     kdDebug(5850) << "KOAgenda: calling insertAllDayItem in non all-day mode is illegal." << endl;
01609     return 0;
01610   }
01611 
01612   mActionType = NOP;
01613 
01614   KOAgendaItem *agendaItem = new KOAgendaItem( event, qd, viewport() );
01615   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem* ) ),
01616            SLOT( removeAgendaItem( KOAgendaItem* ) ) );
01617   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem* ) ),
01618            SLOT( showAgendaItem( KOAgendaItem* ) ) );
01619 
01620   agendaItem->setCellXY( XBegin, 0, 0 );
01621   agendaItem->setCellXRight( XEnd );
01622 
01623   double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
01624   double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
01625                                    agendaItem->cellXLeft() );
01626 
01627   agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
01628 
01629   agendaItem->installEventFilter( this );
01630   agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
01631   addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
01632   mItems.append( agendaItem );
01633 
01634   placeSubCells( agendaItem );
01635 
01636   agendaItem->show();
01637 
01638   return agendaItem;
01639 }
01640 
01641 
01642 void KOAgenda::insertMultiItem (Event *event,const QDate &qd,int XBegin,int XEnd,
01643                                 int YTop,int YBottom)
01644 {
01645   if (mAllDayMode) {
01646     kdDebug(5850) << "KOAgenda: calling insertMultiItem in all-day mode is illegal." << endl;
01647     return;
01648   }
01649   mActionType = NOP;
01650 
01651   int cellX,cellYTop,cellYBottom;
01652   QString newtext;
01653   int width = XEnd - XBegin + 1;
01654   int count = 0;
01655   KOAgendaItem *current = 0;
01656   QPtrList<KOAgendaItem> multiItems;
01657   int visibleCount = mSelectedDates.first().daysTo(mSelectedDates.last());
01658   for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
01659     ++count;
01660     //Only add the items that are visible.
01661     if( cellX >=0 && cellX <= visibleCount ) {
01662       if ( cellX == XBegin ) cellYTop = YTop;
01663       else cellYTop = 0;
01664       if ( cellX == XEnd ) cellYBottom = YBottom;
01665       else cellYBottom = rows() - 1;
01666       newtext = QString("(%1/%2): ").arg( count ).arg( width );
01667       newtext.append( event->summary() );
01668 
01669       current = insertItem( event, qd, cellX, cellYTop, cellYBottom );
01670       current->setText( newtext );
01671       multiItems.append( current );
01672     }
01673   }
01674 
01675   KOAgendaItem *next = 0;
01676   KOAgendaItem *prev = 0;
01677   KOAgendaItem *last = multiItems.last();
01678   KOAgendaItem *first = multiItems.first();
01679   KOAgendaItem *setFirst,*setLast;
01680   current = first;
01681   while (current) {
01682     next = multiItems.next();
01683     if (current == first) setFirst = 0;
01684     else setFirst = first;
01685     if (current == last) setLast = 0;
01686     else setLast = last;
01687 
01688     current->setMultiItem(setFirst, prev, next, setLast);
01689     prev=current;
01690     current = next;
01691   }
01692 
01693   marcus_bains();
01694 }
01695 
01696 void KOAgenda::removeIncidence( Incidence *incidence )
01697 {
01698   // First find all items to be deleted and store them
01699   // in its own list. Otherwise removeAgendaItem will reset
01700   // the current position and mess this up.
01701   QPtrList<KOAgendaItem> itemsToRemove;
01702 
01703   KOAgendaItem *item = mItems.first();
01704   while ( item ) {
01705     if ( item->incidence() == incidence ) {
01706       itemsToRemove.append( item );
01707     }
01708     item = mItems.next();
01709   }
01710   item = itemsToRemove.first();
01711   while ( item ) {
01712     removeAgendaItem( item );
01713     item = itemsToRemove.next();
01714   }
01715 }
01716 
01717 void KOAgenda::showAgendaItem( KOAgendaItem *agendaItem )
01718 {
01719   if ( !agendaItem ) return;
01720   agendaItem->hide();
01721   addChild( agendaItem );
01722   if ( !mItems.containsRef( agendaItem ) )
01723     mItems.append( agendaItem );
01724   placeSubCells( agendaItem );
01725 
01726   agendaItem->show();
01727 }
01728 
01729 bool KOAgenda::removeAgendaItem( KOAgendaItem *item )
01730 {
01731   // we found the item. Let's remove it and update the conflicts
01732   bool taken = false;
01733   KOAgendaItem *thisItem = item;
01734   QPtrList<KOAgendaItem> conflictItems = thisItem->conflictItems();
01735   removeChild( thisItem );
01736   int pos = mItems.find( thisItem );
01737   if ( pos>=0 ) {
01738     mItems.take( pos );
01739     taken = true;
01740   }
01741 
01742   KOAgendaItem *confitem;
01743   for ( confitem = conflictItems.first(); confitem != 0;
01744         confitem = conflictItems.next() ) {
01745     // the item itself is also in its own conflictItems list!
01746     if ( confitem != thisItem ) placeSubCells(confitem);
01747 
01748   }
01749   mItemsToDelete.append( thisItem );
01750   QTimer::singleShot( 0, this, SLOT( deleteItemsToDelete() ) );
01751   return taken;
01752 }
01753 
01754 void KOAgenda::deleteItemsToDelete()
01755 {
01756   mItemsToDelete.clear();
01757 }
01758 
01759 /*QSizePolicy KOAgenda::sizePolicy() const
01760 {
01761   // Thought this would make the all-day event agenda minimum size and the
01762   // normal agenda take the remaining space. But it doesnt work. The QSplitter
01763   // dont seem to think that an Expanding widget needs more space than a
01764   // Preferred one.
01765   // But it doesnt hurt, so it stays.
01766   if (mAllDayMode) {
01767     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
01768   } else {
01769     return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
01770   }
01771 }
01772 */
01773 
01774 /*
01775   Overridden from QScrollView to provide proper resizing of KOAgendaItems.
01776 */
01777 void KOAgenda::resizeEvent ( QResizeEvent *ev )
01778 {
01779 //  kdDebug(5850) << "KOAgenda::resizeEvent" << endl;
01780 
01781   QSize newSize( ev->size() );
01782   if (mAllDayMode) {
01783     mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
01784     mGridSpacingY = newSize.height() - 2 * frameWidth();
01785   } else {
01786     mGridSpacingX = double( newSize.width() - verticalScrollBar()->width() - 2 * frameWidth()) / double(mColumns);
01787     // make sure that there are not more than 24 per day
01788     mGridSpacingY = double(newSize.height() - 2 * frameWidth()) / double(mRows);
01789     if ( mGridSpacingY < mDesiredGridSpacingY )
01790       mGridSpacingY = mDesiredGridSpacingY;
01791   }
01792   calculateWorkingHours();
01793   QTimer::singleShot( 0, this, SLOT( resizeAllContents() ) );
01794   emit gridSpacingYChanged( mGridSpacingY * 4 );
01795   QScrollView::resizeEvent(ev);
01796 }
01797 
01798 void KOAgenda::resizeAllContents()
01799 {
01800   double subCellWidth;
01801   KOAgendaItem *item;
01802   if (mAllDayMode) {
01803     for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01804       subCellWidth = calcSubCellWidth( item );
01805       placeAgendaItem( item, subCellWidth );
01806     }
01807   } else {
01808     for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01809       subCellWidth = calcSubCellWidth( item );
01810       placeAgendaItem( item, subCellWidth );
01811     }
01812   }
01813   checkScrollBoundaries();
01814   marcus_bains();
01815 }
01816 
01817 
01818 void KOAgenda::scrollUp()
01819 {
01820   scrollBy(0,-mScrollOffset);
01821 }
01822 
01823 
01824 void KOAgenda::scrollDown()
01825 {
01826   scrollBy(0,mScrollOffset);
01827 }
01828 
01829 
01830 /*
01831   Calculates the minimum width
01832 */
01833 int KOAgenda::minimumWidth() const
01834 {
01835   // FIXME:: develop a way to dynamically determine the minimum width
01836   int min = 100;
01837 
01838   return min;
01839 }
01840 
01841 void KOAgenda::updateConfig()
01842 {
01843   double oldGridSpacingY = mGridSpacingY;
01844   mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
01845  // make sure that there are not more than 24 per day
01846   mGridSpacingY = (double)height()/(double)mRows;
01847   if (mGridSpacingY<mDesiredGridSpacingY) mGridSpacingY=mDesiredGridSpacingY;
01848 
01849   //can be two doubles equal?, it's better to compare them with an epsilon
01850   if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 )
01851     resizeContents( int( mGridSpacingX * mColumns ),
01852                   int( mGridSpacingY * mRows ) );
01853 
01854   calculateWorkingHours();
01855 
01856   marcus_bains();
01857 }
01858 
01859 void KOAgenda::checkScrollBoundaries()
01860 {
01861   // Invalidate old values to force update
01862   mOldLowerScrollValue = -1;
01863   mOldUpperScrollValue = -1;
01864 
01865   checkScrollBoundaries(verticalScrollBar()->value());
01866 }
01867 
01868 void KOAgenda::checkScrollBoundaries( int v )
01869 {
01870   int yMin = int( (v) / mGridSpacingY );
01871   int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
01872 
01873 //  kdDebug(5850) << "--- yMin: " << yMin << "  yMax: " << yMax << endl;
01874 
01875   if ( yMin != mOldLowerScrollValue ) {
01876     mOldLowerScrollValue = yMin;
01877     emit lowerYChanged(yMin);
01878   }
01879   if ( yMax != mOldUpperScrollValue ) {
01880     mOldUpperScrollValue = yMax;
01881     emit upperYChanged(yMax);
01882   }
01883 }
01884 
01885 int KOAgenda::visibleContentsYMin()
01886 {
01887   int v = verticalScrollBar()->value();
01888   return int( v / mGridSpacingY );
01889 }
01890 
01891 int KOAgenda::visibleContentsYMax()
01892 {
01893   int v = verticalScrollBar()->value();
01894   return int( ( v + visibleHeight() ) / mGridSpacingY );
01895 }
01896 
01897 void KOAgenda::deselectItem()
01898 {
01899   if (mSelectedItem.isNull()) return;
01900   mSelectedItem->select(false);
01901   mSelectedItem = 0;
01902 }
01903 
01904 void KOAgenda::selectItem(KOAgendaItem *item)
01905 {
01906   if ((KOAgendaItem *)mSelectedItem == item) return;
01907   deselectItem();
01908   if (item == 0) {
01909     emit incidenceSelected( 0 );
01910     return;
01911   }
01912   mSelectedItem = item;
01913   mSelectedItem->select();
01914   assert( mSelectedItem->incidence() );
01915   mSelectedUid = mSelectedItem->incidence()->uid();
01916   emit incidenceSelected( mSelectedItem->incidence() );
01917 }
01918 
01919 void KOAgenda::selectItemByUID( const QString& uid )
01920 {
01921   KOAgendaItem *item;
01922   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01923     if( item->incidence() && item->incidence()->uid() == uid ) {
01924       selectItem( item );
01925       break;
01926     }
01927   }
01928 }
01929 
01930 // This function seems never be called.
01931 void KOAgenda::keyPressEvent( QKeyEvent *kev )
01932 {
01933   switch(kev->key()) {
01934     case Key_PageDown:
01935       verticalScrollBar()->addPage();
01936       break;
01937     case Key_PageUp:
01938       verticalScrollBar()->subtractPage();
01939       break;
01940     case Key_Down:
01941       verticalScrollBar()->addLine();
01942       break;
01943     case Key_Up:
01944       verticalScrollBar()->subtractLine();
01945       break;
01946     default:
01947       ;
01948   }
01949 }
01950 
01951 void KOAgenda::calculateWorkingHours()
01952 {
01953   mWorkingHoursEnable = !mAllDayMode;
01954 
01955   QTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
01956   mWorkingHoursYTop = int( 4 * mGridSpacingY *
01957                            ( tmp.hour() + tmp.minute() / 60. +
01958                              tmp.second() / 3600. ) );
01959   tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
01960   mWorkingHoursYBottom = int( 4 * mGridSpacingY *
01961                               ( tmp.hour() + tmp.minute() / 60. +
01962                                 tmp.second() / 3600. ) - 1 );
01963 }
01964 
01965 
01966 DateList KOAgenda::dateList() const
01967 {
01968     return mSelectedDates;
01969 }
01970 
01971 void KOAgenda::setDateList(const DateList &selectedDates)
01972 {
01973     mSelectedDates = selectedDates;
01974     marcus_bains();
01975 }
01976 
01977 void KOAgenda::setHolidayMask(QMemArray<bool> *mask)
01978 {
01979   mHolidayMask = mask;
01980 
01981 }
01982 
01983 void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
01984 {
01985   kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
01986   QScrollView::contentsMousePressEvent(event);
01987 }
01988 
01989 void KOAgenda::setTypeAheadReceiver( QObject *o )
01990 {
01991   mTypeAheadReceiver = o;
01992 }
01993 
01994 QObject *KOAgenda::typeAheadReceiver() const
01995 {
01996   return mTypeAheadReceiver;
01997 }
KDE Home | KDE Accessibility Home | Description of Access Keys