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 Wed Jul 28 23:58:13 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003