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