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