korganizer

koagenda.cpp

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