korganizer

koagenda.cpp
1 /*
2  This file is part of KOrganizer.
3  Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
4  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
5 
6  Marcus Bains line.
7  Copyright (c) 2001 Ali Rahimi
8 
9  This program is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 
23  As a special exception, permission is given to link this program
24  with any edition of TQt, and distribute the resulting executable,
25  without including the source code for TQt in the source distribution.
26 */
27 #include <assert.h>
28 
29 #include <tqintdict.h>
30 #include <tqdatetime.h>
31 #include <tqapplication.h>
32 #include <tqpopupmenu.h>
33 #include <tqcursor.h>
34 #include <tqpainter.h>
35 #include <tqlabel.h>
36 
37 #include <kdebug.h>
38 #include <tdelocale.h>
39 #include <kiconloader.h>
40 #include <tdeglobal.h>
41 #include <tdemessagebox.h>
42 
43 #include "koagendaitem.h"
44 #include "koprefs.h"
45 #include "koglobals.h"
46 #include "komessagebox.h"
47 #include "incidencechanger.h"
48 #include "kohelper.h"
49 
50 #include "koagenda.h"
51 #include "koagenda.moc"
52 #include <korganizer/baseview.h>
53 
54 #include <libkcal/event.h>
55 #include <libkcal/todo.h>
56 #include <libkcal/dndfactory.h>
57 #include <libkcal/icaldrag.h>
58 #include <libkcal/vcaldrag.h>
59 #include <libkcal/calendar.h>
61 #include <libkcal/calhelper.h>
62 #include <math.h>
63 
65 MarcusBains::MarcusBains(KOAgenda *_agenda,const char *name )
66  : TQFrame(_agenda->viewport(), name), agenda(_agenda)
67 {
68  setLineWidth(0);
69  setMargin(0);
70  setBackgroundColor(TQt::red);
71  minutes = new TQTimer(this);
72  connect(minutes, TQ_SIGNAL(timeout()), this, TQ_SLOT(updateLocation()));
73  minutes->start(0, true);
74 
75  mTimeBox = new TQLabel(this);
76  mTimeBox->setAlignment(TQt::AlignRight | TQt::AlignBottom);
77  TQPalette pal = mTimeBox->palette();
78  pal.setColor(TQColorGroup::Foreground, TQt::red);
79  mTimeBox->setPalette(pal);
80  mTimeBox->setAutoMask(true);
81 
82  agenda->addChild(mTimeBox);
83 
84  mOldTime = TQTime( 0, 0 );
85  mOldToday = -1;
86 }
87 
88 MarcusBains::~MarcusBains()
89 {
90  delete minutes;
91 }
92 
93 int MarcusBains::todayColumn()
94 {
95  TQDate currentDate = TQDate::currentDate();
96 
97  DateList dateList = agenda->dateList();
98  DateList::ConstIterator it;
99  int col = 0;
100  for(it = dateList.begin(); it != dateList.end(); ++it) {
101  if((*it) == currentDate)
102  return KOGlobals::self()->reverseLayout() ?
103  agenda->columns() - 1 - col : col;
104  ++col;
105  }
106 
107  return -1;
108 }
109 
110 void MarcusBains::updateLocation()
111 {
112  updateLocationRecalc();
113 }
114 
115 void MarcusBains::updateLocationRecalc( bool recalculate )
116 {
117  TQTime tim = TQTime::currentTime();
118  if((tim.hour() == 0) && (mOldTime.hour()==23))
119  recalculate = true;
120 
121  int mins = tim.hour()*60 + tim.minute();
122  int minutesPerCell = 24 * 60 / agenda->rows();
123  int y = int( mins * agenda->gridSpacingY() / minutesPerCell );
124  int today = recalculate ? todayColumn() : mOldToday;
125  int x = int( agenda->gridSpacingX() * today );
126 
127  mOldTime = tim;
128  mOldToday = today;
129 
130  bool hideIt = !( KOPrefs::instance()->mMarcusBainsEnabled );
131 
132  if ( !isHidden() && ( hideIt || ( today < 0 ) ) ) {
133  hide();
134  mTimeBox->hide();
135  return;
136  }
137 
138  if ( isHidden() && !hideIt ) {
139  show();
140  mTimeBox->show();
141  }
142 
143  if ( recalculate ) setFixedSize( int( agenda->gridSpacingX() ), 1 );
144  agenda->moveChild( this, x, y );
145  raise();
146 
147  if(recalculate)
148  mTimeBox->setFont(KOPrefs::instance()->mMarcusBainsFont);
149 
150  TQString timeStr = TDEGlobal::locale()->formatTime(tim, KOPrefs::instance()->mMarcusBainsShowSeconds);
151  TQFontMetrics fm = fontMetrics();
152  mTimeBox->setText( timeStr );
153  TQSize sz( fm.width( timeStr + ' ' ), fm.height() );
154  mTimeBox->setFixedSize( sz );
155 
156  if (y-mTimeBox->height()>=0) y-=mTimeBox->height(); else y++;
157  if (x-mTimeBox->width()+agenda->gridSpacingX() > 0)
158  x += int( agenda->gridSpacingX() - mTimeBox->width() - 1 );
159  else x++;
160  agenda->moveChild(mTimeBox,x,y);
161  mTimeBox->raise();
162  mTimeBox->setAutoMask(true);
163 
164  minutes->start(1000,true);
165 }
166 
167 
169 
170 
171 /*
172  Create an agenda widget with rows rows and columns columns.
173 */
174 KOAgenda::KOAgenda( int columns, int rows, int rowSize, CalendarView *calendarView,
175  TQWidget *parent, const char *name, WFlags f )
176  : TQScrollView( parent, name, f ), mChanger( 0 )
177 {
178  mColumns = columns;
179  mRows = rows;
180  mGridSpacingY = rowSize;
181  if ( mGridSpacingY < 4 || mGridSpacingY > 30 ) {
182  mGridSpacingY = 10;
183  }
184 
185  mCalendarView = calendarView;
186 
187  mAllDayMode = false;
188 
189  init();
190 
191  viewport()->setMouseTracking(true);
192 }
193 
194 /*
195  Create an agenda widget with columns columns and one row. This is used for
196  all-day events.
197 */
198 KOAgenda::KOAgenda( int columns, CalendarView *calendarView, TQWidget *parent,
199  const char *name, WFlags f ) : TQScrollView( parent, name, f )
200 {
201  mColumns = columns;
202  mRows = 1;
203  mGridSpacingY = 24;
204  mAllDayMode = true;
205  mCalendarView = calendarView;
206  setVScrollBarMode( AlwaysOff );
207 
208  init();
209 }
210 
211 
212 KOAgenda::~KOAgenda()
213 {
214  delete mMarcusBains;
215 }
216 
217 
218 Incidence *KOAgenda::selectedIncidence() const
219 {
220  return ( mSelectedItem ? mSelectedItem->incidence() : 0 );
221 }
222 
223 
224 TQDate KOAgenda::selectedIncidenceDate() const
225 {
226  return ( mSelectedItem ? mSelectedItem->itemDate() : TQDate() );
227 }
228 
229 const TQString KOAgenda::lastSelectedUid() const
230 {
231  return mSelectedUid;
232 }
233 
234 
235 void KOAgenda::init()
236 {
237  mGridSpacingX = 100;
238  mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
239  if ( mDesiredGridSpacingY < 4 || mDesiredGridSpacingY > 30 ) {
240  mDesiredGridSpacingY = 10;
241  }
242 
243  // make sure that there are not more than 24 per day
244  mGridSpacingY = (double)height() / (double)mRows;
245  if ( mGridSpacingY < mDesiredGridSpacingY ) {
246  mGridSpacingY = mDesiredGridSpacingY;
247  }
248 
249  mResizeBorderWidth = 8;
250  mScrollBorderWidth = 8;
251  mScrollDelay = 30;
252  mScrollOffset = 10;
253 
254  enableClipper( true );
255 
256  // Grab key strokes for keyboard navigation of agenda. Seems to have no
257  // effect. Has to be fixed.
258  setFocusPolicy( TQWidget::WheelFocus );
259 
260  connect( &mScrollUpTimer, TQ_SIGNAL( timeout() ), TQ_SLOT( scrollUp() ) );
261  connect( &mScrollDownTimer, TQ_SIGNAL( timeout() ), TQ_SLOT( scrollDown() ) );
262 
263  mStartCell = TQPoint( 0, 0 );
264  mEndCell = TQPoint( 0, 0 );
265 
266  mHasSelection = false;
267  mSelectionStartPoint = TQPoint( 0, 0 );
268  mSelectionStartCell = TQPoint( 0, 0 );
269  mSelectionEndCell = TQPoint( 0, 0 );
270 
271  mOldLowerScrollValue = -1;
272  mOldUpperScrollValue = -1;
273 
274  mClickedItem = 0;
275 
276  mActionItem = 0;
277  mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), TQString() );
278  mActionType = NOP;
279  mItemMoved = false;
280 
281  mSelectedItem = 0;
282  mSelectedUid = TQString();
283 
284  setAcceptDrops( true );
285  installEventFilter( this );
286  mItems.setAutoDelete( true );
287  mItemsToDelete.setAutoDelete( true );
288 
289  resizeContents( int( mGridSpacingX * mColumns ),
290  int( mGridSpacingY * mRows ) );
291 
292  viewport()->update();
293  viewport()->setBackgroundMode( NoBackground );
294  viewport()->setFocusPolicy( TQWidget::WheelFocus );
295 
296  setMinimumSize( 30, int( mGridSpacingY + 1 ) );
297 // setMaximumHeight(mGridSpacingY * mRows + 5);
298 
299  // Disable horizontal scrollbar. This is a hack. The geometry should be
300  // controlled in a way that the contents horizontally always fits. Then it is
301  // not necessary to turn off the scrollbar.
302  setHScrollBarMode( AlwaysOff );
303 
304  setStartTime( KOPrefs::instance()->mDayBegins.time() );
305 
306  calculateWorkingHours();
307 
308  connect( verticalScrollBar(), TQ_SIGNAL( valueChanged( int ) ),
309  TQ_SLOT( checkScrollBoundaries( int ) ) );
310 
311  // Create the Marcus Bains line.
312  if( mAllDayMode ) {
313  mMarcusBains = 0;
314  } else {
315  mMarcusBains = new MarcusBains( this );
316  addChild( mMarcusBains );
317  }
318 
319  mTypeAhead = false;
320  mTypeAheadReceiver = 0;
321 
322  mReturnPressed = false;
323 }
324 
325 
326 void KOAgenda::clear()
327 {
328 // kdDebug(5850) << "KOAgenda::clear()" << endl;
329 
330  KOAgendaItem *item;
331  for ( item = mItems.first(); item != 0; item = mItems.next() ) {
332  removeChild( item );
333  }
334  mItems.clear();
335  mItemsToDelete.clear();
336 
337  mSelectedItem = 0;
338 
339  clearSelection();
340 }
341 
342 
343 void KOAgenda::clearSelection()
344 {
345  mHasSelection = false;
346  mActionType = NOP;
347  updateContents();
348 }
349 
350 void KOAgenda::marcus_bains()
351 {
352  if(mMarcusBains) mMarcusBains->updateLocationRecalc( true );
353 }
354 
355 
356 void KOAgenda::changeColumns(int columns)
357 {
358  if (columns == 0) {
359  kdDebug(5850) << "KOAgenda::changeColumns() called with argument 0" << endl;
360  return;
361  }
362 
363  clear();
364  mColumns = columns;
365 // setMinimumSize(mColumns * 10, mGridSpacingY + 1);
366 // init();
367 // update();
368 
369  TQResizeEvent event( size(), size() );
370 
371  TQApplication::sendEvent( this, &event );
372 }
373 
374 /*
375  This is the eventFilter function, which gets all events from the KOAgendaItems
376  contained in the agenda. It has to handle moving and resizing for all items.
377 */
378 bool KOAgenda::eventFilter ( TQObject *object, TQEvent *event )
379 {
380 // kdDebug(5850) << "KOAgenda::eventFilter() " << int( event->type() ) << endl;
381 
382  switch( event->type() ) {
383  case TQEvent::MouseButtonPress:
384  case TQEvent::MouseButtonDblClick:
385  case TQEvent::MouseButtonRelease:
386  case TQEvent::MouseMove:
387  return eventFilter_mouse( object, static_cast<TQMouseEvent*>( event ) );
388 #ifndef TQT_NO_WHEELEVENT
389  case TQEvent::Wheel:
390  return eventFilter_wheel( object, static_cast<TQWheelEvent*>( event ) );
391 #endif
392  case TQEvent::KeyPress:
393  case TQEvent::KeyRelease:
394  return eventFilter_key( object, static_cast<TQKeyEvent*>( event ) );
395 
396  case ( TQEvent::Leave ):
397  if ( !mActionItem )
398  setCursor( arrowCursor );
399  if ( object == viewport() )
400  emit leaveAgenda();
401  return true;
402 
403  case TQEvent::Enter:
404  emit enterAgenda();
405  return TQScrollView::eventFilter( object, event );
406 
407 #ifndef KORG_NODND
408  case TQEvent::DragEnter:
409  case TQEvent::DragMove:
410  case TQEvent::DragLeave:
411  case TQEvent::Drop:
412  // case TQEvent::DragResponse:
413  return eventFilter_drag(object, static_cast<TQDropEvent*>(event));
414 #endif
415 
416  default:
417  return TQScrollView::eventFilter( object, event );
418  }
419 }
420 
421 bool KOAgenda::eventFilter_drag( TQObject *object, TQDropEvent *de )
422 {
423 #ifndef KORG_NODND
424  TQPoint viewportPos;
425  if ( object != viewport() && object != this ) {
426  viewportPos = static_cast<TQWidget*>( object )->mapToParent( de->pos() );
427  } else {
428  viewportPos = de->pos();
429  }
430 
431  switch ( de->type() ) {
432  case TQEvent::DragEnter:
433  case TQEvent::DragMove:
434  if ( ICalDrag::canDecode( de ) || VCalDrag::canDecode( de ) ) {
435 
436  DndFactory factory( mCalendar );
437  Todo *todo = factory.createDropTodo( de );
438  if ( todo ) {
439  de->accept();
440  delete todo;
441  } else {
442  de->ignore();
443  }
444  return true;
445  } else return false;
446  break;
447  case TQEvent::DragLeave:
448  return false;
449  break;
450  case TQEvent::Drop:
451  {
452  if ( !ICalDrag::canDecode( de ) && !VCalDrag::canDecode( de ) ) {
453  return false;
454  }
455 
456  DndFactory factory( mCalendar );
457  Todo *todo = factory.createDropTodo( de );
458 
459  if ( todo ) {
460  de->acceptAction();
461  TQPoint pos;
462  // FIXME: This is a bad hack, as the viewportToContents seems to be off by
463  // 2000 (which is the left upper corner of the viewport). It works correctly
464  // for agendaItems.
465  if ( object == this ) {
466  pos = viewportPos + TQPoint( contentsX(), contentsY() );
467  } else {
468  pos = viewportToContents( viewportPos );
469  }
470  TQPoint gpos = contentsToGrid( pos );
471  emit droppedToDo( todo, gpos, mAllDayMode );
472  return true;
473  }
474  }
475  break;
476 
477  case TQEvent::DragResponse:
478  default:
479  break;
480  }
481 #endif
482 
483  return false;
484 }
485 
486 bool KOAgenda::eventFilter_key( TQObject *, TQKeyEvent *ke )
487 {
488  // kdDebug(5850) << "KOAgenda::eventFilter_key() " << ke->type() << endl;
489 
490  // If Return is pressed bring up an editor for the current selected time span.
491  if ( ke->key() == Key_Return ) {
492  if ( ke->type() == TQEvent::KeyPress ) mReturnPressed = true;
493  else if ( ke->type() == TQEvent::KeyRelease ) {
494  if ( mReturnPressed ) {
495  emitNewEventForSelection();
496  mReturnPressed = false;
497  return true;
498  } else {
499  mReturnPressed = false;
500  }
501  }
502  }
503 
504  // Ignore all input that does not produce any output
505  if ( ke->text().isEmpty() ) return false;
506 
507  if ( ke->type() == TQEvent::KeyPress || ke->type() == TQEvent::KeyRelease ) {
508  switch ( ke->key() ) {
509  case Key_Escape:
510  case Key_Return:
511  case Key_Enter:
512  case Key_Tab:
513  case Key_Backtab:
514  case Key_Left:
515  case Key_Right:
516  case Key_Up:
517  case Key_Down:
518  case Key_Backspace:
519  case Key_Delete:
520  case Key_Prior:
521  case Key_Next:
522  case Key_Home:
523  case Key_End:
524  case Key_Control:
525  case Key_Meta:
526  case Key_Alt:
527  break;
528  default:
529  mTypeAheadEvents.append( new TQKeyEvent( ke->type(), ke->key(),
530  ke->ascii(), ke->state(),
531  ke->text(), ke->isAutoRepeat(),
532  ke->count() ) );
533  if ( !mTypeAhead ) {
534  mTypeAhead = true;
535  emitNewEventForSelection();
536  }
537  return true;
538  }
539  }
540  return false;
541 }
542 
543 void KOAgenda::emitNewEventForSelection()
544 {
545  TQPair<ResourceCalendar *, TQString>p = mCalendarView->viewSubResourceCalendar();
546  emit newEventSignal( p.first, p.second );
547 }
548 
549 void KOAgenda::finishTypeAhead()
550 {
551 // kdDebug(5850) << "KOAgenda::finishTypeAhead()" << endl;
552  if ( typeAheadReceiver() ) {
553  for( TQEvent *e = mTypeAheadEvents.first(); e;
554  e = mTypeAheadEvents.next() ) {
555 // kdDebug(5850) << "sendEvent() " << int( typeAheadReceiver() ) << endl;
556  TQApplication::sendEvent( typeAheadReceiver(), e );
557  }
558  }
559  mTypeAheadEvents.clear();
560  mTypeAhead = false;
561 }
562 #ifndef TQT_NO_WHEELEVENT
563 bool KOAgenda::eventFilter_wheel ( TQObject *object, TQWheelEvent *e )
564 {
565  TQPoint viewportPos;
566  bool accepted=false;
567  if ( ( e->state() & ShiftButton) == ShiftButton ) {
568  if ( object != viewport() ) {
569  viewportPos = ( (TQWidget *) object )->mapToParent( e->pos() );
570  } else {
571  viewportPos = e->pos();
572  }
573  //kdDebug(5850)<< "KOAgenda::eventFilter_wheel: type:"<<
574  // e->type()<<" delta: "<< e->delta()<< endl;
575  emit zoomView( -e->delta() ,
576  contentsToGrid( viewportToContents( viewportPos ) ),
577  TQt::Horizontal );
578  accepted=true;
579  }
580 
581  if ( ( e->state() & ControlButton ) == ControlButton ){
582  if ( object != viewport() ) {
583  viewportPos = ( (TQWidget *)object )->mapToParent( e->pos() );
584  } else {
585  viewportPos = e->pos();
586  }
587  emit zoomView( -e->delta() ,
588  contentsToGrid( viewportToContents( viewportPos ) ),
589  TQt::Vertical );
590  emit mousePosSignal(gridToContents(contentsToGrid(viewportToContents( viewportPos ))));
591  accepted=true;
592  }
593  if (accepted ) e->accept();
594  return accepted;
595 }
596 #endif
597 bool KOAgenda::eventFilter_mouse(TQObject *object, TQMouseEvent *me)
598 {
599  TQPoint viewportPos;
600  if (object != viewport()) {
601  viewportPos = ((TQWidget *)object)->mapToParent(me->pos());
602  } else {
603  viewportPos = me->pos();
604  }
605 
606  switch (me->type()) {
607  case TQEvent::MouseButtonPress:
608 // kdDebug(5850) << "koagenda: filtered button press" << endl;
609  if (object != viewport()) {
610  if (me->button() == TQt::RightButton) {
611  mClickedItem = dynamic_cast<KOAgendaItem *>(object);
612  if (mClickedItem) {
613  selectItem(mClickedItem);
614  emit showIncidencePopupSignal( mCalendar,
615  mClickedItem->incidence(),
616  mClickedItem->itemDate() );
617  } else {
618  return TQScrollView::eventFilter( object, me ); // pass through for use by multiagenda
619  }
620  } else {
621  KOAgendaItem* item = dynamic_cast<KOAgendaItem *>(object);
622  if (item) {
623  Incidence *incidence = item->incidence();
624  if ( incidence->isReadOnly() ) {
625  mActionItem = 0;
626  mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), TQString() );
627  } else {
628  mActionItem = item;
629  mResPair = CalHelper::incSubResourceCalendar( mCalendar, incidence );
630  startItemAction(viewportPos);
631  }
632  // Warning: do selectItem() as late as possible, since all
633  // sorts of things happen during this call. Some can lead to
634  // this filter being run again and mActionItem being set to
635  // null.
636  selectItem( item );
637  } else {
638  return TQScrollView::eventFilter( object, me ); // pass through for use by multiagenda
639  }
640  }
641  } else {
642  if ( me->button() == TQt::RightButton ) {
643  // if mouse pointer is not in selection, select the cell below the cursor
644  TQPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
645  if ( !ptInSelection( gpos ) ) {
646  mSelectionStartCell = gpos;
647  mSelectionEndCell = gpos;
648  mHasSelection = true;
649  emit newStartSelectSignal();
650  emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
651  updateContents();
652  }
653  showNewEventPopupSignal();
654  } else {
655  // if mouse pointer is in selection, don't change selection
656  TQPoint gpos = contentsToGrid( viewportToContents( viewportPos ) );
657  if ( !ptInSelection( gpos ) ) {
658  selectItem(0);
659  mActionItem = 0;
660  mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), TQString() );
661  setCursor(arrowCursor);
662  startSelectAction(viewportPos);
663  }
664  }
665  return TQScrollView::eventFilter( object, me ); // pass through for use by multiagenda
666  }
667  break;
668 
669  case TQEvent::MouseButtonRelease:
670  if (mActionItem) {
671  endItemAction();
672  } else if ( mActionType == SELECT ) {
673  endSelectAction( viewportPos );
674  }
675  // This nasty gridToContents(contentsToGrid(..)) is needed to
676  // avoid an offset of a few pixels. Don't ask me why...
677  emit mousePosSignal( gridToContents(contentsToGrid(
678  viewportToContents( viewportPos ) ) ));
679  break;
680 
681  case TQEvent::MouseMove: {
682  // This nasty gridToContents(contentsToGrid(..)) is needed to
683  // avoid an offset of a few pixels. Don't ask me why...
684  TQPoint indicatorPos = gridToContents(contentsToGrid(
685  viewportToContents( viewportPos )));
686  if (object != viewport()) {
687  KOAgendaItem *moveItem = dynamic_cast<KOAgendaItem *>(object);
688  if (moveItem && !moveItem->incidence()->isReadOnly() ) {
689  if (!mActionItem)
690  setNoActionCursor(moveItem,viewportPos);
691  else {
692  performItemAction(viewportPos);
693 
694  if ( mActionType == MOVE ) {
695  // show cursor at the current begin of the item
696  KOAgendaItem *firstItem = mActionItem->firstMultiItem();
697  if (!firstItem) firstItem = mActionItem;
698  indicatorPos = gridToContents( TQPoint( firstItem->cellXLeft(),
699  firstItem->cellYTop() ) );
700 
701  } else if ( mActionType == RESIZEBOTTOM ) {
702  // RESIZETOP is handled correctly, only resizebottom works differently
703  indicatorPos = gridToContents( TQPoint( mActionItem->cellXLeft(),
704  mActionItem->cellYBottom()+1 ) );
705  }
706 
707  } // If we have an action item
708  } // If move item && !read only
709  } else {
710  if ( mActionType == SELECT ) {
711  performSelectAction( viewportPos );
712 
713  // show cursor at end of timespan
714  if ( ((mStartCell.y() < mEndCell.y()) && (mEndCell.x() >= mStartCell.x())) ||
715  (mEndCell.x() > mStartCell.x()) )
716  indicatorPos = gridToContents( TQPoint(mEndCell.x(), mEndCell.y()+1) );
717  else
718  indicatorPos = gridToContents( mEndCell );
719  }
720  }
721  emit mousePosSignal( indicatorPos );
722  break; }
723 
724  case TQEvent::MouseButtonDblClick:
725  if (object == viewport()) {
726  selectItem(0);
727  TQPair<ResourceCalendar *, TQString>p = mCalendarView->viewSubResourceCalendar();
728  emit newEventSignal( p.first, p.second );
729  } else {
730  KOAgendaItem *doubleClickedItem = dynamic_cast<KOAgendaItem *>( object );
731  if ( doubleClickedItem ) {
732  selectItem( doubleClickedItem );
733  emit editIncidenceSignal( doubleClickedItem->incidence(), doubleClickedItem->itemDate() );
734  }
735  }
736  break;
737 
738  default:
739  break;
740  }
741 
742  return true;
743 }
744 
745 bool KOAgenda::ptInSelection( TQPoint gpos ) const
746 {
747  if ( !mHasSelection ) {
748  return false;
749  } else if ( gpos.x()<mSelectionStartCell.x() || gpos.x()>mSelectionEndCell.x() ) {
750  return false;
751  } else if ( (gpos.x()==mSelectionStartCell.x()) && (gpos.y()<mSelectionStartCell.y()) ) {
752  return false;
753  } else if ( (gpos.x()==mSelectionEndCell.x()) && (gpos.y()>mSelectionEndCell.y()) ) {
754  return false;
755  }
756  return true;
757 }
758 
759 void KOAgenda::startSelectAction( const TQPoint &viewportPos )
760 {
761  emit newStartSelectSignal();
762 
763  mActionType = SELECT;
764  mSelectionStartPoint = viewportPos;
765  mHasSelection = true;
766 
767  TQPoint pos = viewportToContents( viewportPos );
768  TQPoint gpos = contentsToGrid( pos );
769 
770  // Store new selection
771  mStartCell = gpos;
772  mEndCell = gpos;
773  mSelectionStartCell = gpos;
774  mSelectionEndCell = gpos;
775 
776  updateContents();
777 }
778 
779 void KOAgenda::performSelectAction(const TQPoint& viewportPos)
780 {
781  TQPoint pos = viewportToContents( viewportPos );
782  TQPoint gpos = contentsToGrid( pos );
783 
784  TQPoint clipperPos = clipper()->
785  mapFromGlobal(viewport()->mapToGlobal(viewportPos));
786 
787  // Scroll if cursor was moved to upper or lower end of agenda.
788  if (clipperPos.y() < mScrollBorderWidth) {
789  mScrollUpTimer.start(mScrollDelay);
790  } else if (visibleHeight() - clipperPos.y() <
791  mScrollBorderWidth) {
792  mScrollDownTimer.start(mScrollDelay);
793  } else {
794  mScrollUpTimer.stop();
795  mScrollDownTimer.stop();
796  }
797 
798  if ( gpos != mEndCell ) {
799  mEndCell = gpos;
800  if ( mStartCell.x()>mEndCell.x() ||
801  ( mStartCell.x()==mEndCell.x() && mStartCell.y()>mEndCell.y() ) ) {
802  // backward selection
803  mSelectionStartCell = mEndCell;
804  mSelectionEndCell = mStartCell;
805  } else {
806  mSelectionStartCell = mStartCell;
807  mSelectionEndCell = mEndCell;
808  }
809 
810  updateContents();
811  }
812 }
813 
814 void KOAgenda::endSelectAction( const TQPoint &currentPos )
815 {
816  mScrollUpTimer.stop();
817  mScrollDownTimer.stop();
818 
819  mActionType = NOP;
820 
821  emit newTimeSpanSignal( mSelectionStartCell, mSelectionEndCell );
822 
823  if ( KOPrefs::instance()->mSelectionStartsEditor ) {
824  if ( ( mSelectionStartPoint - currentPos ).manhattanLength() >
825  TQApplication::startDragDistance() ) {
826  emitNewEventForSelection();
827  }
828  }
829 }
830 
831 KOAgenda::MouseActionType KOAgenda::isInResizeArea( bool horizontal,
832  const TQPoint &pos, KOAgendaItem*item )
833 {
834  if (!item) return NOP;
835  TQPoint gridpos = contentsToGrid( pos );
836  TQPoint contpos = gridToContents( gridpos +
837  TQPoint( (KOGlobals::self()->reverseLayout())?1:0, 0 ) );
838 
839 //kdDebug(5850)<<"contpos="<<contpos<<", pos="<<pos<<", gpos="<<gpos<<endl;
840 //kdDebug(5850)<<"clXLeft="<<clXLeft<<", clXRight="<<clXRight<<endl;
841 
842  if ( horizontal ) {
843  int clXLeft = item->cellXLeft();
844  int clXRight = item->cellXRight();
845  if ( KOGlobals::self()->reverseLayout() ) {
846  int tmp = clXLeft;
847  clXLeft = clXRight;
848  clXRight = tmp;
849  }
850  int gridDistanceX = int( pos.x() - contpos.x() );
851  if (gridDistanceX < mResizeBorderWidth && clXLeft == gridpos.x() ) {
852  if ( KOGlobals::self()->reverseLayout() ) return RESIZERIGHT;
853  else return RESIZELEFT;
854  } else if ((mGridSpacingX - gridDistanceX) < mResizeBorderWidth &&
855  clXRight == gridpos.x() ) {
856  if ( KOGlobals::self()->reverseLayout() ) return RESIZELEFT;
857  else return RESIZERIGHT;
858  } else {
859  return MOVE;
860  }
861  } else {
862  int gridDistanceY = int( pos.y() - contpos.y() );
863  if (gridDistanceY < mResizeBorderWidth &&
864  item->cellYTop() == gridpos.y() &&
865  !item->firstMultiItem() ) {
866  return RESIZETOP;
867  } else if ((mGridSpacingY - gridDistanceY) < mResizeBorderWidth &&
868  item->cellYBottom() == gridpos.y() &&
869  !item->lastMultiItem() ) {
870  return RESIZEBOTTOM;
871  } else {
872  return MOVE;
873  }
874  }
875 }
876 
877 void KOAgenda::startItemAction(const TQPoint& viewportPos)
878 {
879  TQPoint pos = viewportToContents( viewportPos );
880  mStartCell = contentsToGrid( pos );
881  mEndCell = mStartCell;
882 
883  bool noResize = ( mActionItem->incidence()->type() == "Todo");
884 
885  mActionType = MOVE;
886  if ( !noResize ) {
887  mActionType = isInResizeArea( mAllDayMode, pos, mActionItem );
888  }
889 
890 
891  mActionItem->startMove();
892  setActionCursor( mActionType, true );
893 }
894 
895 void KOAgenda::performItemAction(const TQPoint& viewportPos)
896 {
897 // kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
898 // TQPoint point = viewport()->mapToGlobal(viewportPos);
899 // kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
900 // point = clipper()->mapFromGlobal(point);
901 // kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
902 // kdDebug(5850) << "visible height: " << visibleHeight() << endl;
903  TQPoint pos = viewportToContents( viewportPos );
904 // kdDebug(5850) << "contents: " << x << "," << y << "\n" << endl;
905  TQPoint gpos = contentsToGrid( pos );
906  TQPoint clipperPos = clipper()->
907  mapFromGlobal(viewport()->mapToGlobal(viewportPos));
908 
909  // Cursor left active agenda area.
910  // This starts a drag.
911  if ( clipperPos.y() < 0 || clipperPos.y() > visibleHeight() ||
912  clipperPos.x() < 0 || clipperPos.x() > visibleWidth() ) {
913  if ( mActionType == MOVE ) {
914  mScrollUpTimer.stop();
915  mScrollDownTimer.stop();
916  mActionItem->resetMove();
917  placeSubCells( mActionItem );
918  emit startDragSignal( mActionItem->incidence() );
919  setCursor( arrowCursor );
920  mActionItem = 0;
921  mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), TQString() );
922  mActionType = NOP;
923  mItemMoved = false;
924  return;
925  }
926  } else {
927  setActionCursor( mActionType );
928  }
929 
930  // Scroll if item was moved to upper or lower end of agenda.
931  if (clipperPos.y() < mScrollBorderWidth) {
932  mScrollUpTimer.start(mScrollDelay);
933  } else if (visibleHeight() - clipperPos.y() <
934  mScrollBorderWidth) {
935  mScrollDownTimer.start(mScrollDelay);
936  } else {
937  mScrollUpTimer.stop();
938  mScrollDownTimer.stop();
939  }
940 
941  // Move or resize item if necessary
942  if ( mEndCell != gpos ) {
943  if ( !mItemMoved ) {
944  if ( !mChanger ||
945  !mChanger->beginChange( mActionItem->incidence(), mResPair.first, mResPair.second ) ) {
946  KMessageBox::information( this, i18n("Unable to lock item for "
947  "modification. You cannot make any changes."),
948  i18n("Locking Failed"), "AgendaLockingFailed" );
949  mScrollUpTimer.stop();
950  mScrollDownTimer.stop();
951  mActionItem->resetMove();
952  placeSubCells( mActionItem );
953  setCursor( arrowCursor );
954  mActionItem = 0;
955  mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), TQString() );
956  mActionType = NOP;
957  mItemMoved = false;
958  return;
959  }
960  mItemMoved = true;
961  }
962  mActionItem->raise();
963  if (mActionType == MOVE) {
964  // Move all items belonging to a multi item
965  KOAgendaItem *firstItem = mActionItem->firstMultiItem();
966  if (!firstItem) firstItem = mActionItem;
967  KOAgendaItem *lastItem = mActionItem->lastMultiItem();
968  if (!lastItem) lastItem = mActionItem;
969  TQPoint deltapos = gpos - mEndCell;
970  KOAgendaItem *moveItem = firstItem;
971  while (moveItem) {
972  bool changed=false;
973  if ( deltapos.x()!=0 ) {
974  moveItem->moveRelative( deltapos.x(), 0 );
975  changed=true;
976  }
977  // in agenda's all day view don't try to move multi items, since there are none
978  if ( moveItem==firstItem && !mAllDayMode ) { // is the first item
979  int newY = deltapos.y() + moveItem->cellYTop();
980  // If event start moved earlier than 0:00, it starts the previous day
981  if ( newY<0 ) {
982  moveItem->expandTop( -moveItem->cellYTop() );
983  // prepend a new item at ( x-1, rows()+newY to rows() )
984  KOAgendaItem *newFirst = firstItem->prevMoveItem();
985  // cell's y values are first and last cell of the bar, so if newY=-1, they need to be the same
986  if (newFirst) {
987  newFirst->setCellXY(moveItem->cellXLeft()-1, rows()+newY, rows()-1);
988  mItems.append( newFirst );
989  moveItem->resize( int( mGridSpacingX * newFirst->cellWidth() ),
990  int( mGridSpacingY * newFirst->cellHeight() ));
991  TQPoint cpos = gridToContents( TQPoint( newFirst->cellXLeft(), newFirst->cellYTop() ) );
992  addChild( newFirst, cpos.x(), cpos.y() );
993  } else {
994  newFirst = insertItem( moveItem->incidence(), moveItem->itemDate(),
995  moveItem->cellXLeft()-1, rows()+newY, rows()-1, moveItem->itemPos(), moveItem->itemCount() ) ;
996  }
997  if (newFirst) newFirst->show();
998  moveItem->prependMoveItem(newFirst);
999  firstItem=newFirst;
1000  } else if ( newY>=rows() ) {
1001  // If event start is moved past 24:00, it starts the next day
1002  // erase current item (i.e. remove it from the multiItem list)
1003  firstItem = moveItem->nextMultiItem();
1004  moveItem->hide();
1005  mItems.take( mItems.find( moveItem ) );
1006  removeChild( moveItem );
1007  mActionItem->removeMoveItem(moveItem);
1008  moveItem=firstItem;
1009  // adjust next day's item
1010  if (moveItem) moveItem->expandTop( rows()-newY );
1011  } else {
1012  moveItem->expandTop(deltapos.y());
1013  }
1014  changed=true;
1015  }
1016  if ( !moveItem->lastMultiItem() && !mAllDayMode ) { // is the last item
1017  int newY = deltapos.y()+moveItem->cellYBottom();
1018  if (newY<0) {
1019  // erase current item
1020  lastItem = moveItem->prevMultiItem();
1021  moveItem->hide();
1022  mItems.take( mItems.find(moveItem) );
1023  removeChild( moveItem );
1024  moveItem->removeMoveItem( moveItem );
1025  moveItem = lastItem;
1026  moveItem->expandBottom(newY+1);
1027  } else if (newY>=rows()) {
1028  moveItem->expandBottom( rows()-moveItem->cellYBottom()-1 );
1029  // append item at ( x+1, 0 to newY-rows() )
1030  KOAgendaItem *newLast = lastItem->nextMoveItem();
1031  if (newLast) {
1032  newLast->setCellXY( moveItem->cellXLeft()+1, 0, newY-rows()-1 );
1033  mItems.append(newLast);
1034  moveItem->resize( int( mGridSpacingX * newLast->cellWidth() ),
1035  int( mGridSpacingY * newLast->cellHeight() ));
1036  TQPoint cpos = gridToContents( TQPoint( newLast->cellXLeft(), newLast->cellYTop() ) ) ;
1037  addChild( newLast, cpos.x(), cpos.y() );
1038  } else {
1039  newLast = insertItem( moveItem->incidence(), moveItem->itemDate(),
1040  moveItem->cellXLeft()+1, 0, newY-rows()-1, moveItem->itemPos(), moveItem->itemCount() ) ;
1041  }
1042  moveItem->appendMoveItem( newLast );
1043  newLast->show();
1044  lastItem = newLast;
1045  } else {
1046  moveItem->expandBottom( deltapos.y() );
1047  }
1048  changed=true;
1049  }
1050  if (changed) {
1051  adjustItemPosition( moveItem );
1052  }
1053  moveItem = moveItem->nextMultiItem();
1054  }
1055  } else if (mActionType == RESIZETOP) {
1056  if (mEndCell.y() <= mActionItem->cellYBottom()) {
1057  mActionItem->expandTop(gpos.y() - mEndCell.y());
1058  adjustItemPosition( mActionItem );
1059  }
1060  } else if (mActionType == RESIZEBOTTOM) {
1061  if (mEndCell.y() >= mActionItem->cellYTop()) {
1062  mActionItem->expandBottom(gpos.y() - mEndCell.y());
1063  adjustItemPosition( mActionItem );
1064  }
1065  } else if (mActionType == RESIZELEFT) {
1066  if (mEndCell.x() <= mActionItem->cellXRight()) {
1067  mActionItem->expandLeft( gpos.x() - mEndCell.x() );
1068  adjustItemPosition( mActionItem );
1069  }
1070  } else if (mActionType == RESIZERIGHT) {
1071  if (mEndCell.x() >= mActionItem->cellXLeft()) {
1072  mActionItem->expandRight(gpos.x() - mEndCell.x());
1073  adjustItemPosition( mActionItem );
1074  }
1075  }
1076  mEndCell = gpos;
1077  }
1078 }
1079 
1080 void KOAgenda::endItemAction()
1081 {
1082 // kdDebug(5850) << "KOAgenda::endItemAction() " << endl;
1083  mActionType = NOP;
1084  mScrollUpTimer.stop();
1085  mScrollDownTimer.stop();
1086  setCursor( arrowCursor );
1087  bool multiModify = false;
1088  // FIXME: do the cloning here...
1089  Incidence* inc = mActionItem->incidence();
1090 
1091  if ( mStartCell.x() == mEndCell.x() && mStartCell.y() == mEndCell.y() ) {
1092  // not really moved, so stop any change
1093  if ( mItemMoved ) {
1094  mItemMoved = false;
1095  mChanger->endChange( inc, mResPair.first, mResPair.second );
1096  }
1097  }
1098 
1099  if ( mItemMoved ) {
1100  Incidence *incToChange = inc;
1101  if ( mActionItem->incidence()->doesRecur() ) {
1102  Incidence* oldIncSaved = inc->clone();
1103  KOGlobals::WhichOccurrences chosenOption;
1104  incToChange = mCalendarView->singleOccurrenceOrAll( inc,
1105  KOGlobals::EDIT,
1106  chosenOption,
1107  mActionItem->itemDate() );
1108 
1109  if ( chosenOption == KOGlobals::ONLY_THIS_ONE ||
1110  chosenOption == KOGlobals::ONLY_FUTURE ) {
1111 
1112  // FIXME Prompt for this...it is quite possible that the user does not want to broadcast the change
1113  // That prompting dialog will require the ability to suppress/override the mChanger->endChange GroupWare communication though.
1114  int autoAnswerGroupWare = 1; // Send all possible GroupWare messages without prompting
1115 
1116  // Store modification information in case it is needed to recreate the changes with a new actionitem...
1117  int mai_xl = mActionItem->cellXLeft();
1118  int mai_xr = mActionItem->cellXRight();
1119  int mai_yt = mActionItem->cellYTop();
1120  int mai_yb = mActionItem->cellYBottom();
1121 
1122  multiModify = true;
1123  emit startMultiModify( i18n("Dissociate event from recurrence") );
1124  enableAgendaUpdate( false );
1125 
1126  mChanger->addIncidence( incToChange, mResPair.first, mResPair.second, this, autoAnswerGroupWare );
1127  enableAgendaUpdate( true );
1128  KOGlobals::WhatChanged wc = chosenOption == KOGlobals::ONLY_THIS_ONE ?
1129  KOGlobals::RECURRENCE_MODIFIED_ONE_ONLY :
1130  KOGlobals::RECURRENCE_MODIFIED_ALL_FUTURE;
1131 
1132  mChanger->changeIncidence( oldIncSaved, inc, wc, this, autoAnswerGroupWare );
1133 
1134  // mActionItem does not exist any more, seeing as we just got done deleting it
1135  // (by deleting/replacing the original incidence it was created from through
1136  // user modification of said incidence) above!
1137  // Therefore we have to find the new KOAgendaItem that matches the new incidence
1138  // Then we can apply the saved X/Y settings from the original move operation as shown.
1139 
1140  KOAgendaItem *koai_insertedItem;
1141  for ( koai_insertedItem = mItems.first(); koai_insertedItem; koai_insertedItem = mItems.next() ) {
1142  if (koai_insertedItem->incidence() == incToChange) {
1143  selectItem( koai_insertedItem );
1144  mSelectedItem->startMove();
1145  mSelectedItem->setCellY(mai_yt, mai_yb);
1146  mSelectedItem->setCellX(mai_xl, mai_xr);
1147  mActionItem = mSelectedItem;
1148  //mSelectedItem->endMove();
1149  break;
1150  }
1151  }
1152 
1153  mActionItem->dissociateFromMultiItem();
1154  mActionItem->setIncidence( incToChange );
1155  }
1156  }
1157 
1158  if ( incToChange ) {
1159  mActionItem->endMove();
1160  KOAgendaItem *placeItem = mActionItem->firstMultiItem();
1161  if ( !placeItem ) {
1162  placeItem = mActionItem;
1163  }
1164 
1165  KOAgendaItem *modif = placeItem;
1166 
1167  TQPtrList<KOAgendaItem> oldconflictItems = placeItem->conflictItems();
1168  KOAgendaItem *item;
1169  for ( item = oldconflictItems.first(); item != 0;
1170  item = oldconflictItems.next() ) {
1171  placeSubCells( item );
1172  }
1173  while ( placeItem ) {
1174  placeSubCells( placeItem );
1175  placeItem = placeItem->nextMultiItem();
1176  }
1177 
1178  // Notify about change
1179  // the agenda view will apply the changes to the actual Incidence*!
1180  mChanger->endChange( inc, mResPair.first, mResPair.second );
1181  emit itemModified( modif );
1182  } else {
1183 
1184  mActionItem->resetMove();
1185  placeSubCells( mActionItem );
1186 
1187  // the item was moved, but not further modified, since it's not recurring
1188  // make sure the view updates anyhow, with the right item
1189  mChanger->endChange( inc, mResPair.first, mResPair.second );
1190  emit itemModified( mActionItem );
1191  }
1192  }
1193 
1194  mActionItem = 0;
1195  mResPair = qMakePair( static_cast<ResourceCalendar *>( 0 ), TQString() );
1196  mItemMoved = false;
1197 
1198  if ( multiModify ) {
1199  emit endMultiModify();
1200  }
1201 
1202  kdDebug(5850) << "KOAgenda::endItemAction() done" << endl;
1203 }
1204 
1205 void KOAgenda::setActionCursor( int actionType, bool acting )
1206 {
1207  switch ( actionType ) {
1208  case MOVE:
1209  if (acting) setCursor( sizeAllCursor );
1210  else setCursor( arrowCursor );
1211  break;
1212  case RESIZETOP:
1213  case RESIZEBOTTOM:
1214  setCursor( sizeVerCursor );
1215  break;
1216  case RESIZELEFT:
1217  case RESIZERIGHT:
1218  setCursor( sizeHorCursor );
1219  break;
1220  default:
1221  setCursor( arrowCursor );
1222  }
1223 }
1224 
1225 void KOAgenda::setNoActionCursor( KOAgendaItem *moveItem, const TQPoint& viewportPos )
1226 {
1227 // kdDebug(5850) << "viewportPos: " << viewportPos.x() << "," << viewportPos.y() << endl;
1228 // TQPoint point = viewport()->mapToGlobal(viewportPos);
1229 // kdDebug(5850) << "Global: " << point.x() << "," << point.y() << endl;
1230 // point = clipper()->mapFromGlobal(point);
1231 // kdDebug(5850) << "clipper: " << point.x() << "," << point.y() << endl;
1232 
1233  TQPoint pos = viewportToContents( viewportPos );
1234  bool noResize = (moveItem && moveItem->incidence() &&
1235  moveItem->incidence()->type() == "Todo");
1236 
1237  KOAgenda::MouseActionType resizeType = MOVE;
1238  if ( !noResize ) resizeType = isInResizeArea( mAllDayMode, pos , moveItem);
1239  setActionCursor( resizeType );
1240 }
1241 
1242 
1245 double KOAgenda::calcSubCellWidth( KOAgendaItem *item )
1246 {
1247  TQPoint pt, pt1;
1248  pt = gridToContents( TQPoint( item->cellXLeft(), item->cellYTop() ) );
1249  pt1 = gridToContents( TQPoint( item->cellXLeft(), item->cellYTop() ) +
1250  TQPoint( 1, 1 ) );
1251  pt1 -= pt;
1252  int maxSubCells = item->subCells();
1253  double newSubCellWidth;
1254  if ( mAllDayMode ) {
1255  newSubCellWidth = double( pt1.y() ) / maxSubCells;
1256  } else {
1257  newSubCellWidth = double( pt1.x() ) / maxSubCells;
1258  }
1259  return newSubCellWidth;
1260 }
1261 
1262 void KOAgenda::adjustItemPosition( KOAgendaItem *item )
1263 {
1264  if (!item) return;
1265  item->resize( int( mGridSpacingX * item->cellWidth() ),
1266  int( mGridSpacingY * item->cellHeight() ) );
1267  int clXLeft = item->cellXLeft();
1268  if ( KOGlobals::self()->reverseLayout() )
1269  clXLeft = item->cellXRight() + 1;
1270  TQPoint cpos = gridToContents( TQPoint( clXLeft, item->cellYTop() ) );
1271  moveChild( item, cpos.x(), cpos.y() );
1272 }
1273 
1274 void KOAgenda::placeAgendaItem( KOAgendaItem *item, double subCellWidth )
1275 {
1276 // kdDebug(5850) << "KOAgenda::placeAgendaItem(): " << item->incidence()->summary()
1277 // << " subCellWidth: " << subCellWidth << endl;
1278 
1279  // "left" upper corner, no subcells yet, RTL layouts have right/left switched, widths are negative then
1280  TQPoint pt = gridToContents( TQPoint( item->cellXLeft(), item->cellYTop() ) );
1281  // right lower corner
1282  TQPoint pt1 = gridToContents( TQPoint( item->cellXLeft() + item->cellWidth(),
1283  item->cellYBottom()+1 ) );
1284 
1285  double subCellPos = item->subCell() * subCellWidth;
1286 
1287  // we need to add 0.01 to make sure we don't loose one pixed due to
1288  // numerics (i.e. if it would be x.9998, we want the integer, not rounded down.
1289  double delta=0.01;
1290  if (subCellWidth<0) delta=-delta;
1291  int height, width, xpos, ypos;
1292  if (mAllDayMode) {
1293  width = pt1.x()-pt.x();
1294  height = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
1295  xpos = pt.x();
1296  ypos = pt.y() + int( subCellPos );
1297  } else {
1298  width = int( subCellPos + subCellWidth + delta ) - int( subCellPos );
1299  height = pt1.y()-pt.y();
1300  xpos = pt.x() + int( subCellPos );
1301  ypos = pt.y();
1302  }
1303  if ( KOGlobals::self()->reverseLayout() ) { // RTL language/layout
1304  xpos += width;
1305  width = -width;
1306  }
1307  if ( height<0 ) { // BTT (bottom-to-top) layout ?!?
1308  ypos += height;
1309  height = -height;
1310  }
1311  item->resize( width, height );
1312  moveChild( item, xpos, ypos );
1313 }
1314 
1315 /*
1316  Place item in cell and take care that multiple items using the same cell do
1317  not overlap. This method is not yet optimal. It doesn't use the maximum space
1318  it can get in all cases.
1319  At the moment the method has a bug: When an item is placed only the sub cell
1320  widths of the items are changed, which are within the Y region the item to
1321  place spans. When the sub cell width change of one of this items affects a
1322  cell, where other items are, which do not overlap in Y with the item to place,
1323  the display gets corrupted, although the corruption looks quite nice.
1324 */
1325 void KOAgenda::placeSubCells( KOAgendaItem *placeItem )
1326 {
1327 #if 0
1328  kdDebug(5850) << "KOAgenda::placeSubCells()" << endl;
1329  if ( placeItem ) {
1330  Incidence *event = placeItem->incidence();
1331  if ( !event ) {
1332  kdDebug(5850) << " event is 0" << endl;
1333  } else {
1334  kdDebug(5850) << " event: " << event->summary() << endl;
1335  }
1336  } else {
1337  kdDebug(5850) << " placeItem is 0" << endl;
1338  }
1339  kdDebug(5850) << "KOAgenda::placeSubCells()..." << endl;
1340 #endif
1341 
1342  TQPtrList<KOrg::CellItem> cells;
1343  KOAgendaItem *item;
1344  for ( item = mItems.first(); item != 0; item = mItems.next() ) {
1345  cells.append( item );
1346  }
1347 
1348  TQPtrList<KOrg::CellItem> items = KOrg::CellItem::placeItem( cells,
1349  placeItem );
1350 
1351  placeItem->setConflictItems( TQPtrList<KOAgendaItem>() );
1352  double newSubCellWidth = calcSubCellWidth( placeItem );
1353  KOrg::CellItem *i;
1354  for ( i = items.first(); i; i = items.next() ) {
1355  item = static_cast<KOAgendaItem *>( i );
1356  placeAgendaItem( item, newSubCellWidth );
1357  item->addConflictItem( placeItem );
1358  placeItem->addConflictItem( item );
1359  }
1360  if ( items.isEmpty() ) {
1361  placeAgendaItem( placeItem, newSubCellWidth );
1362  }
1363  placeItem->update();
1364 }
1365 
1366 int KOAgenda::columnWidth( int column )
1367 {
1368  int start = gridToContents( TQPoint( column, 0 ) ).x();
1369  if (KOGlobals::self()->reverseLayout() )
1370  column--;
1371  else
1372  column++;
1373  int end = gridToContents( TQPoint( column, 0 ) ).x();
1374  return end - start;
1375 }
1376 /*
1377  Draw grid in the background of the agenda.
1378 */
1379 void KOAgenda::drawContents(TQPainter* p, int cx, int cy, int cw, int ch)
1380 {
1381  TQPixmap db(cw, ch);
1382  db.fill(KOPrefs::instance()->mAgendaBgColor);
1383  TQPainter dbp(&db);
1384  dbp.translate(-cx,-cy);
1385 
1386 // kdDebug(5850) << "KOAgenda::drawContents()" << endl;
1387  double lGridSpacingY = mGridSpacingY*2;
1388 
1389  // Highlight working hours
1390  if (mWorkingHoursEnable) {
1391  TQPoint pt1( cx, mWorkingHoursYTop );
1392  TQPoint pt2( cx+cw, mWorkingHoursYBottom );
1393  if ( pt2.x() >= pt1.x() /*&& pt2.y() >= pt1.y()*/) {
1394  int gxStart = contentsToGrid( pt1 ).x();
1395  int gxEnd = contentsToGrid( pt2 ).x();
1396  // correct start/end for rtl layouts
1397  if ( gxStart > gxEnd ) {
1398  int tmp = gxStart;
1399  gxStart = gxEnd;
1400  gxEnd = tmp;
1401  }
1402  int xoffset = ( KOGlobals::self()->reverseLayout()?1:0 );
1403  while( gxStart <= gxEnd ) {
1404  int xStart = gridToContents( TQPoint( gxStart+xoffset, 0 ) ).x();
1405  int xWidth = columnWidth( gxStart ) + 1;
1406  if ( pt2.y() < pt1.y() ) {
1407  // overnight working hours
1408  if ( ( (gxStart==0) && !mHolidayMask->at(mHolidayMask->count()-1) ) ||
1409  ( (gxStart>0) && (gxStart<int(mHolidayMask->count())) && (!mHolidayMask->at(gxStart-1) ) ) ) {
1410  if ( pt2.y() > cy ) {
1411  dbp.fillRect( xStart, cy, xWidth, pt2.y() - cy + 1,
1412  KOPrefs::instance()->mWorkingHoursColor);
1413  }
1414  }
1415  if ( (gxStart < int(mHolidayMask->count()-1)) && (!mHolidayMask->at(gxStart)) ) {
1416  if ( pt1.y() < cy + ch - 1 ) {
1417  dbp.fillRect( xStart, pt1.y(), xWidth, cy + ch - pt1.y() + 1,
1418  KOPrefs::instance()->mWorkingHoursColor);
1419  }
1420  }
1421  } else {
1422  // last entry in holiday mask denotes the previous day not visible (needed for overnight shifts)
1423  if ( gxStart < int(mHolidayMask->count()-1) && !mHolidayMask->at(gxStart)) {
1424  dbp.fillRect( xStart, pt1.y(), xWidth, pt2.y() - pt1.y() + 1,
1425  KOPrefs::instance()->mWorkingHoursColor );
1426  }
1427  }
1428  ++gxStart;
1429  }
1430  }
1431  }
1432 
1433  // draw selection
1434  if ( mHasSelection ) {
1435  TQPoint pt, pt1;
1436 
1437  if ( mSelectionEndCell.x() > mSelectionStartCell.x() ) { // multi day selection
1438  // draw start day
1439  pt = gridToContents( mSelectionStartCell );
1440  pt1 = gridToContents( TQPoint( mSelectionStartCell.x() + 1, mRows + 1 ) );
1441  dbp.fillRect( TQRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
1442  // draw all other days between the start day and the day of the selection end
1443  for ( int c = mSelectionStartCell.x() + 1; c < mSelectionEndCell.x(); ++c ) {
1444  pt = gridToContents( TQPoint( c, 0 ) );
1445  pt1 = gridToContents( TQPoint( c + 1, mRows + 1 ) );
1446  dbp.fillRect( TQRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
1447  }
1448  // draw end day
1449  pt = gridToContents( TQPoint( mSelectionEndCell.x(), 0 ) );
1450  pt1 = gridToContents( mSelectionEndCell + TQPoint(1,1) );
1451  dbp.fillRect( TQRect( pt, pt1), KOPrefs::instance()->mHighlightColor );
1452  } else { // single day selection
1453  pt = gridToContents( mSelectionStartCell );
1454  pt1 = gridToContents( mSelectionEndCell + TQPoint(1,1) );
1455  dbp.fillRect( TQRect( pt, pt1 ), KOPrefs::instance()->mHighlightColor );
1456  }
1457  }
1458 
1459  TQPen hourPen( KOPrefs::instance()->mAgendaBgColor.dark( 150 ) );
1460  TQPen halfHourPen( KOPrefs::instance()->mAgendaBgColor.dark( 125 ) );
1461  dbp.setPen( hourPen );
1462 
1463  // Draw vertical lines of grid, start with the last line not yet visible
1464  // kdDebug(5850) << "drawContents cx: " << cx << " cy: " << cy << " cw: " << cw << " ch: " << ch << endl;
1465  double x = ( int( cx / mGridSpacingX ) ) * mGridSpacingX;
1466  while (x < cx + cw) {
1467  dbp.drawLine( int( x ), cy, int( x ), cy + ch );
1468  x+=mGridSpacingX;
1469  }
1470 
1471  // Draw horizontal lines of grid
1472  double y = ( int( cy / (2*lGridSpacingY) ) ) * 2 * lGridSpacingY;
1473  while (y < cy + ch) {
1474 // kdDebug(5850) << " y: " << y << endl;
1475  dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
1476  y += 2 * lGridSpacingY;
1477  }
1478  y = ( 2 * int( cy / (2*lGridSpacingY) ) + 1) * lGridSpacingY;
1479  dbp.setPen( halfHourPen );
1480  while (y < cy + ch) {
1481 // kdDebug(5850) << " y: " << y << endl;
1482  dbp.drawLine( cx, int( y ), cx + cw, int( y ) );
1483  y+=2*lGridSpacingY;
1484  }
1485  p->drawPixmap(cx,cy, db);
1486 }
1487 
1488 /*
1489  Convert srcollview contents coordinates to agenda grid coordinates.
1490 */
1491 TQPoint KOAgenda::contentsToGrid ( const TQPoint &pos ) const
1492 {
1493  int gx = int( KOGlobals::self()->reverseLayout() ?
1494  mColumns - pos.x()/mGridSpacingX : pos.x()/mGridSpacingX );
1495  int gy = int( pos.y()/mGridSpacingY );
1496  return TQPoint( gx, gy );
1497 }
1498 
1499 /*
1500  Convert agenda grid coordinates to scrollview contents coordinates.
1501 */
1502 TQPoint KOAgenda::gridToContents( const TQPoint &gpos ) const
1503 {
1504  int x = int( KOGlobals::self()->reverseLayout() ?
1505  (mColumns - gpos.x())*mGridSpacingX : gpos.x()*mGridSpacingX );
1506  int y = int( gpos.y()*mGridSpacingY );
1507  return TQPoint( x, y );
1508 }
1509 
1510 
1511 /*
1512  Return Y coordinate corresponding to time. Coordinates are rounded to fit into
1513  the grid.
1514 */
1515 int KOAgenda::timeToY(const TQTime &time)
1516 {
1517 // kdDebug(5850) << "Time: " << time.toString() << endl;
1518  int minutesPerCell = 24 * 60 / mRows;
1519 // kdDebug(5850) << "minutesPerCell: " << minutesPerCell << endl;
1520  int timeMinutes = time.hour() * 60 + time.minute();
1521 // kdDebug(5850) << "timeMinutes: " << timeMinutes << endl;
1522  int Y = (timeMinutes + (minutesPerCell / 2)) / minutesPerCell;
1523 // kdDebug(5850) << "y: " << Y << endl;
1524 // kdDebug(5850) << "\n" << endl;
1525  return Y;
1526 }
1527 
1528 
1529 /*
1530  Return time corresponding to cell y coordinate. Coordinates are rounded to
1531  fit into the grid.
1532 */
1533 TQTime KOAgenda::gyToTime(int gy)
1534 {
1535 // kdDebug(5850) << "gyToTime: " << gy << endl;
1536  int secondsPerCell = 24 * 60 * 60/ mRows;
1537 
1538  int timeSeconds = secondsPerCell * gy;
1539 
1540  TQTime time( 0, 0, 0 );
1541  if ( timeSeconds < 24 * 60 * 60 ) {
1542  time = time.addSecs(timeSeconds);
1543  } else {
1544  time.setHMS( 23, 59, 59 );
1545  }
1546 // kdDebug(5850) << " gyToTime: " << time.toString() << endl;
1547 
1548  return time;
1549 }
1550 
1551 TQMemArray<int> KOAgenda::minContentsY()
1552 {
1553  TQMemArray<int> minArray;
1554  minArray.fill( timeToY( TQTime(23, 59) ), mSelectedDates.count() );
1555  for ( KOAgendaItem *item = mItems.first();
1556  item != 0; item = mItems.next() ) {
1557  int ymin = item->cellYTop();
1558  int index = item->cellXLeft();
1559  if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
1560  if ( ymin < minArray[index] && mItemsToDelete.findRef( item ) == -1 )
1561  minArray[index] = ymin;
1562  }
1563  }
1564 
1565  return minArray;
1566 }
1567 
1568 TQMemArray<int> KOAgenda::maxContentsY()
1569 {
1570  TQMemArray<int> maxArray;
1571  maxArray.fill( timeToY( TQTime(0, 0) ), mSelectedDates.count() );
1572  for ( KOAgendaItem *item = mItems.first();
1573  item != 0; item = mItems.next() ) {
1574  int ymax = item->cellYBottom();
1575  int index = item->cellXLeft();
1576  if ( index>=0 && index<(int)(mSelectedDates.count()) ) {
1577  if ( ymax > maxArray[index] && mItemsToDelete.findRef( item ) == -1 )
1578  maxArray[index] = ymax;
1579  }
1580  }
1581 
1582  return maxArray;
1583 }
1584 
1585 void KOAgenda::setStartTime( const TQTime &startHour )
1586 {
1587  double startPos = ( startHour.hour()/24. + startHour.minute()/1440. +
1588  startHour.second()/86400. ) * mRows * gridSpacingY();
1589  setContentsPos( 0, int( startPos ) );
1590 }
1591 
1592 
1593 /*
1594  Insert KOAgendaItem into agenda.
1595 */
1596 KOAgendaItem *KOAgenda::insertItem( Incidence *incidence, const TQDate &qd, int X,
1597  int YTop, int YBottom, int itemPos, int itemCount )
1598 {
1599  if ( mAllDayMode ) {
1600  kdDebug(5850) << "KOAgenda: calling insertItem in all-day mode is illegal." << endl;
1601  return 0;
1602  }
1603 
1604  mActionType = NOP;
1605 
1606  KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, incidence, qd, viewport(), itemPos, itemCount );
1607  connect( agendaItem, TQ_SIGNAL( removeAgendaItem( KOAgendaItem * ) ),
1608  TQ_SLOT( removeAgendaItem( KOAgendaItem * ) ) );
1609  connect( agendaItem, TQ_SIGNAL( showAgendaItem( KOAgendaItem * ) ),
1610  TQ_SLOT( showAgendaItem( KOAgendaItem * ) ) );
1611 
1612  if ( YBottom <= YTop ) {
1613  kdDebug(5850) << "KOAgenda::insertItem(): Text: " << agendaItem->text() << " YSize<0" << endl;
1614  YBottom = YTop;
1615  }
1616 
1617  agendaItem->resize( int( ( X + 1 ) * mGridSpacingX ) -
1618  int( X * mGridSpacingX ),
1619  int( YTop * mGridSpacingY ) -
1620  int( ( YBottom + 1 ) * mGridSpacingY ) );
1621  agendaItem->setCellXY( X, YTop, YBottom );
1622  agendaItem->setCellXRight( X );
1623  agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, incidence ) );
1624  agendaItem->installEventFilter( this );
1625 
1626  addChild( agendaItem, int( X * mGridSpacingX ), int( YTop * mGridSpacingY ) );
1627  mItems.append( agendaItem );
1628 
1629  placeSubCells( agendaItem );
1630 
1631  agendaItem->show();
1632 
1633  marcus_bains();
1634 
1635  return agendaItem;
1636 }
1637 
1638 /*
1639  Insert all-day KOAgendaItem into agenda.
1640 */
1641 KOAgendaItem *KOAgenda::insertAllDayItem( Incidence *event, const TQDate &qd,
1642  int XBegin, int XEnd )
1643 {
1644  if ( !mAllDayMode ) {
1645  kdDebug(5850) << "KOAgenda: calling insertAllDayItem in non all-day mode is illegal." << endl;
1646  return 0;
1647  }
1648 
1649  mActionType = NOP;
1650 
1651  KOAgendaItem *agendaItem = new KOAgendaItem( mCalendar, event, qd, viewport(), 1, 1 );
1652  connect( agendaItem, TQ_SIGNAL( removeAgendaItem( KOAgendaItem* ) ),
1653  TQ_SLOT( removeAgendaItem( KOAgendaItem* ) ) );
1654  connect( agendaItem, TQ_SIGNAL( showAgendaItem( KOAgendaItem* ) ),
1655  TQ_SLOT( showAgendaItem( KOAgendaItem* ) ) );
1656 
1657  agendaItem->setCellXY( XBegin, 0, 0 );
1658  agendaItem->setCellXRight( XEnd );
1659 
1660  double startIt = mGridSpacingX * ( agendaItem->cellXLeft() );
1661  double endIt = mGridSpacingX * ( agendaItem->cellWidth() +
1662  agendaItem->cellXLeft() );
1663 
1664  agendaItem->resize( int( endIt ) - int( startIt ), int( mGridSpacingY ) );
1665 
1666  agendaItem->installEventFilter( this );
1667  agendaItem->setResourceColor( KOHelper::resourceColor( mCalendar, event ) );
1668  addChild( agendaItem, int( XBegin * mGridSpacingX ), 0 );
1669  mItems.append( agendaItem );
1670 
1671  placeSubCells( agendaItem );
1672 
1673  agendaItem->show();
1674 
1675  return agendaItem;
1676 }
1677 
1678 
1679 void KOAgenda::insertMultiItem( Event *event, const TQDate &qd, int XBegin, int XEnd,
1680  int YTop, int YBottom )
1681 {
1682  if ( mAllDayMode ) {
1683  kdDebug(5850) << "KOAgenda: calling insertMultiItem in all-day mode is illegal." << endl;
1684  return;
1685  }
1686  mActionType = NOP;
1687 
1688  int cellX,cellYTop,cellYBottom;
1689  TQString newtext;
1690  int width = XEnd - XBegin + 1;
1691  int count = 0;
1692  KOAgendaItem *current = 0;
1693  TQPtrList<KOAgendaItem> multiItems;
1694  const int visibleCount = mSelectedDates.first().daysTo( mSelectedDates.last() );
1695  for ( cellX = XBegin; cellX <= XEnd; ++cellX ) {
1696  ++count;
1697  //Only add the items that are visible.
1698  if( cellX >= 0 && cellX <= visibleCount ) {
1699  if ( cellX == XBegin ) {
1700  cellYTop = YTop;
1701  } else {
1702  cellYTop = 0;
1703  }
1704 
1705  if ( cellX == XEnd ) {
1706  cellYBottom = YBottom;
1707  } else {
1708  cellYBottom = rows() - 1;
1709  }
1710 
1711  newtext = TQString("(%1/%2): ").arg( count ).arg( width );
1712  newtext.append( event->summary() );
1713 
1714  current = insertItem( event, qd, cellX, cellYTop, cellYBottom, count, width );
1715  current->setText( newtext );
1716  multiItems.append( current );
1717  }
1718  }
1719  TQPtrList<KOAgendaItem>::iterator it = multiItems.begin();
1720  TQPtrList<KOAgendaItem>::iterator e = multiItems.end();
1721 
1722  if ( it != e ) { // .first asserts if the list is empty
1723  KOAgendaItem *first = multiItems.first();
1724  KOAgendaItem *last = multiItems.last();
1725  KOAgendaItem *prev = 0, *next = 0;
1726 
1727  while ( it != e ) {
1728  KOAgendaItem *item = *it;
1729  ++it;
1730  next = ( it == e ) ? 0 : (*it);
1731  if ( item ) {
1732  item->setMultiItem( ( item == first ) ? 0 : first,
1733  prev, next,
1734  ( item == last ) ? 0 : last );
1735  }
1736  prev = item;
1737  }
1738  }
1739 
1740  marcus_bains();
1741 }
1742 
1743 void KOAgenda::removeIncidence( Incidence *incidence )
1744 {
1745  // First find all items to be deleted and store them
1746  // in its own list. Otherwise removeAgendaItem will reset
1747  // the current position and mess this up.
1748  TQPtrList<KOAgendaItem> itemsToRemove;
1749 
1750  KOAgendaItem *item = mItems.first();
1751  while ( item ) {
1752  if ( item->incidence() == incidence ) {
1753  itemsToRemove.append( item );
1754  }
1755  item = mItems.next();
1756  }
1757  item = itemsToRemove.first();
1758  while ( item ) {
1759  removeAgendaItem( item );
1760  item = itemsToRemove.next();
1761  }
1762 }
1763 
1764 void KOAgenda::showAgendaItem( KOAgendaItem *agendaItem )
1765 {
1766  if ( !agendaItem ) {
1767  return;
1768  }
1769 
1770  agendaItem->hide();
1771  addChild( agendaItem );
1772  if ( !mItems.containsRef( agendaItem ) ) {
1773  mItems.append( agendaItem );
1774  }
1775  placeSubCells( agendaItem );
1776 
1777  agendaItem->show();
1778 }
1779 
1780 bool KOAgenda::removeAgendaItem( KOAgendaItem *item )
1781 {
1782  // we found the item. Let's remove it and update the conflicts
1783  bool taken = false;
1784  KOAgendaItem *thisItem = item;
1785  TQPtrList<KOAgendaItem> conflictItems = thisItem->conflictItems();
1786  removeChild( thisItem );
1787 
1788  int pos = mItems.find( thisItem );
1789  if ( pos >= 0 ) {
1790  mItems.take( pos );
1791  taken = true;
1792  }
1793 
1794  KOAgendaItem *confitem;
1795  for ( confitem = conflictItems.first(); confitem != 0;
1796  confitem = conflictItems.next() ) {
1797  // the item itself is also in its own conflictItems list!
1798  if ( confitem != thisItem ) placeSubCells(confitem);
1799 
1800  }
1801  mItemsToDelete.append( thisItem );
1802  TQTimer::singleShot( 0, this, TQ_SLOT( deleteItemsToDelete() ) );
1803  return taken;
1804 }
1805 
1806 void KOAgenda::deleteItemsToDelete()
1807 {
1808  mItemsToDelete.clear();
1809 }
1810 
1811 /*TQSizePolicy KOAgenda::sizePolicy() const
1812 {
1813  // Thought this would make the all-day event agenda minimum size and the
1814  // normal agenda take the remaining space. But it doesnt work. The TQSplitter
1815  // dont seem to think that an Expanding widget needs more space than a
1816  // Preferred one.
1817  // But it doesnt hurt, so it stays.
1818  if (mAllDayMode) {
1819  return TQSizePolicy(TQSizePolicy::Expanding,TQSizePolicy::Preferred);
1820  } else {
1821  return TQSizePolicy(TQSizePolicy::Expanding,TQSizePolicy::Expanding);
1822  }
1823 }
1824 */
1825 
1826 /*
1827  Overridden from TQScrollView to provide proper resizing of KOAgendaItems.
1828 */
1829 void KOAgenda::resizeEvent ( TQResizeEvent *ev )
1830 {
1831 // kdDebug(5850) << "KOAgenda::resizeEvent" << endl;
1832 
1833  TQSize newSize( ev->size() );
1834  if (mAllDayMode) {
1835  mGridSpacingX = double( newSize.width() - 2 * frameWidth() ) / (double)mColumns;
1836  mGridSpacingY = newSize.height() - 2 * frameWidth();
1837  } else {
1838  int scrollbarWidth = vScrollBarMode() != AlwaysOff ? verticalScrollBar()->width() : 0;
1839  mGridSpacingX = double( newSize.width() - scrollbarWidth - 2 * frameWidth()) / double(mColumns);
1840  // make sure that there are not more than 24 per day
1841  mGridSpacingY = double(newSize.height() - 2 * frameWidth()) / double(mRows);
1842  if ( mGridSpacingY < mDesiredGridSpacingY )
1843  mGridSpacingY = mDesiredGridSpacingY;
1844  }
1845  calculateWorkingHours();
1846  TQTimer::singleShot( 0, this, TQ_SLOT( resizeAllContents() ) );
1847  emit gridSpacingYChanged( mGridSpacingY * 4 );
1848  TQScrollView::resizeEvent(ev);
1849 }
1850 
1851 void KOAgenda::resizeAllContents()
1852 {
1853  double subCellWidth;
1854  if ( mItems.count() > 0 ) {
1855  KOAgendaItem *item;
1856  if (mAllDayMode) {
1857  for ( item=mItems.first(); item != 0; item=mItems.next() ) {
1858  subCellWidth = calcSubCellWidth( item );
1859  placeAgendaItem( item, subCellWidth );
1860  }
1861  } else {
1862  for ( item=mItems.first(); item != 0; item=mItems.next() ) {
1863  subCellWidth = calcSubCellWidth( item );
1864  placeAgendaItem( item, subCellWidth );
1865  }
1866  }
1867  }
1868  checkScrollBoundaries();
1869  marcus_bains();
1870 }
1871 
1872 void KOAgenda::scrollUp()
1873 {
1874  scrollBy(0,-mScrollOffset);
1875 }
1876 
1877 
1878 void KOAgenda::scrollDown()
1879 {
1880  scrollBy(0,mScrollOffset);
1881 }
1882 
1883 
1884 /*
1885  Calculates the minimum width
1886 */
1887 int KOAgenda::minimumWidth() const
1888 {
1889  // FIXME:: develop a way to dynamically determine the minimum width
1890  int min = 100;
1891 
1892  return min;
1893 }
1894 
1895 void KOAgenda::updateConfig()
1896 {
1897  double oldGridSpacingY = mGridSpacingY;
1898 
1899  mDesiredGridSpacingY = KOPrefs::instance()->mHourSize;
1900  if ( mDesiredGridSpacingY < 4 || mDesiredGridSpacingY > 30 ) {
1901  mDesiredGridSpacingY = 10;
1902  }
1903 
1904  // make sure that there are not more than 24 per day
1905  mGridSpacingY = (double)height() / (double)mRows;
1906  if ( mGridSpacingY < mDesiredGridSpacingY ) {
1907  mGridSpacingY = mDesiredGridSpacingY;
1908  }
1909 
1910  //can be two doubles equal?, it's better to compare them with an epsilon
1911  if ( fabs( oldGridSpacingY - mGridSpacingY ) > 0.1 ) {
1912  resizeContents( int( mGridSpacingX * mColumns ),
1913  int( mGridSpacingY * mRows ) );
1914  }
1915 
1916  calculateWorkingHours();
1917 
1918  marcus_bains();
1919 }
1920 
1921 void KOAgenda::checkScrollBoundaries()
1922 {
1923  // Invalidate old values to force update
1924  mOldLowerScrollValue = -1;
1925  mOldUpperScrollValue = -1;
1926 
1927  checkScrollBoundaries(verticalScrollBar()->value());
1928 }
1929 
1930 void KOAgenda::checkScrollBoundaries( int v )
1931 {
1932  int yMin = int( (v) / mGridSpacingY );
1933  int yMax = int( ( v + visibleHeight() ) / mGridSpacingY );
1934 
1935 // kdDebug(5850) << "--- yMin: " << yMin << " yMax: " << yMax << endl;
1936 
1937  if ( yMin != mOldLowerScrollValue ) {
1938  mOldLowerScrollValue = yMin;
1939  emit lowerYChanged(yMin);
1940  }
1941  if ( yMax != mOldUpperScrollValue ) {
1942  mOldUpperScrollValue = yMax;
1943  emit upperYChanged(yMax);
1944  }
1945 }
1946 
1947 int KOAgenda::visibleContentsYMin()
1948 {
1949  int v = verticalScrollBar()->value();
1950  return int( v / mGridSpacingY );
1951 }
1952 
1953 int KOAgenda::visibleContentsYMax()
1954 {
1955  int v = verticalScrollBar()->value();
1956  return int( ( v + visibleHeight() ) / mGridSpacingY );
1957 }
1958 
1959 void KOAgenda::deselectItem()
1960 {
1961  if ( mSelectedItem.isNull() ) {
1962  return;
1963  }
1964  mSelectedItem->select(false);
1965  mSelectedItem = 0;
1966 }
1967 
1968 void KOAgenda::selectItem(KOAgendaItem *item)
1969 {
1970  if ((KOAgendaItem *)mSelectedItem == item) return;
1971  deselectItem();
1972  if (item == 0) {
1973  emit incidenceSelected( 0, TQDate() );
1974  return;
1975  }
1976  mSelectedItem = item;
1977  mSelectedItem->select();
1978  assert( mSelectedItem->incidence() );
1979  mSelectedUid = mSelectedItem->incidence()->uid();
1980  emit incidenceSelected( mSelectedItem->incidence(), mSelectedItem->itemDate() );
1981 }
1982 
1983 void KOAgenda::selectItemByUID( const TQString& uid )
1984 {
1985  KOAgendaItem *item;
1986  for ( item = mItems.first(); item != 0; item = mItems.next() ) {
1987  if( item->incidence() && item->incidence()->uid() == uid ) {
1988  selectItem( item );
1989  break;
1990  }
1991  }
1992 }
1993 
1994 // This function seems never be called.
1995 void KOAgenda::keyPressEvent( TQKeyEvent *kev )
1996 {
1997  switch(kev->key()) {
1998  case Key_PageDown:
1999  verticalScrollBar()->addPage();
2000  break;
2001  case Key_PageUp:
2002  verticalScrollBar()->subtractPage();
2003  break;
2004  case Key_Down:
2005  verticalScrollBar()->addLine();
2006  break;
2007  case Key_Up:
2008  verticalScrollBar()->subtractLine();
2009  break;
2010  default:
2011  ;
2012  }
2013 }
2014 
2015 void KOAgenda::calculateWorkingHours()
2016 {
2017  mWorkingHoursEnable = !mAllDayMode;
2018 
2019  TQTime tmp = KOPrefs::instance()->mWorkingHoursStart.time();
2020  mWorkingHoursYTop = int( 4 * mGridSpacingY *
2021  ( tmp.hour() + tmp.minute() / 60. +
2022  tmp.second() / 3600. ) );
2023  tmp = KOPrefs::instance()->mWorkingHoursEnd.time();
2024  mWorkingHoursYBottom = int( 4 * mGridSpacingY *
2025  ( tmp.hour() + tmp.minute() / 60. +
2026  tmp.second() / 3600. ) - 1 );
2027 }
2028 
2029 
2030 DateList KOAgenda::dateList() const
2031 {
2032  return mSelectedDates;
2033 }
2034 
2035 void KOAgenda::setDateList(const DateList &selectedDates)
2036 {
2037  mSelectedDates = selectedDates;
2038  marcus_bains();
2039 }
2040 
2041 void KOAgenda::setHolidayMask(TQMemArray<bool> *mask)
2042 {
2043  mHolidayMask = mask;
2044 
2045 }
2046 
2047 void KOAgenda::contentsMousePressEvent ( TQMouseEvent *event )
2048 {
2049  kdDebug(5850) << "KOagenda::contentsMousePressEvent(): type: " << event->type() << endl;
2050  TQScrollView::contentsMousePressEvent(event);
2051 }
2052 
2053 void KOAgenda::setTypeAheadReceiver( TQObject *o )
2054 {
2055  mTypeAheadReceiver = o;
2056 }
2057 
2058 TQObject *KOAgenda::typeAheadReceiver() const
2059 {
2060  return mTypeAheadReceiver;
2061 }
This is the main calendar widget.
Definition: calendarview.h:82
virtual bool accept(Visitor &)
bool isReadOnly() const
virtual Incidence * clone()=0
TQString summary() const