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