korganizer

koagendaview.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  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 
20  As a special exception, permission is given to link this program
21  with any edition of TQt, and distribute the resulting executable,
22  without including the source code for TQt in the source distribution.
23 */
24 
25 #include <tqhbox.h>
26 #include <tqvbox.h>
27 #include <tqlabel.h>
28 #include <tqframe.h>
29 #include <tqlayout.h>
30 #ifndef KORG_NOSPLITTER
31 #include <tqsplitter.h>
32 #endif
33 #include <tqfont.h>
34 #include <tqfontmetrics.h>
35 #include <tqpopupmenu.h>
36 #include <tqtooltip.h>
37 #include <tqpainter.h>
38 #include <tqpushbutton.h>
39 #include <tqcursor.h>
40 #include <tqbitarray.h>
41 
42 #include <tdeapplication.h>
43 #include <kdebug.h>
44 #include <kstandarddirs.h>
45 #include <kiconloader.h>
46 #include <tdelocale.h>
47 #include <tdeconfig.h>
48 #include <tdeglobal.h>
49 #include <tdeglobalsettings.h>
50 #include <kholidays.h>
51 
52 #include <libkcal/calendar.h>
53 #include <libkcal/icaldrag.h>
54 #include <libkcal/dndfactory.h>
55 #include <libkcal/calfilter.h>
56 
57 #include <kcalendarsystem.h>
58 
59 #include "koglobals.h"
60 #ifndef KORG_NOPLUGINS
61 #include "kocore.h"
62 #endif
63 #include "koprefs.h"
64 #include "koagenda.h"
65 #include "koagendaitem.h"
66 #include "timelabels.h"
67 
68 #include "koincidencetooltip.h"
69 #include "kogroupware.h"
70 #include "kodialogmanager.h"
71 #include "koeventpopupmenu.h"
72 
73 #include "koagendaview.h"
74 #include "koagendaview.moc"
75 
76 using namespace KOrg;
77 
78 
79 EventIndicator::EventIndicator(Location loc,TQWidget *parent,const char *name)
80  : TQFrame(parent,name)
81 {
82  mColumns = 1;
83  mEnabled.resize( mColumns );
84  mLocation = loc;
85 
86  if (mLocation == Top) mPixmap = KOGlobals::self()->smallIcon("upindicator");
87  else mPixmap = KOGlobals::self()->smallIcon("downindicator");
88 
89  setMinimumHeight(mPixmap.height());
90 }
91 
92 EventIndicator::~EventIndicator()
93 {
94 }
95 
96 void EventIndicator::drawContents(TQPainter *p)
97 {
98 // kdDebug(5850) << "======== top: " << contentsRect().top() << " bottom "
99 // << contentsRect().bottom() << " left " << contentsRect().left()
100 // << " right " << contentsRect().right() << endl;
101 
102  int i;
103  for(i=0;i<mColumns;++i) {
104  if (mEnabled[i]) {
105  int cellWidth = contentsRect().right()/mColumns;
106  int xOffset = KOGlobals::self()->reverseLayout() ?
107  (mColumns - 1 - i)*cellWidth + cellWidth/2 -mPixmap.width()/2 :
108  i*cellWidth + cellWidth/2 -mPixmap.width()/2;
109  p->drawPixmap(TQPoint(xOffset,0),mPixmap);
110  }
111  }
112 }
113 
114 void EventIndicator::changeColumns(int columns)
115 {
116  mColumns = columns;
117  mEnabled.resize(mColumns);
118 
119  update();
120 }
121 
122 void EventIndicator::enableColumn(int column, bool enable)
123 {
124  mEnabled[column] = enable;
125 }
126 
127 
128 #include <libkcal/incidence.h>
129 
133 
134 
135 KOAlternateLabel::KOAlternateLabel(const TQString &shortlabel, const TQString &longlabel,
136  const TQString &extensivelabel, TQWidget *parent, const char *name )
137  : TQLabel(parent, name), mTextTypeFixed(false), mShortText(shortlabel),
138  mLongText(longlabel), mExtensiveText(extensivelabel)
139 {
140  setSizePolicy(TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed ));
141  if (mExtensiveText.isEmpty()) mExtensiveText = mLongText;
142  squeezeTextToLabel();
143 }
144 
145 KOAlternateLabel::~KOAlternateLabel()
146 {
147 }
148 
149 void KOAlternateLabel::useShortText()
150 {
151  mTextTypeFixed = true;
152  TQLabel::setText( mShortText );
153  TQToolTip::remove( this );
154  TQToolTip::add( this, mExtensiveText );
155  update(); // for kolab/issue4350
156 }
157 
158 void KOAlternateLabel::useLongText()
159 {
160  mTextTypeFixed = true;
161  TQLabel::setText( mLongText );
162  TQToolTip::remove( this );
163  TQToolTip::add( this, mExtensiveText );
164  update(); // for kolab/issue4350
165 }
166 
167 void KOAlternateLabel::useExtensiveText()
168 {
169  mTextTypeFixed = true;
170  TQLabel::setText( mExtensiveText );
171  TQToolTip::remove( this );
172  TQToolTip::add( this, "" );
173  update(); // for kolab/issue4350
174 }
175 
176 void KOAlternateLabel::useDefaultText()
177 {
178  mTextTypeFixed = false;
179  squeezeTextToLabel();
180 }
181 
182 KOAlternateLabel::TextType KOAlternateLabel::largestFittingTextType() const
183 {
184  TQFontMetrics fm( fontMetrics() );
185  const int labelWidth = size().width();
186  const int longTextWidth = fm.width( mLongText );
187  const int extensiveTextWidth = fm.width( mExtensiveText );
188  if ( extensiveTextWidth <= labelWidth )
189  return Extensive;
190  else if ( longTextWidth <= labelWidth )
191  return Long;
192  else
193  return Short;
194 }
195 
196 void KOAlternateLabel::setFixedType( TextType type )
197 {
198  switch ( type )
199  {
200  case Extensive: useExtensiveText(); break;
201  case Long: useLongText(); break;
202  case Short: useShortText(); break;
203  }
204 }
205 
206 void KOAlternateLabel::squeezeTextToLabel()
207 {
208  if ( mTextTypeFixed )
209  return;
210 
211  const TextType type = largestFittingTextType();
212  switch ( type )
213  {
214  case Extensive:
215  TQLabel::setText( mExtensiveText );
216  TQToolTip::remove( this );
217  TQToolTip::add( this, "" );
218  break;
219  case Long:
220  TQLabel::setText( mLongText );
221  TQToolTip::remove( this );
222  TQToolTip::add( this, mExtensiveText );
223  break;
224  case Short:
225  TQLabel::setText( mShortText );
226  TQToolTip::remove( this );
227  TQToolTip::add( this, mExtensiveText );
228  break;
229  }
230  update(); // for kolab/issue4350
231 }
232 
233 void KOAlternateLabel::resizeEvent( TQResizeEvent * )
234 {
235  squeezeTextToLabel();
236 }
237 
238 TQSize KOAlternateLabel::minimumSizeHint() const
239 {
240  TQSize sh = TQLabel::minimumSizeHint();
241  sh.setWidth(-1);
242  return sh;
243 }
244 
248 
249 KOAgendaView::KOAgendaView( Calendar *cal,
250  CalendarView *calendarView,
251  TQWidget *parent,
252  const char *name,
253  bool isSideBySide ) :
254  KOrg::AgendaView (cal, parent,name), mExpandButton( 0 ),
255  mAllowAgendaUpdate( true ),
256  mUpdateItem( 0 ),
257  mIsSideBySide( isSideBySide ),
258  mPendingChanges( true ),
259  mAreDatesInitialized( false )
260 {
261  mSelectedDates.append(TQDate::currentDate());
262 
263  mLayoutDayLabels = 0;
264  mDayLabelsFrame = 0;
265  mDayLabels = 0;
266 
267  bool isRTL = KOGlobals::self()->reverseLayout();
268 
269  if ( KOPrefs::instance()->compactDialogs() ) {
270  if ( KOPrefs::instance()->mVerticalScreen ) {
271  mExpandedPixmap = KOGlobals::self()->smallIcon( "1downarrow" );
272  mNotExpandedPixmap = KOGlobals::self()->smallIcon( "1uparrow" );
273  } else {
274  mExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1leftarrow" : "1rightarrow" );
275  mNotExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1rightarrow" : "1leftarrow" );
276  }
277  }
278 
279  TQBoxLayout *topLayout = new TQVBoxLayout(this);
280 
281  // Create day name labels for agenda columns
282  mDayLabelsFrame = new TQHBox(this);
283  topLayout->addWidget(mDayLabelsFrame);
284 
285  // Create agenda splitter
286 #ifndef KORG_NOSPLITTER
287  mSplitterAgenda = new TQSplitter(TQt::Vertical,this);
288  topLayout->addWidget(mSplitterAgenda);
289 
290  mSplitterAgenda->setOpaqueResize( TDEGlobalSettings::opaqueResize() );
291 
292  mAllDayFrame = new TQHBox(mSplitterAgenda);
293 
294  TQWidget *agendaFrame = new TQWidget(mSplitterAgenda);
295 #else
296  TQVBox *mainBox = new TQVBox( this );
297  topLayout->addWidget( mainBox );
298 
299  mAllDayFrame = new TQHBox(mainBox);
300 
301  TQWidget *agendaFrame = new TQWidget(mainBox);
302 #endif
303 
304  // Create all-day agenda widget
305  mDummyAllDayLeft = new TQVBox( mAllDayFrame );
306  if ( isSideBySide )
307  mDummyAllDayLeft->hide();
308 
309  if ( KOPrefs::instance()->compactDialogs() ) {
310  mExpandButton = new TQPushButton(mDummyAllDayLeft);
311  mExpandButton->setPixmap( mNotExpandedPixmap );
312  mExpandButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed,
313  TQSizePolicy::Fixed ) );
314  connect( mExpandButton, TQ_SIGNAL( clicked() ), TQ_SIGNAL( toggleExpand() ) );
315  } else {
316  TQLabel *label = new TQLabel( i18n("All Day"), mDummyAllDayLeft );
317  label->setAlignment( TQt::AlignRight | TQt::AlignVCenter | TQt::WordBreak );
318  }
319 
320  mAllDayAgenda = new KOAgenda( 1, calendarView, mAllDayFrame );
321  mAllDayAgenda->setCalendar( calendar() );
322  TQWidget *dummyAllDayRight = new TQWidget(mAllDayFrame);
323 
324  // Create agenda frame
325  TQGridLayout *agendaLayout = new TQGridLayout(agendaFrame,3,3);
326 // TQHBox *agendaFrame = new TQHBox(splitterAgenda);
327 
328  // create event indicator bars
329  mEventIndicatorTop = new EventIndicator(EventIndicator::Top,agendaFrame);
330  agendaLayout->addWidget(mEventIndicatorTop,0,1);
331  mEventIndicatorBottom = new EventIndicator(EventIndicator::Bottom,
332  agendaFrame);
333  agendaLayout->addWidget(mEventIndicatorBottom,2,1);
334  TQWidget *dummyAgendaRight = new TQWidget(agendaFrame);
335  agendaLayout->addWidget(dummyAgendaRight,0,2);
336 
337  // Create time labels
338  mTimeLabels = new TimeLabels(24,agendaFrame);
339  agendaLayout->addWidget(mTimeLabels,1,0);
340 
341  // Create agenda
342  mAgenda = new KOAgenda( 1, 96, KOPrefs::instance()->mHourSize, calendarView, agendaFrame );
343  mAgenda->setCalendar( calendar() );
344  agendaLayout->addMultiCellWidget(mAgenda,1,1,1,2);
345  agendaLayout->setColStretch(1,1);
346 
347  // Create event context menu for agenda
348  mAgendaPopup = eventPopup();
349 
350  // Create event context menu for all day agenda
351  mAllDayAgendaPopup = eventPopup();
352 
353  // make connections between dependent widgets
354  mTimeLabels->setAgenda(mAgenda);
355  if ( isSideBySide )
356  mTimeLabels->hide();
357 
358  // Update widgets to reflect user preferences
359 // updateConfig();
360 
361  createDayLabels( true );
362 
363  if ( !isSideBySide ) {
364  // these blank widgets make the All Day Event box line up with the agenda
365  dummyAllDayRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
366  dummyAgendaRight->setFixedWidth(mAgenda->verticalScrollBar()->width());
367  }
368 
369  updateTimeBarWidth();
370 
371  // Scrolling
372  connect(mAgenda->verticalScrollBar(),TQ_SIGNAL(valueChanged(int)),
373  mTimeLabels, TQ_SLOT(positionChanged()));
374 
375  connect( mAgenda,
376  TQ_SIGNAL( zoomView( const int, const TQPoint & ,const TQt::Orientation ) ),
377  TQ_SLOT( zoomView( const int, const TQPoint &, const TQt::Orientation ) ) );
378 
379  connect(mTimeLabels->verticalScrollBar(),TQ_SIGNAL(valueChanged(int)),
380  TQ_SLOT(setContentsPos(int)));
381 
382  // Create Events, depends on type of agenda
383  connect( mAgenda, TQ_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)),
384  TQ_SLOT(newTimeSpanSelected(const TQPoint &, const TQPoint &)));
385  connect( mAllDayAgenda, TQ_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)),
386  TQ_SLOT(newTimeSpanSelectedAllDay(const TQPoint &, const TQPoint &)));
387 
388  // event indicator update
389  connect( mAgenda, TQ_SIGNAL(lowerYChanged(int)),
390  TQ_SLOT(updateEventIndicatorTop(int)));
391  connect( mAgenda, TQ_SIGNAL(upperYChanged(int)),
392  TQ_SLOT(updateEventIndicatorBottom(int)));
393 
394  if ( !readOnly() ) {
395  connectAgenda( mAgenda, mAgendaPopup, mAllDayAgenda );
396  connectAgenda( mAllDayAgenda, mAllDayAgendaPopup, mAgenda);
397  }
398 
399  if ( cal ) {
400  cal->registerObserver( this );
401  }
402 }
403 
404 
405 KOAgendaView::~KOAgendaView()
406 {
407  if ( calendar() )
408  calendar()->unregisterObserver( this );
409  delete mAgendaPopup;
410  delete mAllDayAgendaPopup;
411 }
412 
413 void KOAgendaView::connectAgenda( KOAgenda *agenda, TQPopupMenu *popup,
414  KOAgenda *otherAgenda )
415 {
416  connect( agenda, TQ_SIGNAL(showIncidencePopupSignal(Calendar *,Incidence *,const TQDate &)),
417  popup, TQ_SLOT(showIncidencePopup(Calendar *,Incidence *,const TQDate &)) );
418 
419  connect( agenda, TQ_SIGNAL(showNewEventPopupSignal()),
420  TQ_SLOT(showNewEventPopup()) );
421 
422 
423  // Create/Show/Edit/Delete Event
424  connect( agenda, TQ_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)),
425  TQ_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)) );
426 
427  connect( agenda, TQ_SIGNAL(newStartSelectSignal()),
428  otherAgenda, TQ_SLOT(clearSelection()) );
429  connect( agenda, TQ_SIGNAL(newStartSelectSignal()),
430  TQ_SIGNAL(timeSpanSelectionChanged()) );
431 
432  connect( agenda, TQ_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)),
433  TQ_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)) );
434  connect( agenda, TQ_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)),
435  TQ_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)) );
436  connect( agenda, TQ_SIGNAL(deleteIncidenceSignal(Incidence *)),
437  TQ_SIGNAL(deleteIncidenceSignal(Incidence *)) );
438 
439  connect( agenda, TQ_SIGNAL(startMultiModify(const TQString &)),
440  TQ_SIGNAL(startMultiModify(const TQString &)) );
441  connect( agenda, TQ_SIGNAL(endMultiModify()),
442  TQ_SIGNAL(endMultiModify()) );
443 
444  connect( agenda, TQ_SIGNAL(itemModified(KOAgendaItem *)),
445  TQ_SLOT(updateEventDates(KOAgendaItem *)) );
446 
447  connect( agenda, TQ_SIGNAL(enableAgendaUpdate(bool)),
448  TQ_SLOT(enableAgendaUpdate(bool)) );
449 
450  // drag signals
451  connect( agenda, TQ_SIGNAL(startDragSignal(Incidence *)),
452  TQ_SLOT(startDrag(Incidence *)) );
453 
454  // synchronize selections
455  connect( agenda, TQ_SIGNAL(incidenceSelected(Incidence *,const TQDate &)),
456  otherAgenda, TQ_SLOT(deselectItem()) );
457  connect( agenda, TQ_SIGNAL(incidenceSelected(Incidence *,const TQDate &)),
458  TQ_SIGNAL(incidenceSelected(Incidence *,const TQDate &)) );
459 
460  // rescheduling of todos by d'n'd
461  connect( agenda, TQ_SIGNAL(droppedToDo(Todo *,const TQPoint &,bool)),
462  TQ_SLOT(slotTodoDropped(Todo *,const TQPoint &,bool)) );
463 
464 }
465 
466 void KOAgendaView::zoomInVertically( )
467 {
468  if ( !mIsSideBySide )
469  KOPrefs::instance()->mHourSize++;
470  mAgenda->updateConfig();
471  mAgenda->checkScrollBoundaries();
472 
473  mTimeLabels->updateConfig();
474  mTimeLabels->positionChanged();
475  mTimeLabels->repaint();
476 
477  updateView();
478 }
479 
480 void KOAgendaView::zoomOutVertically( )
481 {
482 
483  if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) {
484 
485  if ( !mIsSideBySide )
486  KOPrefs::instance()->mHourSize--;
487  mAgenda->updateConfig();
488  mAgenda->checkScrollBoundaries();
489 
490  mTimeLabels->updateConfig();
491  mTimeLabels->positionChanged();
492  mTimeLabels->repaint();
493 
494  updateView();
495  }
496 }
497 
498 void KOAgendaView::zoomInHorizontally( const TQDate &date)
499 {
500  TQDate begin;
501  TQDate newBegin;
502  TQDate dateToZoom = date;
503  int ndays,count;
504 
505  begin = mSelectedDates.first();
506  ndays = begin.daysTo( mSelectedDates.last() );
507 
508  // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it.
509  if ( ! dateToZoom.isValid () )
510  dateToZoom=mAgenda->selectedIncidenceDate();
511 
512  if( !dateToZoom.isValid() ) {
513  if ( ndays > 1 ) {
514  newBegin=begin.addDays(1);
515  count = ndays-1;
516  emit zoomViewHorizontally ( newBegin , count );
517  }
518  } else {
519  if ( ndays <= 2 ) {
520  newBegin = dateToZoom;
521  count = 1;
522  } else {
523  newBegin = dateToZoom.addDays( -ndays/2 +1 );
524  count = ndays -1 ;
525  }
526  emit zoomViewHorizontally ( newBegin , count );
527  }
528 }
529 
530 void KOAgendaView::zoomOutHorizontally( const TQDate &date )
531 {
532  TQDate begin;
533  TQDate newBegin;
534  TQDate dateToZoom = date;
535  int ndays,count;
536 
537  begin = mSelectedDates.first();
538  ndays = begin.daysTo( mSelectedDates.last() );
539 
540  // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it.
541  if ( ! dateToZoom.isValid () )
542  dateToZoom=mAgenda->selectedIncidenceDate();
543 
544  if ( !dateToZoom.isValid() ) {
545  newBegin = begin.addDays(-1);
546  count = ndays+3 ;
547  } else {
548  newBegin = dateToZoom.addDays( -ndays/2-1 );
549  count = ndays+3;
550  }
551 
552  if ( abs( count ) >= 31 )
553  kdDebug(5850) << "change to the mounth view?"<<endl;
554  else
555  //We want to center the date
556  emit zoomViewHorizontally( newBegin, count );
557 }
558 
559 void KOAgendaView::zoomView( const int delta, const TQPoint &pos,
560  const TQt::Orientation orient )
561 {
562  static TQDate zoomDate;
563  static TQTimer *t = new TQTimer( this );
564 
565 
566  //Zoom to the selected incidence, on the other way
567  // zoom to the date on screen after the first mousewheel move.
568  if ( orient == TQt::Horizontal ) {
569  TQDate date=mAgenda->selectedIncidenceDate();
570  if ( date.isValid() )
571  zoomDate=date;
572  else{
573  if ( !t->isActive() ) {
574  zoomDate= mSelectedDates[pos.x()];
575  }
576  t->start ( 1000,true );
577  }
578  if ( delta > 0 )
579  zoomOutHorizontally( zoomDate );
580  else
581  zoomInHorizontally( zoomDate );
582  } else {
583  // Vertical zoom
584  TQPoint posConstentsOld = mAgenda->gridToContents(pos);
585  if ( delta > 0 ) {
586  zoomOutVertically();
587  } else {
588  zoomInVertically();
589  }
590  TQPoint posConstentsNew = mAgenda->gridToContents(pos);
591  mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() );
592  }
593 }
594 
596 {
597 // kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl;
598 
599  // Check if mSelectedDates has changed, if not just return
600  // Removes some flickering and gains speed (since this is called by each updateView())
601  if ( !force && mSaveSelectedDates == mSelectedDates ) {
602  return;
603  }
604  mSaveSelectedDates = mSelectedDates;
605 
606  delete mDayLabels;
607  mDateDayLabels.clear();
608 
609  mDayLabels = new TQFrame (mDayLabelsFrame);
610  mLayoutDayLabels = new TQHBoxLayout(mDayLabels);
611  if ( !mIsSideBySide )
612  mLayoutDayLabels->addSpacing(mTimeLabels->width());
613 
614  const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
615 
616  DateList::ConstIterator dit;
617  for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) {
618  TQDate date = *dit;
619  TQBoxLayout *dayLayout = new TQVBoxLayout(mLayoutDayLabels);
620  mLayoutDayLabels->setStretchFactor(dayLayout, 1);
621 // dayLayout->setMinimumWidth(1);
622 
623  int dW = calsys->dayOfWeek(date);
624  TQString veryLongStr = TDEGlobal::locale()->formatDate( date );
625  TQString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" )
626  .arg( calsys->weekDayName( dW, true ) )
627  .arg( calsys->day(date) );
628  TQString shortstr = TQString::number(calsys->day(date));
629 
630  KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr,
631  longstr, veryLongStr, mDayLabels);
632  dayLabel->useShortText(); // will be recalculated in updateDayLabelSizes() anyway
633  dayLabel->setMinimumWidth(1);
634  dayLabel->setAlignment(TQLabel::AlignHCenter);
635  if (date == TQDate::currentDate()) {
636  TQFont font = dayLabel->font();
637  font.setBold(true);
638  dayLabel->setFont(font);
639  }
640  dayLayout->addWidget(dayLabel);
641  mDateDayLabels.append( dayLabel );
642 
643  // if a holiday region is selected, show the holiday name
644  TQStringList texts = KOGlobals::self()->holiday( date );
645  TQStringList::ConstIterator textit = texts.begin();
646  for ( ; textit != texts.end(); ++textit ) {
647  // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
648  KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), TQString(), mDayLabels );
649  label->setMinimumWidth(1);
650  label->setAlignment(AlignCenter);
651  dayLayout->addWidget(label);
652  }
653 
654 #ifndef KORG_NOPLUGINS
655  CalendarDecoration::List cds = KOCore::self()->calendarDecorations();
656  CalendarDecoration *it;
657  for(it = cds.first(); it; it = cds.next()) {
658  TQString text = it->shortText( date );
659  if ( !text.isEmpty() ) {
660  // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used
661  KOAlternateLabel*label = new KOAlternateLabel( text, text, TQString(), mDayLabels );
662  label->setMinimumWidth(1);
663  label->setAlignment(AlignCenter);
664  dayLayout->addWidget(label);
665  }
666  }
667 
668  for(it = cds.first(); it; it = cds.next()) {
669  TQWidget *wid = it->smallWidget(mDayLabels,date);
670  if ( wid ) {
671 // wid->setHeight(20);
672  dayLayout->addWidget(wid);
673  }
674  }
675 #endif
676  }
677 
678  if ( !mIsSideBySide )
679  mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width());
680  mDayLabels->show();
681  TQTimer::singleShot( 0, this, TQ_SLOT( updateDayLabelSizes() ) );
682 }
683 
684 void KOAgendaView::enableAgendaUpdate( bool enable )
685 {
686  mAllowAgendaUpdate = enable;
687 }
688 
690 {
691  // Not sure about the max number of events, so return 0 for now.
692  return 0;
693 }
694 
696 {
697  return mSelectedDates.count();
698 }
699 
701 {
702  Incidence::List selected;
703  Incidence *incidence;
704 
705  incidence = mAgenda->selectedIncidence();
706  if (incidence) selected.append(incidence);
707 
708  incidence = mAllDayAgenda->selectedIncidence();
709  if (incidence) selected.append(incidence);
710 
711  return selected;
712 }
713 
715 {
716  DateList selected;
717  TQDate qd;
718 
719  qd = mAgenda->selectedIncidenceDate();
720  if (qd.isValid()) selected.append(qd);
721 
722  qd = mAllDayAgenda->selectedIncidenceDate();
723  if (qd.isValid()) selected.append(qd);
724 
725  return selected;
726 }
727 
728 bool KOAgendaView::eventDurationHint( TQDateTime &startDt, TQDateTime &endDt,
729  bool &allDay )
730 {
731  if ( selectionStart().isValid() ) {
732  TQDateTime start = selectionStart();
733  TQDateTime end = selectionEnd();
734 
735  if ( start.secsTo( end ) == 15*60 ) {
736  // One cell in the agenda view selected, e.g.
737  // because of a double-click, => Use the default duration
738  TQTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() );
739  int addSecs = ( defaultDuration.hour()*3600 ) +
740  ( defaultDuration.minute()*60 );
741  end = start.addSecs( addSecs );
742  }
743 
744  startDt = start;
745  endDt = end;
746  allDay = selectedIsAllDay();
747  return true;
748  }
749  return false;
750 }
751 
754 {
755  if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false;
756 
757  if (selectedIsAllDay()) {
758  int days = selectionStart().daysTo(selectionEnd());
759  return ( days < 1 );
760  } else {
761  int secs = selectionStart().secsTo(selectionEnd());
762  return ( secs <= 24*60*60/mAgenda->rows() );
763  }
764 }
765 
766 
767 void KOAgendaView::updateView()
768 {
769 // kdDebug(5850) << "KOAgendaView::updateView()" << endl;
770  fillAgenda();
771 }
772 
773 
774 /*
775  Update configuration settings for the agenda view. This method is not
776  complete.
777 */
778 void KOAgendaView::updateConfig()
779 {
780 // kdDebug(5850) << "KOAgendaView::updateConfig()" << endl;
781 
782  // update config for children
783  mTimeLabels->updateConfig();
784  mAgenda->updateConfig();
785  mAllDayAgenda->updateConfig();
786 
787  // widget synchronization
788  // FIXME: find a better way, maybe signal/slot
789  mTimeLabels->positionChanged();
790 
791  // for some reason, this needs to be called explicitly
792  mTimeLabels->repaint();
793 
794  updateTimeBarWidth();
795 
796  // ToolTips displaying summary of events
797  KOAgendaItem::toolTipGroup()->setEnabled(KOPrefs::instance()
798  ->mEnableToolTips);
799 
800  setHolidayMasks();
801 
802  createDayLabels( true );
803 
804  updateView();
805 }
806 
807 void KOAgendaView::updateTimeBarWidth()
808 {
809  int width;
810 
811  width = mDummyAllDayLeft->fontMetrics().width( i18n("All Day") );
812  width = TQMAX( width, mTimeLabels->width() );
813 
814  mDummyAllDayLeft->setFixedWidth( width );
815  mTimeLabels->setFixedWidth( width );
816 }
817 
818 void KOAgendaView::updateDayLabelSizes()
819 {
820  // First, calculate the maximum text type that fits for all labels
821  KOAlternateLabel::TextType overallType = KOAlternateLabel::Extensive;
822  TQPtrList<KOAlternateLabel>::const_iterator it = mDateDayLabels.constBegin();
823  for( ; it != mDateDayLabels.constEnd(); it++ ) {
824  KOAlternateLabel::TextType type = (*it)->largestFittingTextType();
825  if ( type < overallType )
826  overallType = type;
827  }
828 
829  // Then, set that maximum text type to all the labels
830  it = mDateDayLabels.constBegin();
831  for( ; it != mDateDayLabels.constEnd(); it++ ) {
832  (*it)->setFixedType( overallType );
833  }
834 }
835 
836 void KOAgendaView::resizeEvent( TQResizeEvent *resizeEvent )
837 {
838  updateDayLabelSizes();
839  KOrg::AgendaView::resizeEvent( resizeEvent );
840 }
841 
842 void KOAgendaView::updateEventDates( KOAgendaItem *item )
843 {
844  kdDebug(5850) << "KOAgendaView::updateEventDates(): " << item->text()
845  << "; item->cellXLeft(): " << item->cellXLeft()
846  << "; item->cellYTop(): " << item->cellYTop()
847  << "; item->lastMultiItem(): " << item->lastMultiItem()
848  << "; item->itemPos(): " << item->itemPos()
849  << "; item->itemCount(): " << item->itemCount()
850  << endl;
851 
852  TQDateTime startDt, endDt;
853 
854  // Start date of this incidence, calculate the offset from it (so recurring and
855  // non-recurring items can be treated exactly the same, we never need to check
856  // for doesRecur(), because we only move the start day by the number of days the
857  // agenda item was really moved. Smart, isn't it?)
858  TQDate thisDate;
859  if ( item->cellXLeft() < 0 ) {
860  thisDate = ( mSelectedDates.first() ).addDays( item->cellXLeft() );
861  } else {
862  thisDate = mSelectedDates[ item->cellXLeft() ];
863  }
864  TQDate oldThisDate( item->itemDate() );
865  const int daysOffset = oldThisDate.daysTo( thisDate );
866  int daysLength = 0;
867 
868  // startDt.setDate( startDate );
869 
870  Incidence *incidence = item->incidence();
871  if ( !incidence ) {
872  return;
873  }
874  if ( !mChanger ||
875  !mChanger->beginChange( incidence, resourceCalendar(), subResourceCalendar() ) ) {
876  return;
877  }
878  Incidence *oldIncidence = incidence->clone();
879 
880  TQTime startTime( 0, 0, 0 ), endTime( 0, 0, 0 );
881  if ( incidence->doesFloat() ) {
882  daysLength = item->cellWidth() - 1;
883  } else {
884  startTime = mAgenda->gyToTime( item->cellYTop() );
885  if ( item->lastMultiItem() ) {
886  endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 );
887  daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft();
888  kdDebug(5850) << "item->lastMultiItem()->cellXLeft(): " << item->lastMultiItem()->cellXLeft()
889  << endl;
890  } else if ( item->itemPos() == item->itemCount() && item->itemCount() > 1 ) {
891  /* multiitem handling in agenda assumes two things:
892  - The start (first KOAgendaItem) is always visible.
893  - The first KOAgendaItem of the incidence has a non-null item->lastMultiItem()
894  pointing to the last KOagendaItem.
895 
896  But those aren't always met, for example when in day-view.
897  kolab/issue4417
898  */
899 
900  // Cornercase 1: - Resizing the end of the event but the start isn't visible
901  endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
902  daysLength = item->itemCount() - 1;
903  startTime = incidence->dtStart().time();
904  } else if ( item->itemPos() == 1 && item->itemCount() > 1 ) {
905  // Cornercase 2: - Resizing the start of the event but the end isn't visible
906  endTime = incidence->dtEnd().time();
907  daysLength = item->itemCount() - 1;
908  } else {
909  endTime = mAgenda->gyToTime( item->cellYBottom() + 1 );
910  }
911  }
912 
913  kdDebug(5850) << "daysLength: " << daysLength << "; startTime: " << startTime
914  << "; endTime: " << endTime << "; thisDate: " << thisDate
915  << "; incidence->dtStart(): " << incidence->dtStart() << endl;
916 
917  // FIXME: use a visitor here
918  if ( incidence->type() == "Event" ) {
919  startDt = incidence->dtStart();
920  startDt = startDt.addDays( daysOffset );
921  startDt.setTime( startTime );
922  endDt = startDt.addDays( daysLength );
923  endDt.setTime( endTime );
924  Event* ev = static_cast<Event*>( incidence );
925  if ( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) {
926  // No change
927  delete oldIncidence;
928  return;
929  }
930  incidence->setDtStart( startDt );
931  ev->setDtEnd( endDt );
932  } else if ( incidence->type() == "Todo" ) {
933  Todo *td = static_cast<Todo*>( incidence );
934  startDt = td->hasStartDate() ? td->dtStart() : td->dtDue();
935  startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) );
936  startDt.setTime( startTime );
937  endDt.setDate( thisDate );
938  endDt.setTime( endTime );
939 
940  if( td->dtDue() == endDt ) {
941  // No change
942  delete oldIncidence;
943  return;
944  }
945  }
946  // FIXME: Adjusting the recurrence should really go to CalendarView so this
947  // functionality will also be available in other views!
948  // TODO_Recurrence: This does not belong here, and I'm not really sure
949  // how it's supposed to work anyway.
950 /*
951  Recurrence *recur = incidence->recurrence();
952  if ( recur->doesRecur() && daysOffset != 0 ) {
953  switch ( recur->recurrenceType() ) {
954  case Recurrence::rYearlyPos: {
955  int freq = recur->frequency();
956  int duration = recur->duration();
957  TQDate endDt( recur->endDate() );
958  bool negative = false;
959 
960  TQPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() );
961  if ( monthPos.first() ) {
962  negative = monthPos.first()->negative;
963  }
964  TQBitArray days( 7 );
965  int pos = 0;
966  days.fill( false );
967  days.setBit( thisDate.dayOfWeek() - 1 );
968  if ( negative ) {
969  pos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
970  } else {
971  pos = ( thisDate.day()-1 ) / 7 + 1;
972  }
973  // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
974  recur->unsetRecurs();
975  if ( duration != 0 ) {
976  recur->setYearly( Recurrence::rYearlyPos, freq, duration );
977  } else {
978  recur->setYearly( Recurrence::rYearlyPos, freq, endDt );
979  }
980  recur->addYearlyMonthPos( pos, days );
981  recur->addYearlyNum( thisDate.month() );
982 
983  break; }
984  case Recurrence::rYearlyDay: {
985  int freq = recur->frequency();
986  int duration = recur->duration();
987  TQDate endDt( recur->endDate() );
988  // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
989  recur->unsetRecurs();
990  if ( duration == 0 ) { // end by date
991  recur->setYearly( Recurrence::rYearlyDay, freq, endDt );
992  } else {
993  recur->setYearly( Recurrence::rYearlyDay, freq, duration );
994  }
995  recur->addYearlyNum( thisDate.dayOfYear() );
996  break; }
997  case Recurrence::rYearlyMonth: {
998  int freq = recur->frequency();
999  int duration = recur->duration();
1000  TQDate endDt( recur->endDate() );
1001  // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
1002  recur->unsetRecurs();
1003  if ( duration != 0 ) {
1004  recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration );
1005  } else {
1006  recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt );
1007  }
1008  recur->addYearlyNum( thisDate.month() );
1009  break; }
1010  case Recurrence::rMonthlyPos: {
1011  int freq = recur->frequency();
1012  int duration = recur->duration();
1013  TQDate endDt( recur->endDate() );
1014  TQPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() );
1015  if ( !monthPos.isEmpty() ) {
1016  // FIXME: How shall I adapt the day x of week Y if we move the date across month borders???
1017  // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
1018  // That's fine for korganizer, but might mess up other organizers.
1019  TQBitArray rDays( 7 );
1020  rDays = monthPos.first()->rDays;
1021  bool negative = monthPos.first()->negative;
1022  int newPos;
1023  rDays.fill( false );
1024  rDays.setBit( thisDate.dayOfWeek() - 1 );
1025  if ( negative ) {
1026  newPos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1;
1027  } else {
1028  newPos = ( thisDate.day()-1 ) / 7 + 1;
1029  }
1030 
1031  // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
1032  recur->unsetRecurs();
1033  if ( duration == 0 ) { // end by date
1034  recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt );
1035  } else {
1036  recur->setMonthly( Recurrence::rMonthlyPos, freq, duration );
1037  }
1038  recur->addMonthlyPos( newPos, rDays );
1039  }
1040  break;}
1041  case Recurrence::rMonthlyDay: {
1042  int freq = recur->frequency();
1043  int duration = recur->duration();
1044  TQDate endDt( recur->endDate() );
1045  TQPtrList<int> monthDays( recur->monthDays() );
1046  // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again
1047  recur->unsetRecurs();
1048  if ( duration == 0 ) { // end by date
1049  recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt );
1050  } else {
1051  recur->setMonthly( Recurrence::rMonthlyDay, freq, duration );
1052  }
1053  // FIXME: How shall I adapt the n-th day if we move the date across month borders???
1054  // for now, just use the date of the moved item and assume the recurrence only occurs on that day.
1055  // That's fine for korganizer, but might mess up other organizers.
1056  recur->addMonthlyDay( thisDate.day() );
1057 
1058  break;}
1059  case Recurrence::rWeekly: {
1060  TQBitArray days(7), oldDays( recur->days() );
1061  int offset = daysOffset % 7;
1062  if ( offset<0 ) offset = (offset+7) % 7;
1063  // rotate the days
1064  for (int d=0; d<7; d++ ) {
1065  days.setBit( (d+offset) % 7, oldDays.at(d) );
1066  }
1067  if ( recur->duration() == 0 ) { // end by date
1068  recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() );
1069  } else { // duration or no end
1070  recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() );
1071  }
1072  break;}
1073  // nothing to be done for the following:
1074  case Recurrence::rDaily:
1075  case Recurrence::rHourly:
1076  case Recurrence::rMinutely:
1077  case Recurrence::rNone:
1078  default:
1079  break;
1080  }
1081  if ( recur->duration()==0 ) { // end by date
1082  recur->setEndDate( recur->endDate().addDays( daysOffset ) );
1083  }
1084  KMessageBox::information( this, i18n("A recurring calendar item was moved "
1085  "to a different day. The recurrence settings "
1086  "have been updated with that move. Please check "
1087  "them in the editor."),
1088  i18n("Recurrence Moved"),
1089  "RecurrenceMoveInAgendaWarning" );
1090  }*/
1091 
1092  // FIXME: use a visitor here
1093  if ( incidence->type() == "Event" ) {
1094  incidence->setDtStart( startDt );
1095  static_cast<Event*>( incidence )->setDtEnd( endDt );
1096  } else if ( incidence->type() == "Todo" ) {
1097  Todo *td = static_cast<Todo*>( incidence );
1098  if ( td->hasStartDate() ) {
1099  td->setDtStart( startDt );
1100  }
1101  td->setDtDue( endDt );
1102  }
1103 
1104  item->setItemDate( startDt.date() );
1105 
1106  KOIncidenceToolTip::remove( item );
1107  KOIncidenceToolTip::add( item, calendar(), incidence, thisDate, KOAgendaItem::toolTipGroup() );
1108 
1109  const bool result = mChanger->changeIncidence( oldIncidence, incidence,
1110  KOGlobals::DATE_MODIFIED, this );
1111  mChanger->endChange( incidence, resourceCalendar(), subResourceCalendar() );
1112  delete oldIncidence;
1113 
1114  if ( !result ) {
1115  mPendingChanges = true;
1116  TQTimer::singleShot( 0, this, TQ_SLOT(updateView()) );
1117  return;
1118  }
1119 
1120  // don't update the agenda as the item already has the correct coordinates.
1121  // an update would delete the current item and recreate it, but we are still
1122  // using a pointer to that item! => CRASH
1123  enableAgendaUpdate( false );
1124  // We need to do this in a timer to make sure we are not deleting the item
1125  // we are currently working on, which would lead to crashes
1126  // Only the actually moved agenda item is already at the correct position and mustn't be
1127  // recreated. All others have to!!!
1128  if ( incidence->doesRecur() ) {
1129  mUpdateItem = incidence;
1130  TQTimer::singleShot( 0, this, TQ_SLOT( doUpdateItem() ) );
1131  }
1132 
1133  enableAgendaUpdate( true );
1134 
1135 // kdDebug(5850) << "KOAgendaView::updateEventDates() done " << endl;
1136 }
1137 
1139 {
1140  if ( mUpdateItem ) {
1141  changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED );
1142  mUpdateItem = 0;
1143  }
1144 }
1145 
1146 
1147 
1148 void KOAgendaView::showDates( const TQDate &start, const TQDate &end )
1149 {
1150 // kdDebug(5850) << "KOAgendaView::selectDates" << endl;
1151  if ( !mSelectedDates.isEmpty() && mSelectedDates.first() == start
1152  && mSelectedDates.last() == end && !mPendingChanges )
1153  return;
1154 
1155  mSelectedDates.clear();
1156 
1157  TQDate d = start;
1158  while ( d <= end ) {
1159  mSelectedDates.append( d );
1160  d = d.addDays( 1 );
1161  }
1162 
1163  mAreDatesInitialized = true;
1164 
1165  // and update the view
1166  fillAgenda();
1167 }
1168 
1169 
1170 void KOAgendaView::showIncidences( const Incidence::List &, const TQDate & )
1171 {
1172  kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl;
1173 }
1174 
1175 void KOAgendaView::insertIncidence( Incidence *incidence, const TQDate &curDate )
1176 {
1177  if ( !filterByResource( incidence ) ) {
1178  return;
1179  }
1180 
1181  // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's
1182  Event *event = dynamic_cast<Event *>( incidence );
1183  Todo *todo = dynamic_cast<Todo *>( incidence );
1184 
1185  int curCol = mSelectedDates.first().daysTo( curDate );
1186 
1187  // In case incidence->dtStart() isn't visible (crosses bounderies)
1188  if ( curCol < 0 ) {
1189  curCol = 0;
1190  }
1191 
1192  // The date for the event is not displayed, just ignore it
1193  if ( curCol >= int( mSelectedDates.count() ) ) {
1194  return;
1195  }
1196 
1197  // Default values, which can never be reached
1198  mMinY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) ) + 1;
1199  mMaxY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) ) - 1;
1200 
1201  int beginX;
1202  int endX;
1203  TQDate columnDate;
1204  if ( event ) {
1205  TQDate firstVisibleDate = mSelectedDates.first();
1206  // its crossing bounderies, lets calculate beginX and endX
1207  if ( curDate < firstVisibleDate ) {
1208  beginX = curCol + firstVisibleDate.daysTo( curDate );
1209  endX = beginX + event->dtStart().daysTo( event->dtEnd() );
1210  columnDate = firstVisibleDate;
1211  } else {
1212  beginX = curCol;
1213  endX = beginX + event->dtStart().daysTo( event->dtEnd() );
1214  columnDate = curDate;
1215  }
1216  } else if ( todo ) {
1217  if ( !todo->hasDueDate() ) {
1218  return; // todo shall not be displayed if it has no date
1219  }
1220  columnDate = curDate;
1221  beginX = endX = curCol;
1222 
1223  } else {
1224  return;
1225  }
1226  if ( todo && todo->isOverdue() ) {
1227  mAllDayAgenda->insertAllDayItem( incidence, columnDate, curCol, curCol );
1228  } else if ( incidence->doesFloat() ||
1229  ( todo &&
1230  !todo->dtDue().isValid() ) ) {
1231  mAllDayAgenda->insertAllDayItem( incidence, columnDate, beginX, endX );
1232  } else if ( event && event->isMultiDay() ) {
1233  int startY = mAgenda->timeToY( event->dtStart().time() );
1234  TQTime endtime = event->dtEnd().time();
1235  if ( endtime == TQTime( 0, 0, 0 ) ) {
1236  endtime = TQTime( 23, 59, 59 );
1237  }
1238  int endY = mAgenda->timeToY( endtime ) - 1;
1239  if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) {
1240  mAgenda->insertMultiItem( event, columnDate, beginX, endX, startY, endY );
1241 
1242  }
1243  if ( beginX == curCol ) {
1244  mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) );
1245  if ( startY < mMinY[curCol] ) {
1246  mMinY[curCol] = startY;
1247  }
1248  } else if ( endX == curCol ) {
1249  mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) );
1250  if ( endY > mMaxY[curCol] ) {
1251  mMaxY[curCol] = endY;
1252  }
1253  } else {
1254  mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) );
1255  mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) );
1256  }
1257  } else {
1258  int startY = 0, endY = 0;
1259  if ( event ) {
1260  startY = mAgenda->timeToY( incidence->dtStart().time() );
1261  TQTime endtime = event->dtEnd().time();
1262  if ( endtime == TQTime( 0, 0, 0 ) ) {
1263  endtime = TQTime( 23, 59, 59 );
1264  }
1265  endY = mAgenda->timeToY( endtime ) - 1;
1266  }
1267  if ( todo ) {
1268  TQTime t = todo->dtDue().time();
1269 
1270  if ( t == TQTime( 0, 0 ) ) {
1271  t = TQTime( 23, 59 );
1272  }
1273 
1274  int halfHour = 1800;
1275  if ( t.addSecs( -halfHour ) < t ) {
1276  startY = mAgenda->timeToY( t.addSecs( -halfHour ) );
1277  endY = mAgenda->timeToY( t ) - 1;
1278  } else {
1279  startY = 0;
1280  endY = mAgenda->timeToY( t.addSecs( halfHour ) ) - 1;
1281  }
1282  }
1283  if ( endY < startY ) {
1284  endY = startY;
1285  }
1286  mAgenda->insertItem( incidence, columnDate, curCol, startY, endY, 1, 1 );
1287  if ( startY < mMinY[curCol] ) {
1288  mMinY[curCol] = startY;
1289  }
1290  if ( endY > mMaxY[curCol] ) {
1291  mMaxY[curCol] = endY;
1292  }
1293  }
1294 }
1295 
1296 void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence )
1297 {
1298  Todo *todo = dynamic_cast<Todo *>(incidence);
1299  CalFilter *filter = calendar()->filter();
1300  if ( ( filter && !filter->filterIncidence( incidence ) ) ||
1301  ( ( todo && !KOPrefs::instance()->showAllDayTodo() ) ) ) {
1302  return;
1303  }
1304 
1305  displayIncidence( incidence );
1306 }
1307 
1308 void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode )
1309 {
1310  switch ( mode ) {
1311  case KOGlobals::INCIDENCEADDED:
1312  {
1313  // Add an event. No need to recreate the whole view!
1314  // recreating everything even causes troubles: dropping to the
1315  // day matrix recreates the agenda items, but the evaluation is
1316  // still in an agendaItems' code, which was deleted in the mean time.
1317  // Thus KOrg crashes...
1318  changeIncidenceDisplayAdded( incidence );
1320  break;
1321  }
1322  case KOGlobals::INCIDENCEEDITED:
1323  {
1324  if ( mAllowAgendaUpdate ) {
1325  removeIncidence( incidence );
1326  changeIncidenceDisplayAdded( incidence );
1327  }
1329  break;
1330  }
1331  case KOGlobals::INCIDENCEDELETED:
1332  {
1333  removeIncidence( incidence );
1335  break;
1336  }
1337  default:
1338  return;
1339  }
1340 
1341  // HACK: Update the view if the all-day agenda has been modified.
1342  // Do this because there are some layout problems in the
1343  // all-day agenda that are not easily solved, but clearing
1344  // and redrawing works ok.
1345  if ( incidence->doesFloat() ) {
1346  updateView();
1347  }
1348 }
1349 
1350 void KOAgendaView::fillAgenda( const TQDate & )
1351 {
1352  fillAgenda();
1353 }
1354 
1356 {
1357  if ( !mAreDatesInitialized ) {
1358  return;
1359  }
1360 
1361  mPendingChanges = false;
1362 
1363  /* Remember the uids of the selected items. In case one of the
1364  * items was deleted and re-added, we want to reselect it. */
1365  const TQString &selectedAgendaUid = mAgenda->lastSelectedUid();
1366  const TQString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid();
1367 
1368  enableAgendaUpdate( true );
1369  clearView();
1370 
1371  mAllDayAgenda->changeColumns( mSelectedDates.count() );
1372  mAgenda->changeColumns( mSelectedDates.count() );
1373  mEventIndicatorTop->changeColumns( mSelectedDates.count() );
1374  mEventIndicatorBottom->changeColumns( mSelectedDates.count() );
1375 
1376  createDayLabels( false );
1377  setHolidayMasks();
1378 
1379  mMinY.resize( mSelectedDates.count() );
1380  mMaxY.resize( mSelectedDates.count() );
1381 
1382  mAgenda->setDateList( mSelectedDates );
1383 
1384  bool somethingReselected = false;
1385  Incidence::List incidences = calendar()->incidences();
1386 
1387  for ( Incidence::List::ConstIterator it = incidences.begin(); it!=incidences.constEnd(); ++it ) {
1388  Incidence *incidence = (*it);
1389  displayIncidence( incidence );
1390 
1391  if( incidence->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) {
1392  mAgenda->selectItemByUID( incidence->uid() );
1393  somethingReselected = true;
1394  }
1395 
1396  if( incidence->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) {
1397  mAllDayAgenda->selectItemByUID( incidence->uid() );
1398  somethingReselected = true;
1399  }
1400 
1401  }
1402 
1403  mAgenda->checkScrollBoundaries();
1405 
1406  // mAgenda->viewport()->update();
1407  // mAllDayAgenda->viewport()->update();
1408 
1409  // make invalid
1411 
1412  if( !somethingReselected ) {
1413  emit incidenceSelected( 0, TQDate() );
1414  }
1415 }
1416 
1417 void KOAgendaView::displayIncidence( Incidence *incidence )
1418 {
1419  TQDate today = TQDate::currentDate();
1420  DateTimeList::iterator t;
1421 
1422  // FIXME: use a visitor here
1423  Todo *todo = dynamic_cast<Todo *>( incidence );
1424  Event *event = dynamic_cast<Event *>( incidence );
1425 
1426  TQDateTime firstVisibleDateTime = mSelectedDates.first();
1427  TQDateTime lastVisibleDateTime = mSelectedDates.last();
1428 
1429  lastVisibleDateTime.setTime( TQTime( 23, 59, 59, 59 ) );
1430  firstVisibleDateTime.setTime( TQTime( 0, 0 ) );
1431  DateTimeList dateTimeList;
1432 
1433  TQDateTime incDtStart = incidence->dtStart();
1434  TQDateTime incDtEnd = incidence->dtEnd();
1435 
1436  if ( todo &&
1437  ( !KOPrefs::instance()->showAllDayTodo() || !todo->hasDueDate() ) ) {
1438  return;
1439  }
1440 
1441  if ( incidence->doesRecur() ) {
1442  int eventDuration = event ? incDtStart.daysTo( incDtEnd ) : 0;
1443 
1444  // if there's a multiday event that starts before firstVisibleDateTime but ends after
1445  // lets include it. timesInInterval() ignores incidences that aren't totaly inside
1446  // the range
1447  TQDateTime startDateTimeWithOffset = firstVisibleDateTime.addDays( -eventDuration );
1448  dateTimeList =
1449  incidence->recurrence()->timesInInterval( startDateTimeWithOffset,
1450  lastVisibleDateTime );
1451  } else {
1452  TQDateTime dateToAdd; // date to add to our date list
1453  TQDateTime incidenceStart;
1454  TQDateTime incidenceEnd;
1455 
1456  if ( todo && todo->hasDueDate() && !todo->isOverdue() ) {
1457  // If it's not overdue it will be shown at the original date (not today)
1458  dateToAdd = todo->dtDue();
1459 
1460  // To-dos are drawn with the bottom of the rectangle at dtDue
1461  // if dtDue is at 00:00, then it should be displayed in the previous day, at 23:59
1462  if ( !todo->doesFloat() && dateToAdd.time() == TQTime( 0, 0 ) ) {
1463  dateToAdd = dateToAdd.addSecs( -1 );
1464  }
1465 
1466  incidenceEnd = dateToAdd;
1467  } else if ( event ) {
1468  dateToAdd = incDtStart;
1469  incidenceEnd = incDtEnd;
1470  }
1471 
1472  if ( incidence->doesFloat() ) {
1473  // so comparisons with < > actually work
1474  dateToAdd.setTime( TQTime( 0, 0 ) );
1475  incidenceEnd.setTime( TQTime( 23, 59, 59, 59 ) );
1476  }
1477 
1478  if ( dateToAdd <= lastVisibleDateTime && incidenceEnd > firstVisibleDateTime ) {
1479  dateTimeList += dateToAdd;
1480  }
1481  }
1482 
1483  // ToDo items shall be displayed today if they are already overdude
1484  TQDateTime dateTimeToday = today;
1485  if ( todo &&
1486  todo->isOverdue() &&
1487  dateTimeToday >= firstVisibleDateTime &&
1488  dateTimeToday <= lastVisibleDateTime ) {
1489 
1490  bool doAdd = true;
1491 
1492  if ( todo->doesRecur() ) {
1493  /* If there's a recurring instance showing up today don't add "today" again
1494  * we don't want the event to appear duplicated */
1495  for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
1496  if ( (*t).date() == today ) {
1497  doAdd = false;
1498  break;
1499  }
1500  }
1501  }
1502 
1503  if ( doAdd ) {
1504  dateTimeList += dateTimeToday;
1505  }
1506  }
1507 
1508  for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) {
1509  insertIncidence( incidence, (*t).date() );
1510  }
1511 }
1512 
1514 {
1515 // kdDebug(5850) << "ClearView" << endl;
1516  mAllDayAgenda->clear();
1517  mAgenda->clear();
1518 }
1519 
1520 CalPrinterBase::PrintType KOAgendaView::printType()
1521 {
1522  if ( currentDateCount() == 1 ) return CalPrinterBase::Day;
1523  else return CalPrinterBase::Week;
1524 }
1525 
1526 void KOAgendaView::updateEventIndicatorTop( int newY )
1527 {
1528  uint i;
1529  for( i = 0; i < mMinY.size(); ++i ) {
1530  mEventIndicatorTop->enableColumn( i, newY > mMinY[i] );
1531  }
1532  mEventIndicatorTop->update();
1533 }
1534 
1535 void KOAgendaView::updateEventIndicatorBottom( int newY )
1536 {
1537  uint i;
1538  for( i = 0; i < mMaxY.size(); ++i ) {
1539  mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] );
1540  }
1541  mEventIndicatorBottom->update();
1542 }
1543 
1544 void KOAgendaView::slotTodoDropped( Todo *todo, const TQPoint &gpos, bool allDay )
1545 {
1546  if ( gpos.x()<0 || gpos.y()<0 ) return;
1547  TQDate day = mSelectedDates[gpos.x()];
1548  TQTime time = mAgenda->gyToTime( gpos.y() );
1549  TQDateTime newTime( day, time );
1550 
1551  if ( todo ) {
1552  Todo *existingTodo = calendar()->todo( todo->uid() );
1553  if ( existingTodo ) {
1554  kdDebug(5850) << "Drop existing Todo" << endl;
1555  Todo *oldTodo = existingTodo->clone();
1556  if ( mChanger &&
1557  mChanger->beginChange( existingTodo, resourceCalendar(), subResourceCalendar() ) ) {
1558  existingTodo->setDtDue( newTime );
1559  existingTodo->setFloats( allDay );
1560  existingTodo->setHasDueDate( true );
1561  mChanger->changeIncidence( oldTodo, existingTodo,
1562  KOGlobals::DATE_MODIFIED, this );
1563  mChanger->endChange( existingTodo, resourceCalendar(), subResourceCalendar() );
1564  } else {
1565  KMessageBox::sorry( this, i18n("Unable to modify this to-do, "
1566  "because it cannot be locked.") );
1567  }
1568  delete oldTodo;
1569  } else {
1570  kdDebug(5850) << "Drop new Todo" << endl;
1571  todo->setDtDue( newTime );
1572  todo->setFloats( allDay );
1573  todo->setHasDueDate( true );
1574  if ( !mChanger->addIncidence( todo, 0, TQString(), this ) ) {
1575  KODialogManager::errorSaveIncidence( this, todo );
1576  }
1577  }
1578  }
1579 }
1580 
1581 void KOAgendaView::startDrag( Incidence *incidence )
1582 {
1583 #ifndef KORG_NODND
1584  DndFactory factory( calendar() );
1585  ICalDrag *vd = factory.createDrag( incidence, this );
1586  if ( vd->drag() ) {
1587  kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl;
1588  }
1589 #endif
1590 }
1591 
1592 void KOAgendaView::readSettings()
1593 {
1594  readSettings(KOGlobals::self()->config());
1595 }
1596 
1597 void KOAgendaView::readSettings(TDEConfig *config)
1598 {
1599 // kdDebug(5850) << "KOAgendaView::readSettings()" << endl;
1600 
1601  config->setGroup("Views");
1602 
1603 #ifndef KORG_NOSPLITTER
1604  TQValueList<int> sizes = config->readIntListEntry("Separator AgendaView");
1605  if (sizes.count() == 2) {
1606  mSplitterAgenda->setSizes(sizes);
1607  }
1608 #endif
1609 
1610  updateConfig();
1611 }
1612 
1613 void KOAgendaView::writeSettings(TDEConfig *config)
1614 {
1615 // kdDebug(5850) << "KOAgendaView::writeSettings()" << endl;
1616 
1617  config->setGroup("Views");
1618 
1619 #ifndef KORG_NOSPLITTER
1620  TQValueList<int> list = mSplitterAgenda->sizes();
1621  config->writeEntry("Separator AgendaView",list);
1622 #endif
1623 }
1624 
1626 {
1627  if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) {
1628  return;
1629  }
1630 
1631  mHolidayMask.resize( mSelectedDates.count() + 1 );
1632 
1633  for( uint i = 0; i < mSelectedDates.count(); ++i ) {
1634  mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] );
1635  }
1636 
1637  // Store the information about the day before the visible area (needed for
1638  // overnight working hours) in the last bit of the mask:
1639  bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) );
1640  mHolidayMask[ mSelectedDates.count() ] = showDay;
1641 
1642  mAgenda->setHolidayMask( &mHolidayMask );
1643  mAllDayAgenda->setHolidayMask( &mHolidayMask );
1644 }
1645 
1646 void KOAgendaView::setContentsPos( int y )
1647 {
1648  mAgenda->setContentsPos( 0, y );
1649 }
1650 
1651 void KOAgendaView::setExpandedButton( bool expanded )
1652 {
1653  if ( !mExpandButton ) return;
1654 
1655  if ( expanded ) {
1656  mExpandButton->setPixmap( mExpandedPixmap );
1657  } else {
1658  mExpandButton->setPixmap( mNotExpandedPixmap );
1659  }
1660 }
1661 
1662 void KOAgendaView::clearSelection()
1663 {
1664  mAgenda->deselectItem();
1665  mAllDayAgenda->deselectItem();
1666 }
1667 
1668 void KOAgendaView::newTimeSpanSelectedAllDay( const TQPoint &start, const TQPoint &end )
1669 {
1670  newTimeSpanSelected( start, end );
1671  mTimeSpanInAllDay = true;
1672 }
1673 
1674 void KOAgendaView::newTimeSpanSelected( const TQPoint &start, const TQPoint &end )
1675 {
1676  if (!mSelectedDates.count()) return;
1677 
1678  mTimeSpanInAllDay = false;
1679 
1680  TQDate dayStart = mSelectedDates[ kClamp( start.x(), 0, (int)mSelectedDates.size() - 1 ) ];
1681  TQDate dayEnd = mSelectedDates[ kClamp( end.x(), 0, (int)mSelectedDates.size() - 1 ) ];
1682 
1683  TQTime timeStart = mAgenda->gyToTime(start.y());
1684  TQTime timeEnd = mAgenda->gyToTime( end.y() + 1 );
1685 
1686  TQDateTime dtStart(dayStart,timeStart);
1687  TQDateTime dtEnd(dayEnd,timeEnd);
1688 
1689  mTimeSpanBegin = dtStart;
1690  mTimeSpanEnd = dtEnd;
1691 }
1692 
1694 {
1695  mTimeSpanBegin.setDate(TQDate());
1696  mTimeSpanEnd.setDate(TQDate());
1697  mTimeSpanInAllDay = false;
1698 }
1699 
1700 void KOAgendaView::setTypeAheadReceiver( TQObject *o )
1701 {
1702  mAgenda->setTypeAheadReceiver( o );
1703  mAllDayAgenda->setTypeAheadReceiver( o );
1704 }
1705 
1706 void KOAgendaView::finishTypeAhead()
1707 {
1708  mAgenda->finishTypeAhead();
1709  mAllDayAgenda->finishTypeAhead();
1710 }
1711 
1712 void KOAgendaView::removeIncidence( Incidence *incidence )
1713 {
1714  mAgenda->removeIncidence( incidence );
1715  mAllDayAgenda->removeIncidence( incidence );
1716 }
1717 
1719 {
1720  mMinY = mAgenda->minContentsY();
1721  mMaxY = mAgenda->maxContentsY();
1722 
1723  mAgenda->checkScrollBoundaries();
1724  updateEventIndicatorTop( mAgenda->visibleContentsYMin() );
1725  updateEventIndicatorBottom( mAgenda->visibleContentsYMax() );
1726 }
1727 
1728 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer )
1729 {
1730  mChanger = changer;
1731  mAgenda->setIncidenceChanger( changer );
1732  mAllDayAgenda->setIncidenceChanger( changer );
1733 }
1734 
1735 void KOAgendaView::clearTimeSpanSelection()
1736 {
1737  mAgenda->clearSelection();
1738  mAllDayAgenda->clearSelection();
1740 }
1741 
1742 bool KOAgendaView::filterByResource( Incidence *incidence )
1743 {
1744  // Special handling for groupware to-dos that are in Task folders.
1745  // Put them in the top-level "Calendar" folder for lack of a better
1746  // place since we never show Task type folders even in the
1747  // multiagenda view.
1748  if ( resourceCalendar() && incidence->type() == "Todo" ) {
1749  TQString subRes = resourceCalendar()->subresourceIdentifier( incidence );
1750  if ( resourceCalendar()->subresourceType( subRes ) == "todo" ) {
1751  TQString calmatch = "/.INBOX.directory/Calendar";
1752  TQString i18nmatch = "/.INBOX.directory/" + i18n( "Calendar" );
1753  if ( subResourceCalendar().contains( calmatch ) ||
1754  subResourceCalendar().contains( i18nmatch ) ) {
1755  return true;
1756  }
1757  }
1758  }
1759 
1760  // Normal handling
1761  if ( !resourceCalendar() )
1762  return true;
1763  CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() );
1764  if ( !calRes )
1765  return true;
1766  if ( calRes->resource( incidence ) != resourceCalendar() )
1767  return false;
1768  if ( !subResourceCalendar().isEmpty() ) {
1769  if ( resourceCalendar()->subresourceIdentifier( incidence ) != subResourceCalendar() )
1770  return false;
1771  }
1772  return true;
1773 }
1774 
1775 void KOAgendaView::resourcesChanged()
1776 {
1777  mPendingChanges = true;
1778 }
1779 
1780 void KOAgendaView::calendarIncidenceAdded(Incidence * incidence)
1781 {
1782  Q_UNUSED( incidence );
1783  mPendingChanges = true;
1784 }
1785 
1786 void KOAgendaView::calendarIncidenceChanged(Incidence * incidence)
1787 {
1788  Q_UNUSED( incidence );
1789  mPendingChanges = true;
1790 }
1791 
1792 void KOAgendaView::calendarIncidenceDeleted(Incidence * incidence)
1793 {
1794  Q_UNUSED( incidence );
1795  mPendingChanges = true;
1796 }
This is the main calendar widget.
Definition: calendarview.h:82
bool filterIncidence(Incidence *) const
ResourceCalendar * resource(Incidence *incidence)
virtual Incidence::List incidences()
virtual Todo * todo(const TQString &uid)=0
void registerObserver(Observer *observer)
void unregisterObserver(Observer *observer)
CalFilter * filter()
virtual TQDateTime dtEnd() const
void setDtEnd(const TQDateTime &dtEnd)
bool doesFloat() const
TQString uid() const
virtual TQDateTime dtStart() const
virtual Incidence * clone()=0
virtual TQDateTime dtEnd() const
void setFloats(bool f)
bool doesRecur() const
virtual void setDtStart(const TQDateTime &dtStart)
Recurrence * recurrence() const
DateTimeList timesInInterval(const TQDateTime &start, const TQDateTime &end) const
virtual TQString subresourceIdentifier(Incidence *incidence)
bool hasDueDate() const
bool hasStartDate() const
Todo * clone()
void setDtDue(const TQDateTime &dtDue, bool first=false)
bool isOverdue() const
TQDateTime dtStart(bool first=false) const
void setDtStart(const TQDateTime &dtStart)
TQDateTime dtDue(bool first=false) const
void setHasDueDate(bool hasDueDate)
bool selectedIsSingleCell()
returns if only a single cell is selected, or a range of cells
void doUpdateItem()
update just the display of the given incidence, called by a single-shot timer
void newTimeSpanSelectedAllDay(const TQPoint &start, const TQPoint &end)
Updates data for selected timespan for all day event.
void fillAgenda()
Fill agenda using the current set value for the start date.
TQDateTime selectionEnd()
end-datetime of selection
Definition: koagendaview.h:143
virtual int maxDatesHint()
Returns maximum number of days supported by the koagendaview.
void clearView()
Remove all events from view.
void createDayLabels(bool force)
Create labels for the selected dates.
void updateEventIndicators()
Updates the event indicators after a certain incidence was modified or removed.
void setHolidayMasks()
Set the masks on the agenda widgets indicating, which days are holidays.
TQDateTime selectionStart()
start-datetime of selection
Definition: koagendaview.h:141
void updateEventDates(KOAgendaItem *item)
Update event belonging to agenda item.
virtual DateList selectedIncidenceDates()
returns the currently selected events
void newTimeSpanSelected(const TQPoint &start, const TQPoint &end)
Updates data for selected timespan.
virtual int currentDateCount()
Returns number of currently shown dates.
void slotTodoDropped(Todo *, const TQPoint &, bool)
reschedule the todo to the given x- and y- coordinates.
virtual bool eventDurationHint(TQDateTime &startDt, TQDateTime &endDt, bool &allDay)
return the default start/end date/time for new events
void deleteSelectedDateTime()
make selected start/end invalid
virtual Incidence::List selectedIncidences()
returns the currently selected events
bool selectedIsAllDay()
returns true if selection is for whole day
Definition: koagendaview.h:145
static void add(TQWidget *widget, Calendar *calendar, Incidence *incidence, const TQDate &date=TQDate(), TQToolTipGroup *group=0, const TQString &longText="")
Base class for single/multi agenda views.
Definition: agendaview.h:28
void editIncidenceSignal(Incidence *, const TQDate &)
instructs the receiver to begin editing the incidence specified in some manner.
ResourceCalendar * resourceCalendar()
Return resourceCalendar of this view.
Definition: baseview.h:100
void deleteIncidenceSignal(Incidence *)
instructs the receiver to delete the Incidence in some manner; some possibilities include automatical...
void newEventSignal(ResourceCalendar *res, const TQString &subResource)
instructs the receiver to create a new event.
TQString subResourceCalendar() const
Return subResourceCalendar of this view.
Definition: baseview.h:105
void showIncidenceSignal(Incidence *, const TQDate &)
instructs the receiver to show the incidence in read-only mode.
virtual Calendar * calendar()
Return calendar object of this view.
Definition: baseview.h:89
This class provides the interface for a date dependent decoration.
virtual TQString shortText(const TQDate &)
Return a short text for a given date, ususally only a few words.
virtual TQWidget * smallWidget(TQWidget *, const TQDate &)
Return a small widget.