korganizer Library API Documentation

koagenda.cpp

00001 /*
00002     This file is part of KOrganizer.
00003     Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00004 
00005     Marcus Bains line.
00006     Copyright (c) 2001 Ali Rahimi
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00021 
00022     As a special exception, permission is given to link this program
00023     with any edition of Qt, and distribute the resulting executable,
00024     without including the source code for Qt in the source distribution.
00025 */
00026 
00027 #include <qintdict.h>
00028 #include <qdatetime.h>
00029 #include <qapplication.h>
00030 #include <qpopupmenu.h>
00031 #include <qcursor.h>
00032 #include <qpainter.h>
00033 
00034 #include <kdebug.h>
00035 #include <klocale.h>
00036 #include <kiconloader.h>
00037 #include <kglobal.h>
00038 
00039 #include "koagendaitem.h"
00040 #include "koprefs.h"
00041 #include "koglobals.h"
00042 
00043 #include "koagenda.h"
00044 #include "koagenda.moc"
00045 
00046 #include <libkcal/event.h>
00047 #include <libkcal/todo.h>
00048 #include <libkcal/dndfactory.h>
00049 #include <libkcal/icaldrag.h>
00050 #include <libkcal/vcaldrag.h>
00051 
00053 MarcusBains::MarcusBains(KOAgenda *_agenda,const char *name)
00054     : QFrame(_agenda->viewport(),name), agenda(_agenda)
00055 {
00056   setLineWidth(0);
00057   setMargin(0);
00058   setBackgroundColor(Qt::red);
00059   minutes = new QTimer(this);
00060   connect(minutes, SIGNAL(timeout()), this, SLOT(updateLocation()));
00061   minutes->start(0, true);
00062 
00063   mTimeBox = new QLabel(this);
00064   mTimeBox->setAlignment(Qt::AlignRight | Qt::AlignBottom);
00065   QPalette pal = mTimeBox->palette();
00066   pal.setColor(QColorGroup::Foreground, Qt::red);
00067   mTimeBox->setPalette(pal);
00068   mTimeBox->setAutoMask(true);
00069 
00070   agenda->addChild(mTimeBox);
00071 
00072   oldToday = -1;
00073 }
00074 
00075 MarcusBains::~MarcusBains()
00076 {
00077   delete minutes;
00078 }
00079 
00080 int MarcusBains::todayColumn()
00081 {
00082   QDate currentDate = QDate::currentDate();
00083 
00084   DateList dateList = agenda->dateList();
00085   DateList::ConstIterator it;
00086   int col = 0;
00087   for(it = dateList.begin(); it != dateList.end(); ++it) {
00088     if((*it) == currentDate)
00089       return KOGlobals::self()->reverseLayout() ?
00090              agenda->columns() - 1 - col : col;
00091       ++col;
00092   }
00093 
00094   return -1;
00095 }
00096 
00097 void MarcusBains::updateLocation(bool recalculate)
00098 {
00099   QTime tim = QTime::currentTime();
00100   if((tim.hour() == 0) && (oldTime.hour()==23))
00101     recalculate = true;
00102 
00103   int mins = tim.hour()*60 + tim.minute();
00104   int minutesPerCell = 24 * 60 / agenda->rows();
00105   int y = (int)(mins*agenda->gridSpacingY()/minutesPerCell);
00106   int today = recalculate ? todayColumn() : oldToday;
00107   int x = (int)( agenda->gridSpacingX()*today );
00108   bool disabled = !(KOPrefs::instance()->mMarcusBainsEnabled);
00109 
00110   oldTime = tim;
00111   oldToday = today;
00112 
00113   if(disabled || (today<0)) {
00114     hide();
00115     mTimeBox->hide();
00116     return;
00117   } else {
00118     show();
00119     mTimeBox->show();
00120   }
00121 
00122   if(recalculate)
00123     setFixedSize((int)(agenda->gridSpacingX()),1);
00124   agenda->moveChild(this, x, y);
00125   raise();
00126 
00127   if(recalculate)
00128     mTimeBox->setFont(KOPrefs::instance()->mMarcusBainsFont);
00129 
00130   mTimeBox->setText(KGlobal::locale()->formatTime(tim, KOPrefs::instance()->mMarcusBainsShowSeconds));
00131   mTimeBox->adjustSize();
00132   if (y-mTimeBox->height()>=0) y-=mTimeBox->height(); else y++;
00133   agenda->moveChild(mTimeBox,
00134                     (int)(x+agenda->gridSpacingX()-mTimeBox->width()-1),
00135                     y);
00136   mTimeBox->raise();
00137   mTimeBox->setAutoMask(true);
00138 
00139   minutes->start(1000,true);
00140 }
00141 
00142 
00144 
00145 
00146 /*
00147   Create an agenda widget with rows rows and columns columns.
00148 */
00149 KOAgenda::KOAgenda( int columns, int rows, int rowSize, QWidget *parent,
00150                     const char *name, WFlags f )
00151   : QScrollView( parent, name, f )
00152 {
00153   mColumns = columns;
00154   mRows = rows;
00155   mGridSpacingY = rowSize;
00156   mAllDayMode = false;
00157 
00158   init();
00159 }
00160 
00161 /*
00162   Create an agenda widget with columns columns and one row. This is used for
00163   all-day events.
00164 */
00165 KOAgenda::KOAgenda( int columns, QWidget *parent, const char *name, WFlags f )
00166   : QScrollView( parent, name, f )
00167 {
00168   mColumns = columns;
00169   mRows = 1;
00170   mGridSpacingY = 24;
00171   mAllDayMode = true;
00172 
00173   init();
00174 }
00175 
00176 
00177 KOAgenda::~KOAgenda()
00178 {
00179   delete mMarcusBains;
00180 }
00181 
00182 
00183 Incidence *KOAgenda::selectedIncidence() const
00184 {
00185   return ( mSelectedItem ? mSelectedItem->incidence() : 0 );
00186 }
00187 
00188 
00189 QDate KOAgenda::selectedIncidenceDate() const
00190 {
00191   return ( mSelectedItem ? mSelectedItem->itemDate() : QDate() );
00192 }
00193 
00194 
00195 void KOAgenda::init()
00196 {
00197   mGridSpacingX = 100;
00198 
00199   mResizeBorderWidth = 8;
00200   mScrollBorderWidth = 8;
00201   mScrollDelay = 30;
00202   mScrollOffset = 10;
00203 
00204   enableClipper( true );
00205 
00206   // Grab key strokes for keyboard navigation of agenda. Seems to have no
00207   // effect. Has to be fixed.
00208   setFocusPolicy( WheelFocus );
00209 
00210   connect( &mScrollUpTimer, SIGNAL( timeout() ), SLOT( scrollUp() ) );
00211   connect( &mScrollDownTimer, SIGNAL( timeout() ), SLOT( scrollDown() ) );
00212 
00213   mStartCellX = 0;
00214   mStartCellY = 0;
00215   mCurrentCellX = 0;
00216   mCurrentCellY = 0;
00217 
00218   mSelectionCellX = 0;
00219   mSelectionYTop = 0;
00220   mSelectionHeight = 0;
00221 
00222   mOldLowerScrollValue = -1;
00223   mOldUpperScrollValue = -1;
00224 
00225   mClickedItem = 0;
00226 
00227   mActionItem = 0;
00228   mActionType = NOP;
00229   mItemMoved = false;
00230 
00231   mSelectedItem = 0;
00232 
00233   setAcceptDrops( true );
00234   installEventFilter( this );
00235   mItems.setAutoDelete( true );
00236   mItemsToDelete.setAutoDelete( true );
00237 
00238 //  resizeContents( (int)(mGridSpacingX * mColumns + 1) , (int)(mGridSpacingY * mRows + 1) );
00239   resizeContents( int( mGridSpacingX * mColumns ),
00240                   int( mGridSpacingY * mRows ) );
00241 
00242   viewport()->update();
00243   viewport()->setBackgroundMode( NoBackground );
00244   viewport()->setFocusPolicy( WheelFocus );
00245 
00246   setMinimumSize( 30, int( mGridSpacingY + 1 ) );
00247 //  setMaximumHeight(mGridSpacingY * mRows + 5);
00248 
00249   // Disable horizontal scrollbar. This is a hack. The geometry should be
00250   // controlled in a way that the contents horizontally always fits. Then it is
00251   // not necessary to turn off the scrollbar.
00252   setHScrollBarMode( AlwaysOff );
00253 
00254   setStartHour( KOPrefs::instance()->mDayBegins );
00255 
00256   calculateWorkingHours();
00257 
00258   connect( verticalScrollBar(), SIGNAL( valueChanged( int ) ),
00259            SLOT( checkScrollBoundaries( int ) ) );
00260 
00261   // Create the Marcus Bains line.
00262   if( mAllDayMode ) {
00263     mMarcusBains = 0;
00264   } else {
00265     mMarcusBains = new MarcusBains( this );
00266     addChild( mMarcusBains );
00267   }
00268 
00269   mTypeAhead = false;
00270   mTypeAheadReceiver = 0;
00271 
00272   mReturnPressed = false;
00273 }
00274 
00275 
00276 void KOAgenda::clear()
00277 {
00278 //  kdDebug(5850) << "KOAgenda::clear()" << endl;
00279 
00280   KOAgendaItem *item;
00281   for ( item=mItems.first(); item != 0; item=mItems.next() ) {
00282     removeChild(item);
00283   }
00284   mItems.clear();
00285   mItemsToDelete.clear();
00286 
00287   mSelectedItem = 0;
00288 
00289   clearSelection();
00290 }
00291 
00292 
00293 void KOAgenda::clearSelection()
00294 {
00295   mSelectionCellX = 0;
00296   mSelectionYTop = 0;
00297   mSelectionHeight = 0;
00298 }
00299 
00300 void KOAgenda::marcus_bains()
00301 {
00302     if(mMarcusBains) mMarcusBains->updateLocation(true);
00303 }
00304 
00305 
00306 void KOAgenda::changeColumns(int columns)
00307 {
00308   if (columns == 0) {
00309     kdDebug(5850) << "KOAgenda::changeColumns() called with argument 0" << endl;
00310     return;
00311   }
00312 
00313   clear();
00314   mColumns = columns;
00315 //  setMinimumSize(mColumns * 10, mGridSpacingY + 1);
00316 //  init();
00317 //  update();
00318 
00319   QResizeEvent event( size(), size() );
00320 
00321   QApplication::sendEvent( this, &event );
00322 }
00323 
00324 /*
00325   This is the eventFilter function, which gets all events from the KOAgendaItems
00326   contained in the agenda. It has to handle moving and resizing for all items.
00327 */
00328 bool KOAgenda::eventFilter ( QObject *object, QEvent *event )
00329 {
00330 //  kdDebug(5850) << "KOAgenda::eventFilter() " << int( event->type() ) << endl;
00331 
00332   switch( event->type() ) {
00333     case QEvent::MouseButtonPress:
00334     case QEvent::MouseButtonDblClick:
00335     case QEvent::MouseButtonRelease:
00336     case QEvent::MouseMove:
00337       return eventFilter_mouse( object, static_cast<QMouseEvent *>( event ) );
00338 
00339     case QEvent::KeyPress:
00340     case QEvent::KeyRelease:
00341       return eventFilter_key( object, static_cast<QKeyEvent *>( event ) );
00342 
00343     case ( QEvent::Leave ):
00344       if ( !mActionItem )
00345         setCursor( arrowCursor );
00346       return true;
00347 
00348 #ifndef KORG_NODND
00349     case QEvent::DragEnter:
00350     case QEvent::DragMove:
00351     case QEvent::DragLeave:
00352     case QEvent::Drop:
00353  //   case QEvent::DragResponse:
00354       return eventFilter_drag(object, static_cast<QDropEvent*>(event));
00355 #endif
00356 
00357     default:
00358       return QScrollView::eventFilter( object, event );
00359   }
00360 }
00361 
00362 bool KOAgenda::eventFilter_drag( QObject *object, QDropEvent *de )
00363 {
00364 #ifndef KORG_NODND
00365   QPoint viewportPos;
00366   if ( object != viewport() && object != this ) {
00367     viewportPos = static_cast<QWidget *>( object )->mapToParent( de->pos() );
00368   } else {
00369     viewportPos = de->pos();
00370   }
00371 
00372   switch ( de->type() ) {
00373     case QEvent::DragEnter:
00374     case QEvent::DragMove:
00375       if ( ICalDrag::canDecode( de ) || VCalDrag::canDecode( de ) ) {
00376 
00377         DndFactory factory( mCalendar );
00378         Todo *todo = factory.createDropTodo( de );
00379         if ( todo ) {
00380           de->accept();
00381           delete todo;
00382         } else {
00383           de->ignore();
00384         }
00385         return true;
00386       } else return false;
00387       break;
00388     case QEvent::DragLeave:
00389       return false;
00390       break;
00391     case QEvent::Drop:
00392       {
00393         if ( !ICalDrag::canDecode( de ) && !VCalDrag::canDecode( de ) ) {
00394           return false;
00395         }
00396 
00397         DndFactory factory( mCalendar );
00398         Todo *todo = factory.createDropTodo( de );
00399 
00400         if ( todo ) {
00401           de->acceptAction();
00402           int x, y;
00403           // FIXME: This is a bad hack, as the viewportToContents seems to be off by
00404           // 2000 (which is the left upper corner of the viewport). It works correctly
00405           // for agendaItems.
00406           if ( object == this  ) {
00407             x = viewportPos.x() + contentsX();
00408             y = viewportPos.y() + contentsY();
00409           } else {
00410             viewportToContents( viewportPos.x(), viewportPos.y(), x, y );
00411           }
00412           int gx, gy;
00413           contentsToGrid( x, y, gx, gy );
00414           emit droppedToDo( todo, gx, gy, mAllDayMode );
00415           return true;
00416         }
00417       }
00418       break;
00419 
00420     case QEvent::DragResponse:
00421     default:
00422       break;
00423   }
00424 #endif
00425 
00426   return false;
00427 }
00428 
00429 bool KOAgenda::eventFilter_key( QObject *, QKeyEvent *ke )
00430 {
00431 //  kdDebug() << "KOAgenda::eventFilter_key() " << ke->type() << endl;
00432 
00433   // If Return is pressed bring up an editor for the current selected time span.
00434   if ( ke->key() == Key_Return ) {
00435     if ( ke->type() == QEvent::KeyPress ) mReturnPressed = true;
00436     else if ( ke->type() == QEvent::KeyRelease ) {
00437       if ( mReturnPressed ) {
00438         emitNewEventForSelection();
00439         mReturnPressed = false;
00440         return true;
00441       } else {
00442         mReturnPressed = false;
00443       }
00444     }
00445   }
00446 
00447   // Ignore all input that does not produce any output
00448   if ( ke->text().isEmpty() ) return false;
00449 
00450   if ( ke->type() == QEvent::KeyPress || ke->type() == QEvent::KeyRelease ) {
00451     switch ( ke->key() ) {
00452       case Key_Escape:
00453       case Key_Return:
00454       case Key_Enter:
00455       case Key_Tab:
00456       case Key_Backtab:
00457       case Key_Left:
00458       case Key_Right:
00459       case Key_Up:
00460       case Key_Down:
00461       case Key_Backspace:
00462       case Key_Delete:
00463       case Key_Prior:
00464       case Key_Next:
00465       case Key_Home:
00466       case Key_End:
00467       case Key_Control:
00468       case Key_Meta:
00469       case Key_Alt:
00470         break;
00471       default:
00472         mTypeAheadEvents.append( new QKeyEvent( ke->type(), ke->key(),
00473                                                 ke->ascii(), ke->state(),
00474                                                 ke->text(), ke->isAutoRepeat(),
00475                                                 ke->count() ) );
00476         if ( !mTypeAhead ) {
00477           mTypeAhead = true;
00478           emitNewEventForSelection();
00479           return true;
00480         }
00481         break;
00482     }
00483   }
00484   return false;
00485 }
00486 
00487 void KOAgenda::emitNewEventForSelection()
00488 {
00489   if ( mSelectionHeight > 0 ) {
00490     // Subtract 1 from the bottom, because the ybottom describes the last cell in
00491     // the event, not the one after the item
00492     emit newEventSignal( mSelectionCellX, (int)(mSelectionYTop / mGridSpacingY),
00493                          mSelectionCellX,
00494                          (int)( ( mSelectionYTop + mSelectionHeight ) /
00495                          mGridSpacingY ) -1 );
00496   } else {
00497     emit newEventSignal();
00498   }
00499 }
00500 
00501 void KOAgenda::finishTypeAhead()
00502 {
00503 //  kdDebug() << "KOAgenda::finishTypeAhead()" << endl;
00504   if ( typeAheadReceiver() ) {
00505     for( QEvent *e = mTypeAheadEvents.first(); e;
00506          e = mTypeAheadEvents.next() ) {
00507 //      kdDebug() << "postEvent() " << int( typeAheadReceiver() ) << endl;
00508       QApplication::postEvent( typeAheadReceiver(), e );
00509     }
00510   }
00511   mTypeAheadEvents.clear();
00512   mTypeAhead = false;
00513 }
00514 
00515 bool KOAgenda::eventFilter_mouse(QObject *object, QMouseEvent *me)
00516 {
00517   QPoint viewportPos;
00518   if (object != viewport()) {
00519     viewportPos = ((QWidget *)object)->mapToParent(me->pos());
00520   } else {
00521     viewportPos = me->pos();
00522   }
00523 
00524   switch (me->type())  {
00525     case QEvent::MouseButtonPress:
00526 //        kdDebug(5850) << "koagenda: filtered button press" << endl;
00527       if (object != viewport()) {
00528         if (me->button() == RightButton) {
00529           mClickedItem = (KOAgendaItem *)object;
00530           if (mClickedItem) {
00531             selectItem(mClickedItem);
00532             emit showIncidencePopupSignal(mClickedItem->incidence());
00533           }
00534     //            mItemPopup->popup(QCursor::pos());
00535         } else {
00536           mActionItem = (KOAgendaItem *)object;
00537           if (mActionItem) {
00538             selectItem(mActionItem);
00539             Incidence *incidence = mActionItem->incidence();
00540             if ( incidence->isReadOnly() || incidence->doesRecur() ) {
00541               mActionItem = 0;
00542             } else {
00543               startItemAction(viewportPos);
00544             }
00545           }
00546         }
00547       } else {
00548         if (me->button() == RightButton)
00549         {
00550           showNewEventPopupSignal();
00551         }
00552         else
00553         {
00554           selectItem(0);
00555           mActionItem = 0;
00556           setCursor(arrowCursor);
00557           startSelectAction(viewportPos);
00558         }
00559       }
00560       break;
00561 
00562     case QEvent::MouseButtonRelease:
00563       if (mActionItem) {
00564         endItemAction();
00565       } else if ( mActionType == SELECT ) {
00566         endSelectAction( viewportPos );
00567       }
00568       break;
00569 
00570     case QEvent::MouseMove:
00571       if (object != viewport()) {
00572         KOAgendaItem *moveItem = (KOAgendaItem *)object;
00573         if (!moveItem->incidence()->isReadOnly() &&
00574             !moveItem->incidence()->recurrence()->doesRecur() )
00575           if (!mActionItem)
00576             setNoActionCursor(moveItem,viewportPos);
00577           else
00578             performItemAction(viewportPos);
00579         } else {
00580           if ( mActionType == SELECT ) {
00581             performSelectAction( viewportPos );
00582           }
00583         }
00584       break;
00585 
00586     case QEvent::MouseButtonDblClick:
00587       if (object == viewport()) {
00588         selectItem(0);
00589         int x,y;
00590         viewportToContents(viewportPos.x(),viewportPos.y(),x,y);
00591         int gx,gy;
00592         contentsToGrid(x,y,gx,gy);
00593         emit newEventSignal(gx,gy);
00594       } else {
00595         KOAgendaItem *doubleClickedItem = (KOAgendaItem *)object;
00596         selectItem(doubleClickedItem);
00597         emit editIncidenceSignal(doubleClickedItem->incidence());
00598       }
00599       break;
00600 
00601     default:
00602       break;
00603   }
00604 
00605   return true;
00606 }
00607 
00608 void KOAgenda::startSelectAction( const QPoint &viewportPos )
00609 {
00610   emit newStartSelectSignal();
00611 
00612   mActionType = SELECT;
00613   mSelectionStartPoint = viewportPos;
00614 
00615   int x,y;
00616   viewportToContents( viewportPos.x(), viewportPos.y(), x, y );
00617   int gx,gy;
00618   contentsToGrid( x, y, gx, gy );
00619 
00620   mStartCellX = gx;
00621   mStartCellY = gy;
00622   mCurrentCellX = gx;
00623   mCurrentCellY = gy;
00624 
00625   // Store coordinates of old selection
00626   int selectionX = mSelectionCellX;
00627   int selectionYTop = mSelectionYTop;
00628   int selectionHeight = mSelectionHeight;
00629 
00630   // Store new selection
00631   mSelectionCellX = gx;
00632   mSelectionYTop = int( gy * mGridSpacingY );
00633   mSelectionHeight = int( mGridSpacingY );
00634 
00635   // Clear old selection
00636   repaintContents( int( selectionX*mGridSpacingX ), selectionYTop,
00637                    int( (selectionX+1)*mGridSpacingX ) - int( selectionX*mGridSpacingX ), selectionHeight );
00638 
00639   // Paint new selection
00640   repaintContents( int( mSelectionCellX * mGridSpacingX ), mSelectionYTop,
00641                    int( (mSelectionCellX+1)*mGridSpacingX ) - int( mSelectionCellX*mGridSpacingX ),
00642                    mSelectionHeight );
00643 
00644 }
00645 
00646 void KOAgenda::performSelectAction(const QPoint& viewportPos)
00647 {
00648   int x,y;
00649   viewportToContents(viewportPos.x(),viewportPos.y(),x,y);
00650   int gx,gy;
00651   contentsToGrid(x,y,gx,gy);
00652 
00653   QPoint clipperPos = clipper()->
00654                       mapFromGlobal(viewport()->mapToGlobal(viewportPos));
00655 
00656   // Scroll if cursor was moved to upper or lower end of agenda.
00657   if (clipperPos.y() < mScrollBorderWidth) {
00658     mScrollUpTimer.start(mScrollDelay);
00659   } else if (visibleHeight() - clipperPos.y() <
00660              mScrollBorderWidth) {
00661     mScrollDownTimer.start(mScrollDelay);
00662   } else {
00663     mScrollUpTimer.stop();
00664     mScrollDownTimer.stop();
00665   }
00666 
00667   if ( gy != mCurrentCellY && gy >= mStartCellY) {
00668     int selectionHeight = mSelectionHeight;
00669 
00670     // FIXME: Repaint only the newly (de)selected region
00671     int x, x1, y;
00672     gridToContents( mSelectionCellX, 0, x, y );
00673     gridToContents( mSelectionCellX + 1, gy+1, x1, y );
00674     mSelectionHeight = y - mSelectionYTop;
00675     repaintContents( x, mSelectionYTop, x1-x,
00676         (mSelectionHeight>selectionHeight)?mSelectionHeight:selectionHeight );
00677 
00678     mCurrentCellY = gy;
00679   }
00680 }
00681 
00682 void KOAgenda::endSelectAction( const QPoint &currentPos )
00683 {
00684   mActionType = NOP;
00685   mScrollUpTimer.stop();
00686   mScrollDownTimer.stop();
00687 
00688   emit newTimeSpanSignal(mStartCellX,mStartCellY,mCurrentCellX,mCurrentCellY);
00689 
00690   if ( KOPrefs::instance()->mSelectionStartsEditor ) {
00691     if ( ( mSelectionStartPoint - currentPos ).manhattanLength() >
00692          QApplication::startDragDistance() ) {
00693        emitNewEventForSelection();
00694     }
00695   }
00696 }
00697 
00698 void KOAgenda::startItemAction(const QPoint& viewportPos)
00699 {
00700   int x,y;
00701   viewportToContents(viewportPos.x(),viewportPos.y(),x,y);
00702   int gx,gy;
00703   contentsToGrid(x,y,gx,gy);
00704 
00705   mStartCellX = gx;
00706   mStartCellY = gy;
00707   mCurrentCellX = gx;
00708   mCurrentCellY = gy;
00709   bool noResize = ( mActionItem->incidence()->type() == "Todo");
00710 
00711 
00712   if (mAllDayMode) {
00713     int gridDistanceX = (int)(x - gx * mGridSpacingX);
00714     if (gridDistanceX < mResizeBorderWidth &&
00715         mActionItem->cellXLeft() == mCurrentCellX &&
00716         !noResize ) {
00717       mActionType = RESIZELEFT;
00718       setCursor(sizeHorCursor);
00719     } else if ((mGridSpacingX - gridDistanceX) < mResizeBorderWidth &&
00720                mActionItem->cellXRight() == mCurrentCellX &&
00721                !noResize ) {
00722       mActionType = RESIZERIGHT;
00723       setCursor(sizeHorCursor);
00724     } else {
00725       mActionType = MOVE;
00726       mActionItem->startMove();
00727       setCursor(sizeAllCursor);
00728     }
00729   } else {
00730     int gridDistanceY = (int)(y - gy * mGridSpacingY);
00731     if (gridDistanceY < mResizeBorderWidth &&
00732         mActionItem->cellYTop() == mCurrentCellY &&
00733         !mActionItem->firstMultiItem() &&
00734         !noResize ) {
00735       mActionType = RESIZETOP;
00736       setCursor(sizeVerCursor);
00737     } else if ((mGridSpacingY - gridDistanceY) < mResizeBorderWidth &&
00738                mActionItem->cellYBottom() == mCurrentCellY &&
00739                !mActionItem->lastMultiItem() &&
00740                !noResize )  {
00741       mActionType = RESIZEBOTTOM;
00742       setCursor(sizeVerCursor);
00743     } else {
00744       mActionType = MOVE;
00745       mActionItem->startMove();
00746       setCursor(sizeAllCursor);
00747     }
00748   }
00749 }
00750 
00751 void KOAgenda::performItemAction(const QPoint& viewportPos)
00752 {
00753 //  kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
00754 //  QPoint point = viewport()->mapToGlobal(viewportPos);
00755 //  kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
00756 //  point = clipper()->mapFromGlobal(point);
00757 //  kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
00758 //  kdDebug(5850) << "visible height: " << visibleHeight() << endl;
00759   int x,y;
00760   viewportToContents(viewportPos.x(),viewportPos.y(),x,y);
00761 //  kdDebug(5850) << "contents: " << x << "," << y << "\n" << endl;
00762   int gx,gy;
00763   contentsToGrid(x,y,gx,gy);
00764   QPoint clipperPos = clipper()->
00765                       mapFromGlobal(viewport()->mapToGlobal(viewportPos));
00766 
00767   // Cursor left active agenda area.
00768   // This starts a drag.
00769   if ( clipperPos.y() < 0 || clipperPos.y() > visibleHeight() ||
00770        clipperPos.x() < 0 || clipperPos.x() > visibleWidth() ) {
00771     if ( mActionType == MOVE ) {
00772       mScrollUpTimer.stop();
00773       mScrollDownTimer.stop();
00774       mActionItem->resetMove();
00775       placeSubCells( mActionItem );
00776       emit startDragSignal( mActionItem->incidence() );
00777       setCursor( arrowCursor );
00778       mActionItem = 0;
00779       mActionType = NOP;
00780       mItemMoved = 0;
00781       return;
00782     }
00783   } else {
00784     switch ( mActionType ) {
00785       case MOVE:
00786         setCursor( sizeAllCursor );
00787         break;
00788       case RESIZETOP:
00789       case RESIZEBOTTOM:
00790         setCursor( sizeVerCursor );
00791         break;
00792       case RESIZELEFT:
00793       case RESIZERIGHT:
00794         setCursor( sizeHorCursor );
00795         break;
00796       default:
00797         setCursor( arrowCursor );
00798     }
00799   }
00800 
00801   // Scroll if item was moved to upper or lower end of agenda.
00802   if (clipperPos.y() < mScrollBorderWidth) {
00803     mScrollUpTimer.start(mScrollDelay);
00804   } else if (visibleHeight() - clipperPos.y() <
00805              mScrollBorderWidth) {
00806     mScrollDownTimer.start(mScrollDelay);
00807   } else {
00808     mScrollUpTimer.stop();
00809     mScrollDownTimer.stop();
00810   }
00811 
00812   // Move or resize item if necessary
00813   if (mCurrentCellX != gx || mCurrentCellY != gy) {
00814     mItemMoved = true;
00815     mActionItem->raise();
00816     if (mActionType == MOVE) {
00817       // Move all items belonging to a multi item
00818       KOAgendaItem *firstItem = mActionItem->firstMultiItem();
00819       if (!firstItem) firstItem = mActionItem;
00820       KOAgendaItem *lastItem = mActionItem->lastMultiItem();
00821       if (!lastItem) lastItem = mActionItem;
00822       int dy = gy - mCurrentCellY;
00823       int dx = gx - mCurrentCellX;
00824       int x,y;
00825       KOAgendaItem *moveItem = firstItem;
00826       while (moveItem) {
00827         bool changed=false;
00828         if (dx!=0) {
00829           moveItem->moveRelative( dx, 0 );
00830           changed=true;
00831         }
00832         // in agenda's all day view don't try to move multi items, since there are none
00833         if ( moveItem==firstItem && !mAllDayMode ) { // is the first item
00834           int newY = dy+moveItem->cellYTop();
00835           // If event start moved earlier than 0:00, it starts the previous day
00836           if (newY<0) {
00837             moveItem->expandTop( -moveItem->cellYTop() );
00838             // prepend a new item at ( x-1, rows()+newY to rows() )
00839             KOAgendaItem *newFirst = firstItem->prevMoveItem();
00840             // cell's y values are first and last cell of the bar, so if newY=-1, they need to be the same
00841             if (newFirst) {
00842               newFirst->setCellXY(moveItem->cellXLeft()-1, rows()+newY, rows()-1);
00843               mItems.append(newFirst);
00844               moveItem->resize( (int)( mGridSpacingX * newFirst->cellWidth() ),
00845                                 (int)( mGridSpacingY * newFirst->cellHeight() ));
00846               gridToContents(newFirst->cellXLeft(), newFirst->cellYTop(),x,y);
00847               addChild( newFirst, x, y );
00848             } else {
00849               newFirst = insertItem( moveItem->incidence(), moveItem->itemDate(),
00850                 moveItem->cellXLeft()-1, rows()+newY, rows()-1 ) ;
00851             }
00852             if (newFirst) newFirst->show();
00853             moveItem->prependMoveItem(newFirst);
00854             firstItem=newFirst;
00855           } else if ( newY>=rows() ) {
00856             // If event start is moved past 24:00, it starts the next day
00857             // erase current item (i.e. remove it from the multiItem list)
00858             firstItem = moveItem->nextMultiItem();
00859             moveItem->hide();
00860             mItems.take( mItems.find( moveItem ) );
00861             removeChild( moveItem );
00862             mActionItem->removeMoveItem(moveItem);
00863             moveItem=firstItem;
00864             // adjust next day's item
00865             if (moveItem) moveItem->expandTop( rows()-newY );
00866           } else {
00867             moveItem->expandTop(dy);
00868           }
00869           changed=true;
00870         }
00871         if ( !moveItem->lastMultiItem() && !mAllDayMode ) { // is the last item
00872           int newY = dy+moveItem->cellYBottom();
00873           if (newY<0) {
00874             // erase current item
00875             lastItem = moveItem->prevMultiItem();
00876             moveItem->hide();
00877             mItems.take( mItems.find(moveItem) );
00878             removeChild( moveItem );
00879             moveItem->removeMoveItem( moveItem );
00880             moveItem = lastItem;
00881             moveItem->expandBottom(newY+1);
00882           } else if (newY>=rows()) {
00883             moveItem->expandBottom( rows()-moveItem->cellYBottom()-1 );
00884             // append item at ( x+1, 0 to newY-rows() )
00885             KOAgendaItem *newLast = lastItem->nextMoveItem();
00886             if (newLast) {
00887               newLast->setCellXY( moveItem->cellXLeft()+1, 0, newY-rows()-1 );
00888               mItems.append(newLast);
00889               moveItem->resize( (int)( mGridSpacingX * newLast->cellWidth() ),
00890                                 (int)( mGridSpacingY * newLast->cellHeight() ));
00891               gridToContents( newLast->cellXLeft(), newLast->cellYTop(), x, y) ;
00892               addChild( newLast, x, y );
00893             } else {
00894               newLast = insertItem( moveItem->incidence(), moveItem->itemDate(),
00895                 moveItem->cellXLeft()+1, 0, newY-rows()-1 ) ;
00896             }
00897             moveItem->appendMoveItem( newLast );
00898             newLast->show();
00899             lastItem = newLast;
00900           } else {
00901             moveItem->expandBottom( dy );
00902           }
00903           changed=true;
00904         }
00905         if (changed) {
00906           moveItem->resize((int)( mGridSpacingX * moveItem->cellWidth() ),
00907                          (int)( mGridSpacingY * moveItem->cellHeight() ));
00908           gridToContents( moveItem->cellXLeft(), moveItem->cellYTop(), x, y );
00909           moveChild( moveItem, x, y );
00910         }
00911         moveItem = moveItem->nextMultiItem();
00912       }
00913     } else if (mActionType == RESIZETOP) {
00914       if (mCurrentCellY <= mActionItem->cellYBottom()) {
00915         mActionItem->expandTop(gy - mCurrentCellY);
00916         mActionItem->resize(mActionItem->width(),
00917                             (int)( mGridSpacingY * mActionItem->cellHeight() ));
00918         int x,y;
00919         gridToContents(mCurrentCellX,mActionItem->cellYTop(),x,y);
00920         moveChild(mActionItem,childX(mActionItem),y);
00921       }
00922     } else if (mActionType == RESIZEBOTTOM) {
00923       if (mCurrentCellY >= mActionItem->cellYTop()) {
00924         mActionItem->expandBottom(gy - mCurrentCellY);
00925         mActionItem->resize(mActionItem->width(),
00926                             (int)( mGridSpacingY * mActionItem->cellHeight() ));
00927       }
00928     } else if (mActionType == RESIZELEFT) {
00929        if (mCurrentCellX <= mActionItem->cellXRight()) {
00930          mActionItem->expandLeft(gx - mCurrentCellX);
00931          mActionItem->resize((int)(mGridSpacingX * mActionItem->cellWidth()),
00932                              mActionItem->height());
00933         int x,y;
00934         gridToContents(mActionItem->cellXLeft(),mActionItem->cellYTop(),x,y);
00935         moveChild(mActionItem,x,childY(mActionItem));
00936        }
00937     } else if (mActionType == RESIZERIGHT) {
00938        if (mCurrentCellX >= mActionItem->cellXLeft()) {
00939          mActionItem->expandRight(gx - mCurrentCellX);
00940          mActionItem->resize((int)(mGridSpacingX * mActionItem->cellWidth()),
00941                              mActionItem->height());
00942        }
00943     }
00944     mCurrentCellX = gx;
00945     mCurrentCellY = gy;
00946   }
00947 }
00948 
00949 void KOAgenda::endItemAction()
00950 {
00951 //  kdDebug(5850) << "KOAgenda::endItemAction()" << endl;
00952 
00953   if ( mItemMoved ) {
00954     if ( mActionType == MOVE ) {
00955       mActionItem->endMove();
00956     }
00957     KOAgendaItem *placeItem = mActionItem->firstMultiItem();
00958     if ( !placeItem ) {
00959       placeItem = mActionItem;
00960     }
00961     emit itemModified( placeItem );
00962     QPtrList<KOAgendaItem> oldconflictItems = placeItem->conflictItems();
00963     KOAgendaItem *item;
00964     for ( item=oldconflictItems.first(); item != 0;
00965           item=oldconflictItems.next() ) {
00966       placeSubCells(item);
00967     }
00968     while ( placeItem ) {
00969       placeSubCells( placeItem );
00970       placeItem = placeItem->nextMultiItem();
00971     }
00972   }
00973 
00974   mScrollUpTimer.stop();
00975   mScrollDownTimer.stop();
00976   setCursor( arrowCursor );
00977   mActionItem = 0;
00978   mActionType = NOP;
00979   mItemMoved = 0;
00980 
00981 //  kdDebug(5850) << "KOAgenda::endItemAction() done" << endl;
00982 }
00983 
00984 void KOAgenda::setNoActionCursor( KOAgendaItem *moveItem, const QPoint& viewportPos )
00985 {
00986 //  kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
00987 //  QPoint point = viewport()->mapToGlobal(viewportPos);
00988 //  kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
00989 //  point = clipper()->mapFromGlobal(point);
00990 //  kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
00991 
00992   int x,y;
00993   viewportToContents(viewportPos.x(),viewportPos.y(),x,y);
00994 //  kdDebug(5850) << "contents: " << x << "," << y << endl  << endl;
00995   int gx,gy;
00996   contentsToGrid(x,y,gx,gy);
00997   bool noResize = (moveItem && moveItem->incidence() &&
00998       moveItem->incidence()->type() == "Todo");
00999 
01000   // Change cursor to resize cursor if appropriate
01001   if (mAllDayMode) {
01002     int gridDistanceX = (int)(x - gx * mGridSpacingX);
01003     if ( !noResize &&
01004          gridDistanceX < mResizeBorderWidth &&
01005          moveItem->cellXLeft() == gx ) {
01006       setCursor(sizeHorCursor);
01007     } else if ( !noResize &&
01008                 (mGridSpacingX - gridDistanceX) < mResizeBorderWidth &&
01009                 moveItem->cellXRight() == gx ) {
01010       setCursor(sizeHorCursor);
01011     } else {
01012       setCursor(arrowCursor);
01013     }
01014   } else {
01015     int gridDistanceY = (int)(y - gy * mGridSpacingY);
01016     if ( !noResize &&
01017          gridDistanceY < mResizeBorderWidth &&
01018          moveItem->cellYTop() == gy &&
01019          !moveItem->firstMultiItem() ) {
01020       setCursor(sizeVerCursor);
01021     } else if ( !noResize &&
01022                 (mGridSpacingY - gridDistanceY) < mResizeBorderWidth &&
01023                 moveItem->cellYBottom() == gy &&
01024                 !moveItem->lastMultiItem()) {
01025       setCursor(sizeVerCursor);
01026     } else {
01027       setCursor(arrowCursor);
01028     }
01029   }
01030 }
01031 
01032 
01035 double KOAgenda::calcSubCellWidth( KOAgendaItem *item )
01036 {
01037   int x, y, x1, y1;
01038   gridToContents( item->cellXLeft(), item->cellYTop(), x, y );
01039   gridToContents( item->cellXLeft()+1, item->cellYTop()+1, x1, y1 );
01040   int maxSubCells = item->subCells();
01041   double newSubCellWidth;
01042   if ( mAllDayMode ) {
01043     newSubCellWidth = double( y1 - y ) / maxSubCells;
01044   } else {
01045     newSubCellWidth = double( x1 - x ) / maxSubCells;
01046   }
01047   return newSubCellWidth;
01048 }
01049 
01050 void KOAgenda::placeAgendaItem( KOAgendaItem *item, double subCellWidth )
01051 {
01052   int x, y, x1, y1;
01053   // left upper corner, no subcells yet
01054   // right lower corner
01055   gridToContents( item->cellXLeft(), item->cellYTop(), x, y );
01056   gridToContents( item->cellXLeft() + item->cellWidth(),
01057                   item->cellYBottom()+1, x1, y1 );
01058 
01059   double subCellPos = item->subCell() * subCellWidth;
01060 
01061   // we need to add 0.01 to make sure we don't loose one pixed due to
01062   // numerics (i.e. if it would be x.9998, we want the integer, not rounded down.
01063   if (mAllDayMode) {
01064     item->resize( x1-x, int( subCellPos + subCellWidth + 0.01 ) - int( subCellPos ) );
01065     y += int( subCellPos );
01066   } else {
01067     item->resize( int( subCellPos + subCellWidth + 0.01 ) - int( subCellPos ), y1-y );
01068     x += int( subCellPos );
01069   }
01070   moveChild( item, x, y );
01071 }
01072 
01073 /*
01074   Place item in cell and take care that multiple items using the same cell do
01075   not overlap. This method is not yet optimal. It doesn't use the maximum space
01076   it can get in all cases.
01077   At the moment the method has a bug: When an item is placed only the sub cell
01078   widths of the items are changed, which are within the Y region the item to
01079   place spans. When the sub cell width change of one of this items affects a
01080   cell, where other items are, which do not overlap in Y with the item to place,
01081   the display gets corrupted, although the corruption looks quite nice.
01082 */
01083 void KOAgenda::placeSubCells( KOAgendaItem *placeItem )
01084 {
01085 #if 0
01086   kdDebug(5850) << "KOAgenda::placeSubCells()" << endl;
01087   if ( placeItem ) {
01088     Incidence *event = placeItem->incidence();
01089     if ( !event ) {
01090       kdDebug(5850) << "  event is 0" << endl;
01091     } else {
01092       kdDebug(5850) << "  event: " << event->summary() << endl;
01093     }
01094   } else {
01095     kdDebug(5850) << "  placeItem is 0" << endl;
01096   }
01097   kdDebug(5850) << "KOAgenda::placeSubCells()..." << endl;
01098 #endif
01099 
01100   QPtrList<KOrg::CellItem> cells;
01101   KOAgendaItem *item;
01102   for ( item = mItems.first(); item != 0; item = mItems.next() ) {
01103     cells.append( item );
01104   }
01105 
01106   QPtrList<KOrg::CellItem> items = KOrg::CellItem::placeItem( cells,
01107                                                               placeItem );
01108 
01109   placeItem->setConflictItems( QPtrList<KOAgendaItem>() );
01110   double newSubCellWidth = calcSubCellWidth( placeItem );
01111   KOrg::CellItem *i;
01112   for ( i = items.first(); i; i = items.next() ) {
01113     item = static_cast<KOAgendaItem *>( i );
01114     placeAgendaItem( item, newSubCellWidth );
01115     item->addConflictItem( placeItem );
01116     placeItem->addConflictItem( item );
01117   }
01118   if ( items.isEmpty() )
01119     placeAgendaItem( placeItem, newSubCellWidth );
01120   placeItem->update();
01121 }
01122 
01123 /*
01124   Draw grid in the background of the agenda.
01125 */
01126 void KOAgenda::drawContents(QPainter* p, int cx, int cy, int cw, int ch)
01127 {
01128 
01129   QPixmap db(cw, ch);
01130   db.fill(KOPrefs::instance()->mAgendaBgColor);
01131   QPainter dbp(&db);
01132   dbp.translate(-cx,-cy);
01133 
01134 //  kdDebug(5850) << "KOAgenda::drawContents()" << endl;
01135   double lGridSpacingY = mGridSpacingY*2;
01136 
01137   // Highlight working hours
01138   if (mWorkingHoursEnable) {
01139     int x1 = cx;
01140     int y1 = mWorkingHoursYTop;
01141     if (y1 < cy) y1 = cy;
01142     int x2 = cx+cw-1;
01143     //  int x2 = mGridSpacingX * 5 - 1;
01144     //  if (x2 > cx+cw-1) x2 = cx + cw - 1;
01145     int y2 = mWorkingHoursYBottom;
01146     if (y2 > cy+ch-1) y2=cy+ch-1;
01147 
01148     if (x2 >= x1 && y2 >= y1) {
01149       int gxStart = (int)(x1/mGridSpacingX);
01150       int gxEnd = (int)(x2/mGridSpacingX);
01151       while(gxStart <= gxEnd) {
01152         if (gxStart < int(mHolidayMask->count()) &&
01153             !mHolidayMask->at(gxStart)) {
01154           int xStart = (int)( KOGlobals::self()->reverseLayout() ?
01155                                     (mColumns - 1 - gxStart)*mGridSpacingX :
01156                               gxStart*mGridSpacingX );
01157           if (xStart < x1) xStart = x1;
01158           int xEnd = (int)( KOGlobals::self()->reverseLayout() ?
01159                                     (mColumns - gxStart)*mGridSpacingX-1 :
01160                             (gxStart+1)*mGridSpacingX-1 );
01161           if (xEnd > x2) xEnd = x2;
01162           dbp.fillRect(xStart,y1,xEnd-xStart+1,y2-y1+1,
01163                       KOPrefs::instance()->mWorkingHoursColor);
01164         }
01165         ++gxStart;
01166       }
01167     }
01168   }
01169 
01170   int selectionX, selectionX1, selectionY;
01171   gridToContents( mSelectionCellX, 0, selectionX, selectionY );
01172   gridToContents( mSelectionCellX+1, 0, selectionX1, selectionY );
01173 
01174   // Draw selection
01175   if ( ( cx + cw ) >= selectionX && cx <= ( selectionX1 ) &&
01176        ( cy + ch ) >= mSelectionYTop && cy <= ( mSelectionYTop + mSelectionHeight ) ) {
01177     // TODO: paint only part within cx,cy,cw,ch
01178     dbp.fillRect( selectionX, mSelectionYTop, selectionX1-selectionX,
01179                  mSelectionHeight, KOPrefs::instance()->mHighlightColor );
01180   }
01181 
01182   dbp.setPen( KOPrefs::instance()->mAgendaBgColor.dark(150) );
01183 
01184   // Draw vertical lines of grid, start with the last line not yet visible
01185   //  kdDebug(5850) << "drawContents cx: " << cx << " cy: " << cy << " cw: " << cw << " ch: " << ch << endl;
01186   double x = ((int)(cx/mGridSpacingX))*mGridSpacingX;
01187   while (x < cx + cw) {
01188     dbp.drawLine((int)x,cy,(int)x,cy+ch);
01189     x+=mGridSpacingX;
01190   }
01191 
01192   // Draw horizontal lines of grid
01193   double y = ((int)(cy/lGridSpacingY))*lGridSpacingY;
01194   while (y < cy + ch) {
01195 //    kdDebug(5850) << " y: " << y << endl;
01196     dbp.drawLine(cx,(int)y,cx+cw,(int)y);
01197     y+=lGridSpacingY;
01198   }
01199   p->drawPixmap(cx,cy, db);
01200 }
01201 
01202 /*
01203   Convert srcollview contents coordinates to agenda grid coordinates.
01204 */
01205 void KOAgenda::contentsToGrid (int x, int y, int& gx, int& gy)
01206 {
01207   gx = (int)( KOGlobals::self()->reverseLayout() ?
01208         mColumns - x/mGridSpacingX : x/mGridSpacingX );
01209   gy = (int)( y/mGridSpacingY );
01210 }
01211 
01212 /*
01213   Convert agenda grid coordinates to scrollview contents coordinates.
01214 */
01215 void KOAgenda::gridToContents (int gx, int gy, int& x, int& y)
01216 {
01217   x = (int)( KOGlobals::self()->reverseLayout() ?
01218              (mColumns - gx)*mGridSpacingX : gx*mGridSpacingX );
01219   y = (int)( gy*mGridSpacingY );
01220 }
01221 
01222 
01223 /*
01224   Return Y coordinate corresponding to time. Coordinates are rounded to fit into
01225   the grid.
01226 */
01227 int KOAgenda::timeToY(const QTime &time)
01228 {
01229 //  kdDebug(5850) << "Time: " << time.toString() << endl;
01230   int minutesPerCell = 24 * 60 / mRows;
01231 //  kdDebug(5850) << "minutesPerCell: " << minutesPerCell << endl;
01232   int timeMinutes = time.hour() * 60 + time.minute();
01233 //  kdDebug(5850) << "timeMinutes: " << timeMinutes << endl;
01234   int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
01235 //  kdDebug(5850) << "y: " << Y << endl;
01236 //  kdDebug(5850) << "\n" << endl;
01237   return Y;
01238 }
01239 
01240 
01241 /*
01242   Return time corresponding to cell y coordinate. Coordinates are rounded to
01243   fit into the grid.
01244 */
01245 QTime KOAgenda::gyToTime(int gy)
01246 {
01247 //  kdDebug(5850) << "gyToTime: " << gy << endl;
01248   int secondsPerCell = 24 * 60 * 60/ mRows;
01249 
01250   int timeSeconds = secondsPerCell * gy;
01251 
01252   QTime time( 0, 0, 0 );
01253   if ( timeSeconds < 24 * 60 * 60 ) {
01254     time = time.addSecs(timeSeconds);
01255   } else {
01256     time.setHMS( 23, 59, 59 );
01257   }
01258 //  kdDebug(5850) << "  gyToTime: " << time.toString() << endl;
01259 
01260   return time;
01261 }
01262 
01263 void KOAgenda::setStartHour(int startHour)
01264 {
01265   int startCell = startHour * mRows / 24;
01266   setContentsPos(0, (int)(startCell * gridSpacingY()));
01267 }
01268 
01269 
01270 /*
01271   Insert KOAgendaItem into agenda.
01272 */
01273 KOAgendaItem *KOAgenda::insertItem (Incidence *event,QDate qd,int X,int YTop,int YBottom)
01274 {
01275   //kdDebug(5850) << "KOAgenda::insertItem:" << event->summary() << "-" << qd.toString() << " ;top, bottom:" << YTop << "," << YBottom << endl;
01276 
01277   if (mAllDayMode) {
01278     kdDebug(5850) << "KOAgenda: calling insertItem in all-day mode is illegal." << endl;
01279     return 0;
01280   }
01281 
01282   KOAgendaItem *agendaItem = new KOAgendaItem (event,qd,viewport());
01283   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem* ) ),
01284            this, SLOT( removeAgendaItem( KOAgendaItem* ) ) );
01285   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem* ) ),
01286            this, SLOT( showAgendaItem( KOAgendaItem* ) ) );
01287 
01288   if ( YBottom<=YTop ) {
01289     kdDebug(5850) << "KOAgenda::insertItem(): Text: " << agendaItem->text() << " YSize<0" << endl;
01290     YBottom = YTop;
01291   }
01292 
01293   agendaItem->resize( (int)( (X+1)*mGridSpacingX ) - (int)( X*mGridSpacingX ), 
01294                       (int)( YTop*mGridSpacingY ) - (int)( (YBottom+1) * mGridSpacingY ) );
01295   agendaItem->setCellXY(X,YTop,YBottom);
01296   agendaItem->setCellXRight(X);
01297 
01298   agendaItem->installEventFilter(this);
01299 
01300   addChild(agendaItem,(int)( X*mGridSpacingX ), (int)( YTop*mGridSpacingY ));
01301   mItems.append(agendaItem);
01302 
01303   placeSubCells(agendaItem);
01304 
01305   agendaItem->show();
01306 
01307   marcus_bains();
01308 
01309   return agendaItem;
01310 }
01311 
01312 
01313 /*
01314   Insert all-day KOAgendaItem into agenda.
01315 */
01316 KOAgendaItem *KOAgenda::insertAllDayItem (Incidence *event,QDate qd,int XBegin,int XEnd)
01317 {
01318    if (!mAllDayMode) {
01319     kdDebug(5850) << "KOAgenda: calling insertAllDayItem in non all-day mode is illegal." << endl;
01320     return 0;
01321   }
01322 
01323   KOAgendaItem *agendaItem = new KOAgendaItem (event,qd,viewport());
01324   connect( agendaItem, SIGNAL( removeAgendaItem( KOAgendaItem* ) ),
01325            this, SLOT( removeAgendaItem( KOAgendaItem* ) ) );
01326   connect( agendaItem, SIGNAL( showAgendaItem( KOAgendaItem* ) ),
01327            this, SLOT( showAgendaItem( KOAgendaItem* ) ) );
01328 
01329   agendaItem->setCellXY(XBegin,0,0);
01330   agendaItem->setCellXRight(XEnd);
01331 
01332   double startIt = mGridSpacingX * (agendaItem->cellXLeft());
01333   double endIt = mGridSpacingX * (agendaItem->cellWidth()+agendaItem->cellXLeft());
01334 
01335   agendaItem->resize( int(endIt) - int(startIt), int(mGridSpacingY));
01336 
01337   agendaItem->installEventFilter(this);
01338 
01339   addChild(agendaItem,(int)( XBegin*mGridSpacingX ), 0);
01340   mItems.append(agendaItem);
01341 
01342   placeSubCells(agendaItem);
01343 
01344   agendaItem->show();
01345 
01346   return agendaItem;
01347 }
01348 
01349 
01350 void KOAgenda::insertMultiItem (Event *event,QDate qd,int XBegin,int XEnd,
01351                                 int YTop,int YBottom)
01352 {
01353   if (mAllDayMode) {
01354     kdDebug(5850) << "KOAgenda: calling insertMultiItem in all-day mode is illegal." << endl;
01355     return;
01356   }
01357 
01358   int cellX,cellYTop,cellYBottom;
01359   QString newtext;
01360   int width = XEnd - XBegin + 1;
01361   int count = 0;
01362   KOAgendaItem *current = 0;
01363   QPtrList<KOAgendaItem> multiItems;
01364   for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
01365     if ( cellX == XBegin ) cellYTop = YTop;
01366     else cellYTop = 0;
01367     if ( cellX == XEnd ) cellYBottom = YBottom;
01368     else cellYBottom = rows() - 1;
01369     newtext = QString("(%1/%2): ").arg( ++count ).arg( width );
01370     newtext.append( event->summary() );
01371     current = insertItem( event, qd, cellX, cellYTop, cellYBottom );
01372     current->setText( newtext );
01373     multiItems.append( current );
01374   }
01375 
01376   KOAgendaItem *next = 0;
01377   KOAgendaItem *prev = 0;
01378   KOAgendaItem *last = multiItems.last();
01379   KOAgendaItem *first = multiItems.first();
01380   KOAgendaItem *setFirst,*setLast;
01381   current = first;
01382   while (current) {
01383     next = multiItems.next();
01384     if (current == first) setFirst = 0;
01385     else setFirst = first;
01386     if (current == last) setLast = 0;
01387     else setLast = last;
01388 
01389     current->setMultiItem(setFirst, prev, next, setLast);
01390     prev=current;
01391     current = next;
01392   }
01393 
01394   marcus_bains();
01395 }
01396 
01397 void KOAgenda::removeEvent ( Event *event )
01398 {
01399   KOAgendaItem *item = mItems.first();
01400   bool taken = false;
01401   // First find all items to be deleted and store them
01402   // in its own list. Otherwise removeAgendaItem will reset
01403   // the current position and mess this up.
01404   QPtrList<KOAgendaItem> mItemsToRemove;
01405   while ( item ) {
01406     if ( item->incidence() == event ) {
01407       mItemsToRemove.append( item );
01408     }
01409     item = mItems.next();
01410   }
01411   item = mItemsToRemove.first();
01412   while ( item ) {
01413     taken = removeAgendaItem( item );
01414     item = mItemsToRemove.next();
01415   }
01416 }
01417 
01418 void KOAgenda::showAgendaItem( KOAgendaItem* agendaItem )
01419 {
01420   if ( !agendaItem ) return;
01421   agendaItem->hide();
01422   addChild( agendaItem );
01423   if ( !mItems.containsRef( agendaItem ) )
01424     mItems.append( agendaItem );
01425   placeSubCells( agendaItem );
01426   agendaItem->show();
01427 }
01428 
01429 bool KOAgenda::removeAgendaItem( KOAgendaItem* item )
01430 {
01431   // we found the item. Let's remove it and update the conflicts
01432   bool taken = false;
01433   KOAgendaItem *thisItem = item;
01434   QPtrList<KOAgendaItem> conflictItems = thisItem->conflictItems();
01435   removeChild( thisItem );
01436   int pos = mItems.find( thisItem );
01437   if ( pos>=0 ) {
01438     mItems.take( pos );
01439     taken = true;
01440   }
01441 
01442   KOAgendaItem *confitem;
01443   for ( confitem = conflictItems.first(); confitem != 0;
01444         confitem = conflictItems.next() ) {
01445     // the item itself is also in its own conflictItems list!
01446     if ( confitem != thisItem ) placeSubCells(confitem);
01447     
01448   }
01449   mItemsToDelete.append( thisItem );
01450   QTimer::singleShot( 0, this, SLOT( deleteItemsToDelete() ) );
01451   return taken;
01452 }
01453 
01454 void KOAgenda::deleteItemsToDelete()
01455 {
01456   mItemsToDelete.clear();
01457 }
01458 
01459 //QSizePolicy KOAgenda::sizePolicy() const
01460 //{
01461   // Thought this would make the all-day event agenda minimum size and the
01462   // normal agenda take the remaining space. But it doesnt work. The QSplitter
01463   // dont seem to think that an Expanding widget needs more space than a
01464   // Preferred one.
01465   // But it doesnt hurt, so it stays.
01466 //  if (mAllDayMode) {
01467 //    return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred);
01468 //  } else {
01469 //    return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
01470 //  }
01471 //}
01472 
01473 
01474 /*
01475   Overridden from QScrollView to provide proper resizing of KOAgendaItems.
01476 */
01477 void KOAgenda::resizeEvent ( QResizeEvent *ev )
01478 {
01479 //  kdDebug(5850) << "KOAgenda::resizeEvent" << endl;
01480   double subCellWidth;
01481   KOAgendaItem *item;
01482   if (mAllDayMode) {
01483     mGridSpacingX = double( width() - 2 * frameWidth() ) / (double)mColumns;
01484 //    kdDebug(5850) << "Frame " << frameWidth() << endl;
01485     mGridSpacingY = height() - 2 * frameWidth();
01486     resizeContents( (int)( mGridSpacingX * mColumns ), (int)(mGridSpacingY ));
01487 
01488     for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01489       subCellWidth = calcSubCellWidth( item );
01490       placeAgendaItem( item, subCellWidth );
01491     }
01492   } else {
01493     mGridSpacingX = double(width() - verticalScrollBar()->width() - 2 * frameWidth()) / double(mColumns);
01494     // make sure that there are not more than 24 per day
01495     mGridSpacingY = double(height() - 2 * frameWidth()) / double(mRows);
01496     if ( mGridSpacingY < mDesiredGridSpacingY ) mGridSpacingY = mDesiredGridSpacingY;
01497 
01498     resizeContents( int( mGridSpacingX * mColumns ), int( mGridSpacingY * mRows ));
01499 
01500     for ( item=mItems.first(); item != 0; item=mItems.next() ) {
01501       subCellWidth = calcSubCellWidth( item );
01502       placeAgendaItem( item, subCellWidth );
01503     }
01504   }
01505 
01506   checkScrollBoundaries();
01507   calculateWorkingHours();
01508 
01509   marcus_bains();
01510 
01511   QScrollView::resizeEvent(ev);
01512   viewport()->update();
01513 }
01514 
01515 
01516 void KOAgenda::scrollUp()
01517 {
01518   scrollBy(0,-mScrollOffset);
01519 }
01520 
01521 
01522 void KOAgenda::scrollDown()
01523 {
01524   scrollBy(0,mScrollOffset);
01525 }
01526 
01527 void KOAgenda::popupAlarm()
01528 {
01529   if (!mClickedItem) {
01530     kdDebug(5850) << "KOAgenda::popupAlarm() called without having a clicked item" << endl;
01531     return;
01532   }
01533 // TODO: deal correctly with multiple alarms
01534   Alarm::List alarms = mClickedItem->incidence()->alarms();
01535   Alarm::List::ConstIterator it;
01536   for( it = alarms.begin(); it != alarms.end(); ++it )
01537     (*it)->toggleAlarm();
01538   if (alarms.isEmpty()) {
01539     // Add an alarm if it didn't have one
01540     Alarm*alm = mClickedItem->incidence()->newAlarm();
01541     alm->setEnabled(true);
01542   }
01543 
01544   mClickedItem->updateIcons();
01545 }
01546 
01547 /*
01548   Calculates the minimum width
01549 */
01550 int KOAgenda::minimumWidth() const
01551 {
01552   // TODO:: develop a way to dynamically determine the minimum width
01553   int min = 100;
01554 
01555   return min;
01556 }
01557 
01558 void KOAgenda::updateConfig()
01559 {
01560   mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
01561  // make sure that there are not more than 24 per day
01562   mGridSpacingY = (double)height()/(double)mRows;
01563   if (mGridSpacingY<mDesiredGridSpacingY) mGridSpacingY=mDesiredGridSpacingY;
01564 
01565   calculateWorkingHours();
01566 
01567   marcus_bains();
01568 }
01569 
01570 void KOAgenda::checkScrollBoundaries()
01571 {
01572   // Invalidate old values to force update
01573   mOldLowerScrollValue = -1;
01574   mOldUpperScrollValue = -1;
01575 
01576   checkScrollBoundaries(verticalScrollBar()->value());
01577 }
01578 
01579 void KOAgenda::checkScrollBoundaries(int v)
01580 {
01581   int yMin = (int)(v/mGridSpacingY);
01582   int yMax = (int)((v+visibleHeight())/mGridSpacingY);
01583 
01584 //  kdDebug(5850) << "--- yMin: " << yMin << "  yMax: " << yMax << endl;
01585 
01586   if (yMin != mOldLowerScrollValue) {
01587     mOldLowerScrollValue = yMin;
01588     emit lowerYChanged(yMin);
01589   }
01590   if (yMax != mOldUpperScrollValue) {
01591     mOldUpperScrollValue = yMax;
01592     emit upperYChanged(yMax);
01593   }
01594 }
01595 
01596 void KOAgenda::deselectItem()
01597 {
01598   if (mSelectedItem.isNull()) return;
01599   mSelectedItem->select(false);
01600   mSelectedItem = 0;
01601 }
01602 
01603 void KOAgenda::selectItem(KOAgendaItem *item)
01604 {
01605   if ((KOAgendaItem *)mSelectedItem == item) return;
01606   deselectItem();
01607   if (item == 0) {
01608     emit incidenceSelected( 0 );
01609     return;
01610   }
01611   mSelectedItem = item;
01612   mSelectedItem->select();
01613   emit incidenceSelected( mSelectedItem->incidence() );
01614 }
01615 
01616 // This function seems never be called.
01617 void KOAgenda::keyPressEvent( QKeyEvent *kev )
01618 {
01619   switch(kev->key()) {
01620     case Key_PageDown:
01621       verticalScrollBar()->addPage();
01622       break;
01623     case Key_PageUp:
01624       verticalScrollBar()->subtractPage();
01625       break;
01626     case Key_Down:
01627       verticalScrollBar()->addLine();
01628       break;
01629     case Key_Up:
01630       verticalScrollBar()->subtractLine();
01631       break;
01632     default:
01633       ;
01634   }
01635 }
01636 
01637 void KOAgenda::calculateWorkingHours()
01638 {
01639 //  mWorkingHoursEnable = KOPrefs::instance()->mEnableWorkingHours;
01640   mWorkingHoursEnable = !mAllDayMode;
01641 
01642   mWorkingHoursYTop = (int)(mGridSpacingY *
01643                       KOPrefs::instance()->mWorkingHoursStart * 4);
01644   mWorkingHoursYBottom = (int)(mGridSpacingY *
01645                          KOPrefs::instance()->mWorkingHoursEnd * 4 - 1);
01646 }
01647 
01648 
01649 DateList KOAgenda::dateList() const
01650 {
01651     return mSelectedDates;
01652 }
01653 
01654 void KOAgenda::setDateList(const DateList &selectedDates)
01655 {
01656     mSelectedDates = selectedDates;
01657     marcus_bains();
01658 }
01659 
01660 void KOAgenda::setHolidayMask(QMemArray<bool> *mask)
01661 {
01662   mHolidayMask = mask;
01663 
01664 /*
01665   kdDebug(5850) << "HolidayMask: ";
01666   for(uint i=0;i<mask->count();++i) {
01667     kdDebug(5850) << (mask->at(i) ? "*" : "o");
01668   }
01669   kdDebug(5850) << endl;
01670 */
01671 }
01672 
01673 void KOAgenda::contentsMousePressEvent ( QMouseEvent *event )
01674 {
01675   kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
01676   QScrollView::contentsMousePressEvent(event);
01677 }
01678 
01679 void KOAgenda::setTypeAheadReceiver( QObject *o )
01680 {
01681   mTypeAheadReceiver = o;
01682 }
01683 
01684 QObject *KOAgenda::typeAheadReceiver() const
01685 {
01686   return mTypeAheadReceiver;
01687 }
KDE Logo
This file is part of the documentation for korganizer Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Apr 26 23:23:33 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003