korganizer

komonthview.cpp
1 /*
2  This file is part of KOrganizer.
3 
4  Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
5  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 
21  As a special exception, permission is given to link this program
22  with any edition of TQt, and distribute the resulting executable,
23  without including the source code for TQt in the source distribution.
24 */
25 
26 #include <tqpopupmenu.h>
27 #include <tqfont.h>
28 #include <tqfontmetrics.h>
29 #include <tqkeycode.h>
30 #include <tqhbox.h>
31 #include <tqvbox.h>
32 #include <tqpushbutton.h>
33 #include <tqtooltip.h>
34 #include <tqpainter.h>
35 #include <tqcursor.h>
36 #include <tqlistbox.h>
37 #include <tqlayout.h>
38 #include <tqlabel.h>
39 
40 #include <kdebug.h>
41 #include <tdelocale.h>
42 #include <tdeglobal.h>
43 #include <tdeconfig.h>
44 #include <kiconloader.h>
45 #include <kwordwrap.h>
46 
47 #include <kcalendarsystem.h>
48 #include <libkcal/calfilter.h>
49 #include <libkcal/calendar.h>
50 #include <libkcal/incidenceformatter.h>
52 
53 #include "koprefs.h"
54 #include "koglobals.h"
55 #include "koincidencetooltip.h"
56 #include "koeventpopupmenu.h"
57 #include "kohelper.h"
58 
59 #include "komonthview.h"
60 #include "komonthview.moc"
61 
62 //--------------------------------------------------------------------------
63 
64 KOMonthCellToolTip::KOMonthCellToolTip( TQWidget *parent,
65  Calendar *calendar,
66  const TQDate &date,
67  KNoScrollListBox *lv,
68  MonthViewCell* cl )
69  : TQToolTip( parent ), mCalendar( calendar ), mDate( date ), cell(cl)
70 {
71  eventlist = lv;
72 }
73 
74 void KOMonthCellToolTip::maybeTip( const TQPoint &pos )
75 {
76  TQRect r;
77  TQListBoxItem *it = eventlist->itemAt( pos );
78  MonthViewItem *i = static_cast<MonthViewItem*>( it );
79 
80  if( i && KOPrefs::instance()->mEnableToolTips ) {
81  /* Calculate the rectangle. */
82  r=eventlist->itemRect( it );
83  /* Show the tip */
84  TQString tipText( IncidenceFormatter::toolTipStr( mCalendar, i->incidence(), mDate ) );
85  if(tipText.isEmpty()) tipText = cell->holidayString();
86  if ( !tipText.isEmpty() ) {
87  tip( r, tipText );
88  }
89  }
90 }
91 
92 KNoScrollListBox::KNoScrollListBox( TQWidget *parent, const char *name )
93  : TQListBox( parent, name ),
94  mSqueezing( false )
95 {
96  TQPalette pal = palette();
97  pal.setColor( TQColorGroup::Foreground, KOPrefs::instance()->agendaBgColor().dark( 150 ) );
98  pal.setColor( TQColorGroup::Base, KOPrefs::instance()->agendaBgColor() );
99  setPalette( pal );
100 }
101 
102 void KNoScrollListBox::setBackground( bool primary, bool workDay )
103 {
104  TQColor color;
105  if ( workDay ) {
106  color = KOPrefs::instance()->workingHoursColor();
107  } else {
108  color = KOPrefs::instance()->agendaBgColor();
109  }
110 
111  TQPalette pal = palette();
112  if ( primary ) {
113  pal.setColor( TQColorGroup::Base, color );
114  } else {
115  pal.setColor( TQColorGroup::Base, color.dark( 115 ) );
116  }
117  setPalette( pal );
118 }
119 
120 void KNoScrollListBox::keyPressEvent( TQKeyEvent *e )
121 {
122  switch( e->key() ) {
123  case Key_Right:
124  scrollBy( 4, 0 );
125  break;
126  case Key_Left:
127  scrollBy( -4, 0 );
128  break;
129  case Key_Up:
130  if ( !count() ) break;
131  setCurrentItem( ( currentItem() + count() - 1 ) % count() );
132  if ( !itemVisible( currentItem() ) ) {
133  if ( (unsigned int)currentItem() == ( count() - 1 ) ) {
134  setTopItem( currentItem() - numItemsVisible() + 1 );
135  } else {
136  setTopItem( topItem() - 1 );
137  }
138  }
139  break;
140  case Key_Down:
141  if ( !count() ) break;
142  setCurrentItem( ( currentItem() + 1 ) % count() );
143  if( !itemVisible( currentItem() ) ) {
144  if( currentItem() == 0 ) {
145  setTopItem( 0 );
146  } else {
147  setTopItem( topItem() + 1 );
148  }
149  }
150  case Key_Shift:
151  emit shiftDown();
152  break;
153  default:
154  break;
155  }
156 }
157 
158 void KNoScrollListBox::keyReleaseEvent( TQKeyEvent *e )
159 {
160  switch( e->key() ) {
161  case Key_Shift:
162  emit shiftUp();
163  break;
164  default:
165  break;
166  }
167 }
168 
169 void KNoScrollListBox::mousePressEvent( TQMouseEvent *e )
170 {
171  TQListBox::mousePressEvent( e );
172 
173  if ( e->button() == Qt::RightButton ) {
174  emit rightClick();
175  }
176 }
177 
178 void KNoScrollListBox::contentsMouseDoubleClickEvent ( TQMouseEvent * e )
179 {
180  TQListBox::contentsMouseDoubleClickEvent( e );
181  TQListBoxItem *item = itemAt( e->pos() );
182  if ( !item ) {
183  emit doubleClicked( item );
184  }
185 }
186 
187 void KNoScrollListBox::resizeEvent( TQResizeEvent *e )
188 {
189  bool s = count() && ( maxItemWidth() > e->size().width() );
190  if ( mSqueezing || s )
191  triggerUpdate( false );
192 
193  mSqueezing = s;
194  TQListBox::resizeEvent( e );
195 }
196 
197 MonthViewItem::MonthViewItem( Incidence *incidence, const TQDateTime &qd,
198  const TQString & s ) : TQListBoxItem()
199 {
200  setText( s );
201 
202  mIncidence = incidence;
203  mDateTime = qd;
204 
205  mEventPixmap = KOGlobals::self()->smallIcon( "appointment" );
206  mBirthdayPixmap = KOGlobals::self()->smallIcon( "calendarbirthday" );
207  mAnniversaryPixmap= KOGlobals::self()->smallIcon( "calendaranniversary" );
208  mTodoPixmap = KOGlobals::self()->smallIcon( "todo" );
209  mTodoDonePixmap = KOGlobals::self()->smallIcon( "checkedbox" );
210  mAlarmPixmap = KOGlobals::self()->smallIcon( "bell" );
211  mRecurPixmap = KOGlobals::self()->smallIcon( "recur" );
212  mReplyPixmap = KOGlobals::self()->smallIcon( "mail-reply-sender" );
213 
214  mEvent = false;
215  mTodo = false;
216  mTodoDone = false;
217  mRecur = false;
218  mAlarm = false;
219  mReply = false;
220 }
221 
222 TQColor MonthViewItem::catColor() const
223 {
224  TQColor retColor;
225  if ( !mIncidence ) {
226  return retColor;
227  }
228 
229  TQStringList categories = mIncidence->categories();
230  TQString cat;
231  if ( !categories.isEmpty() ) {
232  cat = categories.first();
233  }
234  if ( cat.isEmpty() ) {
235  retColor = KOPrefs::instance()->unsetCategoryColor();
236  } else {
237  retColor = *( KOPrefs::instance()->categoryColor( cat ) );
238  }
239  return retColor;
240 }
241 
242 void MonthViewItem::paint( TQPainter *p )
243 {
244  bool sel = isSelected();
245 
246  TQColor bgColor = TQColor(); // Default invalid color;
247  if ( mIncidence && mTodo ) {
248  if ( static_cast<Todo*>( mIncidence )->isOverdue() ) {
249  bgColor = KOPrefs::instance()->todoOverdueColor();
250  } else if ( static_cast<Todo*>( mIncidence )->dtDue().date() == TQDate::currentDate() ) {
251  bgColor = KOPrefs::instance()->todoDueTodayColor();
252  }
253  }
254 
255  if ( !bgColor.isValid() ) {
256  if ( KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemResourceOnly ||
257  KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemResourceInsideCategoryOutside ) {
258  bgColor = resourceColor();
259  } else {
260  bgColor = catColor();
261  }
262 
263  if ( !bgColor.isValid() ) {
264  bgColor = palette().color( TQPalette::Normal,
265  sel ? TQColorGroup::Highlight :
266  TQColorGroup::Background );
267  }
268  }
269 
270  TQColor frameColor;
271  if ( KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemResourceOnly ||
272  KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryInsideResourceOutside ) {
273  frameColor = resourceColor();
274  } else {
275  frameColor = catColor();
276  }
277 
278  if ( mIncidence ) {
279  if ( mIncidence->categories().isEmpty() &&
280  KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemResourceInsideCategoryOutside ) {
281  frameColor = bgColor;
282  }
283 
284  if ( mIncidence->categories().isEmpty() &&
285  KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryInsideResourceOutside ) {
286  bgColor = frameColor;
287  }
288  }
289 
290  if ( !frameColor.isValid() ) {
291  frameColor = palette().color( TQPalette::Normal,
292  sel ? TQColorGroup::Highlight :
293  TQColorGroup::Foreground );
294  } else {
295  frameColor = frameColor.dark( 115 );
296  }
297 
298  // draw the box for the item
299  p->setBackgroundColor( frameColor );
300  p->eraseRect( 0, 0, listBox()->maxItemWidth(), height( listBox() ) );
301  int offset = 2;
302  p->setBackgroundColor( bgColor );
303  p->eraseRect( offset, offset, listBox()->maxItemWidth()-2*offset, height( listBox() )-2*offset );
304 
305  int x = 3;
306 
307  bool specialEvent = false;
308  if ( mEvent ) {
309  if ( mIncidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
310  specialEvent = true;
311  if ( mIncidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
312  p->drawPixmap( x, 0, mAnniversaryPixmap );
313  x += mAnniversaryPixmap.width() + 2;
314  } else {
315  p->drawPixmap( x, 0, mBirthdayPixmap );
316  x += mBirthdayPixmap.width() + 2;
317  }
318  // Do NOT put on the event pixmap because it takes up too much space
319  //} else {
320  // p->drawPixmap( x, 0, mEventPixmap );
321  // x += mEventPixmap.width() + 2;
322  //
323  }
324  }
325 
326  if ( mTodo ) {
327  p->drawPixmap( x, 0, mTodoPixmap );
328  x += mTodoPixmap.width() + 2;
329  }
330  if ( mTodoDone ) {
331  p->drawPixmap( x, 0, mTodoDonePixmap );
332  x += mTodoPixmap.width() + 2;
333  }
334  if ( mRecur && !specialEvent ) {
335  p->drawPixmap( x, 0, mRecurPixmap );
336  x += mRecurPixmap.width() + 2;
337  }
338  if ( mAlarm && !specialEvent ) {
339  p->drawPixmap( x, 0, mAlarmPixmap );
340  x += mAlarmPixmap.width() + 2;
341  }
342  if ( mReply ) {
343  p->drawPixmap(x, 0, mReplyPixmap );
344  x += mReplyPixmap.width() + 2;
345  }
346  TQFontMetrics fm = p->fontMetrics();
347  int yPos;
348  int pmheight = TQMAX( mRecurPixmap.height(),
349  TQMAX( mAlarmPixmap.height(), mReplyPixmap.height() ) );
350  if( pmheight < fm.height() )
351  yPos = fm.ascent() + fm.leading()/2;
352  else
353  yPos = pmheight/2 - fm.height()/2 + fm.ascent();
354  TQColor textColor = getTextColor( bgColor );
355  p->setPen( textColor );
356 
357  KWordWrap::drawFadeoutText( p, x, yPos, listBox()->width() - x, text() );
358 }
359 
360 int MonthViewItem::height( const TQListBox *lb ) const
361 {
362  return TQMAX( TQMAX( mRecurPixmap.height(), mReplyPixmap.height() ),
363  TQMAX( mAlarmPixmap.height(), lb->fontMetrics().lineSpacing()+1) );
364 }
365 
366 int MonthViewItem::width( const TQListBox *lb ) const
367 {
368  int x = 3;
369  if( mRecur ) {
370  x += mRecurPixmap.width()+2;
371  }
372  if( mAlarm ) {
373  x += mAlarmPixmap.width()+2;
374  }
375  if( mReply ) {
376  x += mReplyPixmap.width()+2;
377  }
378 
379  return( x + lb->fontMetrics().boundingRect( text() ).width() + 1 );
380 }
381 
382 
383 MonthViewCell::MonthViewCell( KOMonthView *parent)
384  : TQWidget( parent ),
385  mMonthView( parent ), mPrimary( false ), mHoliday( false ),
386  isSelected( false )
387 {
388  TQVBoxLayout *topLayout = new TQVBoxLayout( this );
389 
390  mLabel = new TQLabel( this );
391  mLabel->setFrameStyle( TQFrame::Panel | TQFrame::Plain );
392  mLabel->setLineWidth( 1 );
393  mLabel->setAlignment( AlignCenter );
394 
395  mItemList = new KNoScrollListBox( this );
396  mItemList->setMinimumSize( 10, 10 );
397  mItemList->setFrameStyle( TQFrame::Panel | TQFrame::Plain );
398  mItemList->setLineWidth( 1 );
399 
400  topLayout->addWidget( mItemList );
401 
402  mLabel->raise();
403 
404  mStandardPalette = palette();
405 
406  enableScrollBars( false );
407 
408  updateConfig();
409 
410  connect( mItemList, TQT_SIGNAL( doubleClicked( TQListBoxItem *) ),
411  TQT_SLOT( defaultAction( TQListBoxItem * ) ) );
412  connect( mItemList, TQT_SIGNAL( rightButtonPressed( TQListBoxItem *,
413  const TQPoint &) ),
414  TQT_SLOT( contextMenu( TQListBoxItem * ) ) );
415  connect( mItemList, TQT_SIGNAL( clicked( TQListBoxItem * ) ),
416  TQT_SLOT( select() ) );
417 }
418 
419 void MonthViewCell::setDate( const TQDate &date )
420 {
421 // kdDebug(5850) << "MonthViewCell::setDate(): " << date.toString() << endl;
422 
423  mDate = date;
424 
425  setFrameWidth();
426 
427  TQString text;
428  if ( KOGlobals::self()->calendarSystem()->day( date ) == 1 ) {
429  text = i18n("'Month day' for month view cells", "%1 %2")
430  .arg( KOGlobals::self()->calendarSystem()->monthName( date, true ) )
431  .arg( KOGlobals::self()->calendarSystem()->day(mDate) );
432  TQFontMetrics fm( mLabel->font() );
433  mLabel->resize( mLabelSize + TQSize( fm.width( text ), 0 ) );
434  } else {
435  mLabel->resize( mLabelSize );
436  text = TQString::number( KOGlobals::self()->calendarSystem()->day(mDate) );
437  }
438  mLabel->setText( text );
439 
440  new KOMonthCellToolTip( mItemList->viewport(),
441  monthView()->calendar(),
442  mDate,
443  static_cast<KNoScrollListBox *>( mItemList ), this );
444 
445  resizeEvent( 0 );
446 }
447 
448 TQDate MonthViewCell::date() const
449 {
450  return mDate;
451 }
452 
453 void MonthViewCell::setFrameWidth()
454 {
455  // show current day with a thicker frame
456  if ( mDate == TQDate::currentDate() ) {
457  mItemList->setLineWidth( 3 );
458  } else if ( !isSelected ) {
459  mItemList->setLineWidth( 1 );
460  }
461 }
462 
463 void MonthViewCell::setPrimary( bool primary )
464 {
465  mPrimary = primary;
466 
467  if ( mPrimary ) {
468  mLabel->setBackgroundMode( PaletteBase );
469  } else {
470  mLabel->setBackgroundMode( PaletteBackground );
471  }
472 
473  mItemList->setBackground( mPrimary, KOGlobals::self()->isWorkDay( mDate ) );
474 }
475 
477 {
478  return mPrimary;
479 }
480 
481 void MonthViewCell::setHoliday( bool holiday )
482 {
483  mHoliday = holiday;
484 
485  if ( holiday ) {
486  setPalette( mHolidayPalette );
487  } else {
488  setPalette( mStandardPalette );
489  }
490 }
491 
492 void MonthViewCell::setHolidayString( const TQString &holiday )
493 {
494  mHolidayString = holiday;
495 }
496 
497 void MonthViewCell::updateCell()
498 {
499  setFrameWidth();
500 
501  if ( mDate == TQDate::currentDate() ) {
502  setPalette( mTodayPalette );
503 
504  TQPalette pal = mItemList->palette();
505  pal.setColor( TQColorGroup::Foreground, KOPrefs::instance()->highlightColor() );
506  mItemList->setPalette( pal );
507  }
508  else {
509  if ( mHoliday )
510  setPalette( mHolidayPalette );
511  else
512  setPalette( mStandardPalette );
513 
514  TQPalette pal = mItemList->palette();
515  pal.setColor( TQColorGroup::Foreground, KOPrefs::instance()->agendaBgColor().dark( 150 ) );
516  mItemList->setPalette( pal );
517  }
518 
519  mItemList->clear();
520 
521  if ( !mHolidayString.isEmpty() ) {
522  MonthViewItem *item = new MonthViewItem( 0, TQDateTime( mDate ), mHolidayString );
523  item->setPalette( mHolidayPalette );
524  mItemList->insertItem( item );
525  }
526 }
527 
528 class MonthViewCell::CreateItemVisitor :
530 {
531  public:
532  CreateItemVisitor() : mItem(0) { emails = KOPrefs::instance()->allEmails(); }
533 
534  bool act( IncidenceBase *incidence, TQDate date, TQPalette stdPal, int multiDay )
535  {
536  mItem = 0;
537  mDate = date;
538  mStandardPalette = stdPal;
539  mMultiDay = multiDay;
540  return incidence->accept( *this );
541  }
542  MonthViewItem *item() const { return mItem; }
543  TQStringList emails;
544 
545  protected:
546  bool visit( Event *event ) {
547  TQString text;
548  TQDateTime dt( mDate );
549  // take the time 0:00 into account, which is non-inclusive
550  TQDate dtEnd = event->dtEnd().addSecs( event->doesFloat() ? 0 : -1).date();
551  int length = event->dtStart().daysTo( TQDateTime(dtEnd) );
552  if ( event->isMultiDay() ) {
553  if ( mDate == event->dtStart().date()
554  || ( mMultiDay == 0 && event->recursOn( mDate ) ) ) {
555  text = "(-- " + event->summary();
556  dt = event->dtStart();
557  } else if ( !event->doesRecur() && mDate == dtEnd
558  // last day of a recurring multi-day event?
559  || ( mMultiDay == length && event->recursOn( mDate.addDays( -length ) ) ) ) {
560  text = event->summary() + " --)";
561  } else if (!(event->dtStart().date().daysTo(mDate) % 7) && length > 7 ) {
562  text = "-- " + event->summary() + " --";
563  } else {
564  text = "----------------";
565  dt = TQDateTime( mDate );
566  }
567  } else {
568  if (event->doesFloat())
569  text = event->summary();
570  else {
571  text = TDEGlobal::locale()->formatTime(event->dtStart().time());
572  dt.setTime( event->dtStart().time() );
573  text += ' ' + event->summary();
574  }
575  }
576 
577  mItem = new MonthViewItem( event, dt, text );
578  mItem->setEvent( true );
579  if ( KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryOnly ||
580  KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryInsideResourceOutside ) {
581  TQStringList categories = event->categories();
582  TQString cat = categories.first();
583  if (cat.isEmpty()) {
584  mItem->setPalette(TQPalette(KOPrefs::instance()->unsetCategoryColor(),
585  KOPrefs::instance()->unsetCategoryColor()) );
586  } else {
587  mItem->setPalette(TQPalette(*(KOPrefs::instance()->categoryColor(cat)),
588  *(KOPrefs::instance()->categoryColor(cat))));
589  }
590  } else {
591  mItem->setPalette( mStandardPalette );
592  }
593 
594  Attendee *me = event->attendeeByMails( emails );
595  if ( me != 0 ) {
596  mItem->setReply( me->status() == Attendee::NeedsAction && me->RSVP() );
597  } else
598  mItem->setReply(false);
599  return true;
600  }
601  bool visit( Todo *todo ) {
602  TQString text;
603  if ( !KOPrefs::instance()->showAllDayTodo() )
604  return false;
605  TQDateTime dt( mDate );
606  if ( todo->hasDueDate() && !todo->doesFloat() &&
607  todo->dtDue().time() != TQTime( 0,0 ) && todo->dtDue().time().isValid() ) {
608  text += TDEGlobal::locale()->formatTime( todo->dtDue().time() );
609  text += ' ';
610  dt.setTime( todo->dtDue().time() );
611  }
612  text += todo->summary();
613 
614  mItem = new MonthViewItem( todo, dt, text );
615  if ( todo->doesRecur() ) {
616  mDate < todo->dtDue().date() ?
617  mItem->setTodoDone( true ) : mItem->setTodo( true );
618  }
619  else
620  todo->isCompleted() ? mItem->setTodoDone( true ) : mItem->setTodo( true );
621  mItem->setPalette( mStandardPalette );
622  return true;
623  }
624  protected:
625  MonthViewItem *mItem;
626  TQDate mDate;
627  TQPalette mStandardPalette;
628  int mMultiDay;
629 };
630 
631 
632 void MonthViewCell::addIncidence( Incidence *incidence, CreateItemVisitor& v, int multiDay )
633 {
634  if ( v.act( incidence, mDate, mStandardPalette, multiDay ) ) {
635  MonthViewItem *item = v.item();
636  if ( item ) {
637  item->setAlarm( incidence->isAlarmEnabled() );
638  item->setRecur( incidence->recurrenceType() );
639 
640  TQColor resourceColor = KOHelper::resourceColor( monthView()->calendar(), incidence );
641  if ( !resourceColor.isValid() )
642  resourceColor = KOPrefs::instance()->unsetCategoryColor();
643  item->setResourceColor( resourceColor );
644 
645  // FIXME: Find the correct position (time-wise) to insert the item.
646  // Currently, the items are displayed in "random" order instead of
647  // chronologically sorted.
648  uint i = 0;
649  int pos = -1;
650  TQDateTime dt( item->incidenceDateTime() );
651 
652  while ( i < mItemList->count() && pos<0 ) {
653  TQListBoxItem *item = mItemList->item( i );
654  MonthViewItem *mvitem = dynamic_cast<MonthViewItem*>( item );
655  if ( mvitem && mvitem->incidenceDateTime()>dt ) {
656  pos = i;
657  }
658  ++i;
659  }
660  mItemList->insertItem( item, pos );
661  }
662  }
663 }
664 
666 {
667  for ( uint i = 0; i < mItemList->count(); ++i ) {
668  MonthViewItem *item = static_cast<MonthViewItem *>(mItemList->item( i ) );
669  if ( item && item->incidence() &&
670  item->incidence()->uid() == incidence->uid() ) {
671  mItemList->removeItem( i );
672  --i;
673  }
674  }
675 }
676 
677 void MonthViewCell::updateConfig()
678 {
679  setFont( KOPrefs::instance()->mMonthViewFont );
680 
681  TQFontMetrics fm( font() );
682  mLabelSize = fm.size( 0, "30" ) +
683  TQSize( mLabel->frameWidth() * 2, mLabel->frameWidth() * 2 ) +
684  TQSize( 2, 2 );
685 // mStandardPalette = mOriginalPalette;
686  TQColor bg = mStandardPalette.color( TQPalette::Active, TQColorGroup::Background );
687  int h,s,v;
688  bg.getHsv( &h, &s, &v );
689  if ( date().month() %2 == 0 ) {
690  if ( v < 128 ) {
691  bg = bg.light( 125 );
692  } else {
693  bg = bg.dark( 125 );
694  }
695  }
696  setPaletteBackgroundColor( bg );
697 // mStandardPalette.setColor( TQColorGroup::Background, bg);*/
698 
699  mHolidayPalette = mStandardPalette;
700  mHolidayPalette.setColor( TQColorGroup::Foreground,
701  KOPrefs::instance()->holidayColor() );
702  mHolidayPalette.setColor( TQColorGroup::Text,
703  KOPrefs::instance()->holidayColor() );
704  mTodayPalette = mStandardPalette;
705  mTodayPalette.setColor( TQColorGroup::Foreground,
706  KOPrefs::instance()->highlightColor() );
707  mTodayPalette.setColor( TQColorGroup::Text,
708  KOPrefs::instance()->highlightColor() );
709  updateCell();
710 
711  mItemList->setBackground( mPrimary, KOGlobals::self()->isWorkDay( mDate ) );
712 }
713 
714 void MonthViewCell::enableScrollBars( bool enabled )
715 {
716  if ( enabled ) {
717  mItemList->setVScrollBarMode( TQScrollView::Auto );
718  mItemList->setHScrollBarMode( TQScrollView::Auto );
719  } else {
720  mItemList->setVScrollBarMode( TQScrollView::AlwaysOff );
721  mItemList->setHScrollBarMode( TQScrollView::AlwaysOff );
722  }
723 }
724 
725 Incidence *MonthViewCell::selectedIncidence()
726 {
727  int index = mItemList->currentItem();
728  if ( index < 0 ) return 0;
729 
730  MonthViewItem *item =
731  static_cast<MonthViewItem *>( mItemList->item( index ) );
732 
733  if ( !item ) return 0;
734 
735  return item->incidence();
736 }
737 
738 TQDate MonthViewCell::selectedIncidenceDate()
739 {
740  TQDate qd;
741  int index = mItemList->currentItem();
742  if ( index < 0 ) return qd;
743 
744  MonthViewItem *item =
745  static_cast<MonthViewItem *>( mItemList->item( index ) );
746 
747  if ( !item ) return qd;
748 
749  return item->incidenceDateTime().date();
750 }
751 
752 void MonthViewCell::select()
753 {
754 
755  isSelected = true;
756 
757  // setSelectedCell will deselect currently selected cells
758  mMonthView->setSelectedCell( this );
759 
760  if( KOPrefs::instance()->enableMonthScroll() )
761  enableScrollBars( true );
762 
763  // don't mess up the cell when it represents today
764  if( mDate != TQDate::currentDate() ) {
765  mItemList->setFrameStyle( TQFrame::Sunken | TQFrame::Panel );
766  mItemList->setLineWidth( 3 );
767  }
768 }
769 
770 void MonthViewCell::deselect()
771 {
772  isSelected = false;
773 
774  mItemList->clearSelection();
775  mItemList->setFrameStyle( TQFrame::Plain | TQFrame::Panel );
776  setFrameWidth();
777 
778  enableScrollBars( false );
779 }
780 
781 void MonthViewCell::resizeEvent ( TQResizeEvent * )
782 {
783  mLabel->move( width() - mLabel->width(), height() - mLabel->height() );
784 }
785 
786 void MonthViewCell::defaultAction( TQListBoxItem *item )
787 {
788  select();
789 
790  if ( !item ) {
791  emit newEventSignal( 0/*ResourceCalendar*/, TQString()/*subResource*/, date() );
792  } else {
793  MonthViewItem *eventItem = static_cast<MonthViewItem *>( item );
794  Incidence *incidence = eventItem->incidence();
795  if ( incidence ) mMonthView->defaultAction( incidence );
796  }
797 }
798 
799 void MonthViewCell::contextMenu( TQListBoxItem *item )
800 {
801  select();
802 
803  if ( item ) {
804  MonthViewItem *eventItem = static_cast<MonthViewItem *>( item );
805  Incidence *incidence = eventItem->incidence();
806  if ( incidence ) {
807  mMonthView->showEventContextMenu( monthView()->calendar(), incidence, mDate );
808  }
809  } else {
810  mMonthView->showGeneralContextMenu();
811  }
812 }
813 
814 
815 KOMonthView::KOMonthView( Calendar *calendar, TQWidget *parent, const char *name )
816  : KOEventView( calendar, parent, name ),
817  mDaysPerWeek( 7 ), mNumWeeks( 6 ), mNumCells( mDaysPerWeek * mNumWeeks ),
818  mShortDayLabels( false ), mWidthLongDayLabel( 0 ), mSelectedCell( 0 )
819 {
820  mCells.setAutoDelete( true );
821 
822  TQGridLayout *dayLayout = new TQGridLayout( this );
823 
824  TQFont bfont = font();
825  bfont.setBold( true );
826 
827  TQFont mfont = bfont;
828  mfont.setPointSize( 20 );
829 
830  // month name on top
831  mLabel = new TQLabel( this );
832  mLabel->setFont( mfont );
833  mLabel->setAlignment( AlignCenter );
834  mLabel->setLineWidth( 0 );
835  mLabel->setFrameStyle( TQFrame::Plain );
836 
837  dayLayout->addMultiCellWidget( mLabel, 0, 0, 0, mDaysPerWeek );
838 
839  // create the day of the week labels (Sun, Mon, etc) and add them to
840  // the layout.
841  mDayLabels.resize( mDaysPerWeek );
842  int i;
843  for( i = 0; i < mDaysPerWeek; i++ ) {
844  TQLabel *label = new TQLabel( this );
845  label->setFont( bfont );
846  label->setFrameStyle( TQFrame::Panel | TQFrame::Raised );
847  label->setLineWidth( 1 );
848  label->setAlignment( AlignCenter );
849 
850  mDayLabels.insert( i, label );
851 
852  dayLayout->addWidget( label, 1, i );
853  dayLayout->addColSpacing( i, 10 );
854  dayLayout->setColStretch( i, 1 );
855  }
856 
857  int row, col;
858 
859  mCells.resize( mNumCells );
860  for( row = 0; row < mNumWeeks; ++row ) {
861  for( col = 0; col < mDaysPerWeek; ++col ) {
862  MonthViewCell *cell = new MonthViewCell( this );
863  mCells.insert( row * mDaysPerWeek + col, cell );
864  dayLayout->addWidget( cell, row + 2, col );
865 
866  connect( cell, TQT_SIGNAL(defaultAction(Incidence *)),
867  TQT_SLOT(defaultAction(Incidence *)) );
868  connect( cell, TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &,const TQDate &)),
869  TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &,const TQDate &)) );
870  }
871  dayLayout->setRowStretch( row + 2, 1 );
872  }
873 
874  mEventContextMenu = eventPopup();
875 
876  updateConfig();
877 
878  emit incidenceSelected( 0, TQDate() );
879 }
880 
881 KOMonthView::~KOMonthView()
882 {
883  delete mEventContextMenu;
884 }
885 
887 {
888  return mNumCells;
889 }
890 
892 {
893  return mNumCells;
894 }
895 
897 {
898  Incidence::List selected;
899 
900  if ( mSelectedCell ) {
901  Incidence *incidence = mSelectedCell->selectedIncidence();
902  if ( incidence ) selected.append( incidence );
903  }
904 
905  return selected;
906 }
907 
909 {
910  DateList selected;
911 
912  if ( mSelectedCell ) {
913  TQDate qd = mSelectedCell->selectedIncidenceDate();
914  if ( qd.isValid() ) selected.append( qd );
915  }
916 
917  return selected;
918 }
919 
920 bool KOMonthView::eventDurationHint( TQDateTime &startDt, TQDateTime &endDt, bool &allDay )
921 {
922  if ( mSelectedCell ) {
923  startDt.setDate( mSelectedCell->date() );
924  endDt.setDate( mSelectedCell->date() );
925  allDay = true;
926  return true;
927  }
928  return false;
929 }
930 
931 void KOMonthView::updateConfig()
932 {
933  mWeekStartDay = TDEGlobal::locale()->weekStartDay();
934 
935  TQFontMetrics fontmetric( mDayLabels[0]->font() );
936  mWidthLongDayLabel = 0;
937 
938  for ( int i = 0; i < 7; ++i ) {
939  int width =
940  fontmetric.width( KOGlobals::self()->calendarSystem()->weekDayName( i + 1 ) );
941  if ( width > mWidthLongDayLabel ) mWidthLongDayLabel = width;
942  }
943 
944  updateDayLabels();
945 
946  for ( uint i = 0; i < mCells.count(); ++i ) {
947  mCells[i]->updateConfig();
948  }
949 
950  showLabel( !KOPrefs::instance()->fullViewMonth() );
951 }
952 
953 void KOMonthView::updateDayLabels()
954 {
955  kdDebug(5850) << "KOMonthView::updateDayLabels()" << endl;
956 
957  const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
958  int currDay;
959  for ( int i = 0; i < 7; i++ ) {
960  currDay = i+mWeekStartDay;
961  if ( currDay > 7 ) currDay -= 7;
962  mDayLabels[i]->setText( calsys->weekDayName( currDay, mShortDayLabels ) );
963  }
964 }
965 
966 void KOMonthView::showDates( const TQDate &start, const TQDate & )
967 {
968 // kdDebug(5850) << "KOMonthView::showDates(): " << start.toString() << endl;
969 
970  const KCalendarSystem *calSys = KOGlobals::self()->calendarSystem();
971 
972  mDateToCell.clear();
973 
974  // show first day of month on top for readability issues
975  mStartDate = start.addDays( -start.day() + 1 );
976  // correct begin of week
977  int weekdayCol=( mStartDate.dayOfWeek() + 7 - mWeekStartDay ) % 7;
978  mStartDate = mStartDate.addDays( -weekdayCol );
979 
980  mLabel->setText( i18n( "monthname year", "%1 %2" )
981  .arg( calSys->monthName( start ) )
982  .arg( calSys->year( start ) ) );
983 
984  showLabel( !KOPrefs::instance()->fullViewMonth() );
985 
986  bool primary = false;
987  uint i;
988  for( i = 0; i < mCells.size(); ++i ) {
989  TQDate date = mStartDate.addDays( i );
990  if ( calSys->day( date ) == 1 ) {
991  primary = !primary;
992  }
993 
994  mCells[i]->setDate( date );
995  mDateToCell[ date ] = mCells[ i ];
996  if( date == start ) {
997  mCells[i]->select();
998  }
999 
1000  mCells[i]->setPrimary( primary );
1001 
1002  bool isHoliday = calSys->dayOfWeek( date ) == calSys->weekDayOfPray()
1003  || !KOGlobals::self()->isWorkDay( date );
1004  mCells[i]->setHoliday( isHoliday );
1005 
1006  // add holiday, if present
1007  TQStringList holidays( KOGlobals::self()->holiday( date ) );
1008  mCells[i]->setHolidayString( holidays.join( i18n("delimiter for joining holiday names", ", " ) ) );
1009  }
1010 
1011  updateView();
1012 }
1013 
1015 {
1016  if ( mSelectedCell) {
1017  return TQDateTime( mSelectedCell->date() );
1018  } else {
1019  return TQDateTime();
1020  }
1021 }
1022 
1024 {
1025  // Only one cell can be selected (for now)
1026  return selectionStart();
1027 }
1028 
1029 void KOMonthView::showIncidences( const Incidence::List &, const TQDate & )
1030 {
1031  kdDebug(5850) << "KOMonthView::showIncidences( const Incidence::List & ) is not implemented yet." << endl;
1032 }
1033 
1034 class KOMonthView::GetDateVisitor : public IncidenceBase::Visitor
1035 {
1036  public:
1037  GetDateVisitor() {}
1038 
1039  bool act( IncidenceBase *incidence )
1040  {
1041  return incidence->accept( *this );
1042  }
1043  TQDateTime startDate() const { return mStartDate; }
1044  TQDateTime endDate() const { return mEndDate; }
1045 
1046  protected:
1047  bool visit( Event *event ) {
1048  mStartDate = event->dtStart();
1049  mEndDate = event->dtEnd();
1050  return true;
1051  }
1052  bool visit( Todo *todo ) {
1053  if ( todo->hasDueDate() ) {
1054  if ( todo->dtDue().time() != TQTime( 0, 0 ) &&
1055  todo->dtDue().time().isValid() ) {
1056  mStartDate = todo->dtDue();
1057  mEndDate = todo->dtDue();
1058  } else {
1059  mStartDate = TQDateTime( todo->dtDue().date(), TQTime( 23,59 ) );
1060  mEndDate = mStartDate;
1061  }
1062  }// else
1063 // return false;
1064  return true;
1065  }
1066  bool visit( Journal *journal ) {
1067  mStartDate = journal->dtStart();
1068  mEndDate = journal->dtStart();
1069  return true;
1070  }
1071  protected:
1072  TQDateTime mStartDate;
1073  TQDateTime mEndDate;
1074 };
1075 
1076 void KOMonthView::changeIncidenceDisplayAdded( Incidence *incidence, MonthViewCell::CreateItemVisitor& v)
1077 {
1078  GetDateVisitor gdv;
1079 
1080  if ( !gdv.act( incidence ) ) {
1081  kdDebug(5850) << "Visiting GetDateVisitor failed." << endl;
1082  return;
1083  }
1084 
1085  bool floats = incidence->doesFloat();
1086 
1087  if ( incidence->doesRecur() ) {
1088  for ( uint i = 0; i < mCells.count(); ++i ) {
1089  if ( incidence->recursOn( mCells[i]->date(), calendar() ) ) {
1090 
1091  // handle multiday events
1092  int length = gdv.startDate().daysTo( TQDateTime(gdv.endDate().addSecs( floats ? 0 : -1 ).date()) );
1093  for ( int j = 0; j <= length && i+j < mCells.count(); ++j ) {
1094  mCells[i+j]->addIncidence( incidence, v, j );
1095  }
1096  }
1097  }
1098  } else {
1099  // addSecs(-1) is added to handle 0:00 cases (because it's non-inclusive according to rfc)
1100  if ( gdv.endDate().isValid() ) {
1101  TQDate endDate = gdv.endDate().addSecs( floats ? 0 : -1).date();
1102  for ( TQDate date = gdv.startDate().date();
1103  date <= endDate; date = date.addDays( 1 ) ) {
1104  MonthViewCell *mvc = mDateToCell[ date ];
1105  if ( mvc ) mvc->addIncidence( incidence, v );
1106  }
1107  }
1108  }
1109 }
1110 
1111 void KOMonthView::changeIncidenceDisplay( Incidence *incidence, int action )
1112 {
1113  MonthViewCell::CreateItemVisitor v;
1114  switch ( action ) {
1115  case KOGlobals::INCIDENCEADDED:
1116  changeIncidenceDisplayAdded( incidence, v );
1117  break;
1118  case KOGlobals::INCIDENCEEDITED:
1119  for( uint i = 0; i < mCells.count(); i++ )
1120  mCells[i]->removeIncidence( incidence );
1121  changeIncidenceDisplayAdded( incidence, v );
1122  break;
1123  case KOGlobals::INCIDENCEDELETED:
1124  for( uint i = 0; i < mCells.count(); i++ )
1125  mCells[i]->removeIncidence( incidence );
1126  break;
1127  default:
1128  return;
1129  }
1130 }
1131 
1132 void KOMonthView::updateView()
1133 {
1134  for( uint i = 0; i < mCells.count(); ++i ) {
1135  mCells[i]->updateCell();
1136  }
1137 
1138  Incidence::List incidences = calendar()->incidences();
1139  Incidence::List::ConstIterator it;
1140 
1141  MonthViewCell::CreateItemVisitor v;
1142  for ( it = incidences.begin(); it != incidences.end(); ++it )
1143  changeIncidenceDisplayAdded( *it, v );
1144 
1145  processSelectionChange();
1146 }
1147 
1148 void KOMonthView::resizeEvent( TQResizeEvent * )
1149 {
1150  // select the appropriate heading string size. E.g. "Wednesday" or "Wed".
1151  // note this only changes the text if the requested size crosses the
1152  // threshold between big enough to support the full name and not big
1153  // enough.
1154  if( mDayLabels[0]->width() < mWidthLongDayLabel ) {
1155  if ( !mShortDayLabels ) {
1156  mShortDayLabels = true;
1157  updateDayLabels();
1158  }
1159  } else {
1160  if ( mShortDayLabels ) {
1161  mShortDayLabels = false;
1162  updateDayLabels();
1163  }
1164  }
1165 }
1166 
1167 void KOMonthView::showEventContextMenu( Calendar *cal, Incidence *incidence, const TQDate &qd )
1168 {
1169  mEventContextMenu->showIncidencePopup( cal, incidence, qd );
1170 }
1171 
1172 void KOMonthView::showGeneralContextMenu()
1173 {
1174  showNewEventPopup();
1175 }
1176 
1177 void KOMonthView::setSelectedCell( MonthViewCell *cell )
1178 {
1179  if ( mSelectedCell && cell != mSelectedCell )
1180  mSelectedCell->deselect();
1181 
1182  mSelectedCell = cell;
1183 
1184  if ( !mSelectedCell )
1185  emit incidenceSelected( 0, TQDate() );
1186  else
1187  if ( selectedIncidenceDates().isEmpty() ) {
1188  emit incidenceSelected( mSelectedCell->selectedIncidence(), TQDate() );
1189  } else {
1190  emit incidenceSelected( mSelectedCell->selectedIncidence(), selectedIncidenceDates().first() );
1191  }
1192 }
1193 
1194 void KOMonthView::processSelectionChange()
1195 {
1196  Incidence::List incidences = selectedIncidences();
1197  if (incidences.count() > 0) {
1198  if ( selectedIncidenceDates().isEmpty() ) {
1199  emit incidenceSelected( incidences.first(), TQDate() );
1200  } else {
1201  emit incidenceSelected( incidences.first(), selectedIncidenceDates().first() );
1202  }
1203  } else {
1204  emit incidenceSelected( 0, TQDate() );
1205  }
1206 }
1207 
1208 void KOMonthView::clearSelection()
1209 {
1210  if ( mSelectedCell ) {
1211  mSelectedCell->deselect();
1212  mSelectedCell = 0;
1213  }
1214 }
1215 
1216 void KOMonthView::showLabel( bool show )
1217 {
1218  if ( show ) {
1219  mLabel->show();
1220  } else {
1221  mLabel->hide();
1222  }
1223 }