korganizer

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 Qt, and distribute the resulting executable,
00025     without including the source code for Qt 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 <klocale.h>
00039 #include <kiconloader.h>
00040 #include <kglobal.h>
00041 #include <kmessagebox.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(Qt::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(Qt::AlignRight | Qt::AlignBottom);
00077   TQPalette pal = mTimeBox->palette();
00078   pal.setColor(TQColorGroup::Foreground, Qt::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 = KGlobal::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( 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 = qMakePair( static_cast<ResourceCalendar *>( 0 ), TQString() );
00278   mActionType = NOP;
00279   mItemMoved = false;
00280 
00281   mSelectedItem = 0;
00282   mSelectedUid = TQString::null;
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( 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, static_cast<TQMouseEvent *>( event ) );
00388 #ifndef QT_NO_WHEELEVENT
00389     case TQEvent::Wheel:
00390       return eventFilter_wheel( object, static_cast<TQWheelEvent *>( event ) );
00391 #endif
00392     case TQEvent::KeyPress:
00393     case TQEvent::KeyRelease:
00394       return eventFilter_key( object, static_cast<TQKeyEvent *>( event ) );
00395 
00396     case ( TQEvent::Leave ):
00397       if ( !mActionItem )
00398         setCursor( arrowCursor );
00399       if ( 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 ( object != viewport() && object != this ) {
00426     viewportPos = static_cast<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 ( 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( 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   QPair<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 QT_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 ( 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 ( 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 (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 (object != viewport()) {
00610         if (me->button() == 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( object, 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 = qMakePair( 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( object, me ); // pass through for use by multiagenda
00639           }
00640         }
00641       } else {
00642         if ( me->button() == 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 = qMakePair( static_cast<ResourceCalendar *>( 0 ), TQString() );
00661             setCursor(arrowCursor);
00662             startSelectAction(viewportPos);
00663           }
00664         }
00665         return TQScrollView::eventFilter( object, 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 (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 (object == viewport()) {
00726         selectItem(0);
00727         QPair<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 &currentPos )
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 = qMakePair( 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 = qMakePair( 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 = qMakePair( 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 QSplitter
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 }