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