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