sdsummarywidget.cpp
1 /*
2  This file is part of Kontact.
3  Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
4  Copyright (c) 2004 Allen Winter <winter@kde.org>
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 <tqcursor.h>
26 #include <tqlabel.h>
27 #include <tqlayout.h>
28 #include <tqimage.h>
29 #include <tqtooltip.h>
30 
31 #include <dcopclient.h>
32 #include <dcopref.h>
33 #include <tdeabc/stdaddressbook.h>
34 #include <korganizer/stdcalendar.h>
35 #include <tdeapplication.h>
36 #include <kdialog.h>
37 #include <tdeglobal.h>
38 #include <kiconloader.h>
39 #include <tdelocale.h>
40 #include <tdeparts/part.h>
41 #include <tdepopupmenu.h>
42 #include <kstandarddirs.h>
43 #include <kurllabel.h>
44 #include <libkcal/event.h>
45 #include <libkcal/resourcecalendar.h>
46 #include <libkcal/resourcelocal.h>
47 #include <libtdepim/kpimprefs.h>
48 
49 #include "core.h"
50 #include "plugin.h"
51 
52 #include "sdsummarywidget.h"
53 
54 enum SDIncidenceType {
55  IncidenceTypeContact, IncidenceTypeEvent
56 };
57 enum SDCategory {
58  CategoryBirthday, CategoryAnniversary, CategoryHoliday, CategoryOther
59 };
60 
61 class SDEntry
62 {
63  public:
64  SDIncidenceType type;
65  SDCategory category;
66  int yearsOld;
67  int daysTo;
68  TQDate date;
69  TQString summary;
70  TQString desc;
71  int span; // #days in the special occassion.
72  TDEABC::Addressee addressee;
73 
74  bool operator<( const SDEntry &entry ) const
75  {
76  return daysTo < entry.daysTo;
77  }
78 };
79 
80 SDSummaryWidget::SDSummaryWidget( Kontact::Plugin *plugin, TQWidget *parent,
81  const char *name )
82  : Kontact::Summary( parent, name ), mPlugin( plugin ), mCalendar( 0 ), mHolidays( 0 )
83 {
84  // Create the Summary Layout
85  TQVBoxLayout *mainLayout = new TQVBoxLayout( this, 3, 3 );
86 
87  TQPixmap icon = TDEGlobal::iconLoader()->loadIcon( "cookie",
88  TDEIcon::Desktop, TDEIcon::SizeMedium );
89 
90  TQWidget *header = createHeader( this, icon, i18n( "Special Dates" ) );
91  mainLayout->addWidget(header);
92 
93  mLayout = new TQGridLayout( mainLayout, 7, 6, 3 );
94  mLayout->setRowStretch( 6, 1 );
95 
96  // Setup the Addressbook
97  TDEABC::StdAddressBook *ab = TDEABC::StdAddressBook::self( true );
98  connect( ab, TQ_SIGNAL( addressBookChanged( AddressBook* ) ),
99  this, TQ_SLOT( updateView() ) );
100  connect( mPlugin->core(), TQ_SIGNAL( dayChanged( const TQDate& ) ),
101  this, TQ_SLOT( updateView() ) );
102 
103  // Setup the Calendar
104  mCalendar = new KCal::CalendarResources( KPimPrefs::timezone() );
105  mCalendar->readConfig();
106 
107  KCal::CalendarResourceManager *manager = mCalendar->resourceManager();
108  if ( manager->isEmpty() ) {
109  TDEConfig config( "korganizerrc" );
110  config.setGroup( "General" );
111  TQString fileName = config.readPathEntry( "Active Calendar" );
112 
113  TQString resourceName;
114  if ( fileName.isEmpty() ) {
115  fileName = locateLocal( "data", "korganizer/std.ics" );
116  resourceName = i18n( "Default KOrganizer resource" );
117  } else {
118  resourceName = i18n( "Active Calendar" );
119  }
120 
121  KCal::ResourceCalendar *defaultResource =
122  new KCal::ResourceLocal( fileName );
123 
124  defaultResource->setResourceName( resourceName );
125 
126  manager->add( defaultResource );
127  manager->setStandardResource( defaultResource );
128  }
129  mCalendar = KOrg::StdCalendar::self();
130 
131  connect( mCalendar, TQ_SIGNAL( calendarChanged() ),
132  this, TQ_SLOT( updateView() ) );
133  connect( mPlugin->core(), TQ_SIGNAL( dayChanged( const TQDate& ) ),
134  this, TQ_SLOT( updateView() ) );
135 
136  // Update Configuration
137  configUpdated();
138 }
139 
140 void SDSummaryWidget::configUpdated()
141 {
142  TDEConfig config( "kcmsdsummaryrc" );
143 
144  config.setGroup( "Days" );
145  mDaysAhead = config.readNumEntry( "DaysToShow", 7 );
146 
147  config.setGroup( "EventTypes" );
148  mShowBirthdaysFromKAB =
149  config.readBoolEntry( "ShowBirthdaysFromContacts", true );
150  mShowBirthdaysFromCal =
151  config.readBoolEntry( "ShowBirthdaysFromCalendar", true );
152 
153  mShowAnniversariesFromKAB =
154  config.readBoolEntry( "ShowAnniversariesFromContacts", true );
155  mShowAnniversariesFromCal =
156  config.readBoolEntry( "ShowAnniversariesFromCalendar", true );
157 
158  mShowHolidays =
159  config.readBoolEntry( "ShowHolidays", true );
160 
161  mShowSpecialsFromCal =
162  config.readBoolEntry( "ShowSpecialsFromCalendar", true );
163 
164  updateView();
165 }
166 
167 bool SDSummaryWidget::initHolidays()
168 {
169  TDEConfig hconfig( "korganizerrc" );
170  hconfig.setGroup( "Time & Date" );
171  TQString location = hconfig.readEntry( "Holidays" );
172  if ( !location.isEmpty() ) {
173  if ( mHolidays ) delete mHolidays;
174  mHolidays = new KHolidays( location );
175  return true;
176  }
177  return false;
178 }
179 
180 // number of days remaining in an Event
181 int SDSummaryWidget::span( KCal::Event *event )
182 {
183  int span=1;
184  if ( event->isMultiDay() && event->doesFloat() ) {
185  TQDate d = event->dtStart().date();
186  if ( d < TQDate::currentDate() ) {
187  d = TQDate::currentDate();
188  }
189  while ( d < event->dtEnd().date() ) {
190  span++;
191  d=d.addDays( 1 );
192  }
193  }
194  return span;
195 }
196 
197 // day of a multiday Event
198 int SDSummaryWidget::dayof( KCal::Event *event, const TQDate& date )
199 {
200  int dayof=1;
201  TQDate d = event->dtStart().date();
202  if ( d < TQDate::currentDate() ) {
203  d = TQDate::currentDate();
204  }
205  while ( d < event->dtEnd().date() ) {
206  if ( d < date ) {
207  dayof++;
208  }
209  d = d.addDays( 1 );
210  }
211  return dayof;
212 }
213 
214 
215 
216 void SDSummaryWidget::updateView()
217 {
218  mLabels.setAutoDelete( true );
219  mLabels.clear();
220  mLabels.setAutoDelete( false );
221 
222  TDEIconLoader loader( "tdepim" );
223 
224  TDEABC::StdAddressBook *ab = TDEABC::StdAddressBook::self( true );
225  TQValueList<SDEntry> dates;
226  TQLabel *label = 0;
227 
228  // No reason to show the date year
229  TQString savefmt = TDEGlobal::locale()->dateFormat();
230  TDEGlobal::locale()->setDateFormat( TDEGlobal::locale()->
231  dateFormat().replace( 'Y', ' ' ) );
232 
233  // Search for Birthdays and Anniversaries in the Addressbook
234  TDEABC::AddressBook::Iterator it;
235  for ( it = ab->begin(); it != ab->end(); ++it ) {
236  TQDate birthday = (*it).birthday().date();
237  if ( birthday.isValid() && mShowBirthdaysFromKAB ) {
238  SDEntry entry;
239  entry.type = IncidenceTypeContact;
240  entry.category = CategoryBirthday;
241  dateDiff( birthday, entry.daysTo, entry.yearsOld );
242 
243  entry.date = birthday;
244  entry.addressee = *it;
245  entry.span = 1;
246  if ( entry.daysTo <= mDaysAhead )
247  dates.append( entry );
248  }
249 
250  TQString anniversaryAsString =
251  (*it).custom( "KADDRESSBOOK" , "X-Anniversary" );
252  if ( !anniversaryAsString.isEmpty() ) {
253  TQDate anniversary = TQDate::fromString( anniversaryAsString, TQt::ISODate );
254  if ( anniversary.isValid() && mShowAnniversariesFromKAB ) {
255  SDEntry entry;
256  entry.type = IncidenceTypeContact;
257  entry.category = CategoryAnniversary;
258  dateDiff( anniversary, entry.daysTo, entry.yearsOld );
259 
260  entry.date = anniversary;
261  entry.addressee = *it;
262  entry.span = 1;
263  if ( entry.daysTo <= mDaysAhead )
264  dates.append( entry );
265  }
266  }
267  }
268 
269  // Search for Birthdays, Anniversaries, Holidays, and Special Occasions
270  // in the Calendar
271  TQDate dt;
272  TQDate currentDate = TQDate::currentDate();
273  for ( dt=currentDate;
274  dt<=currentDate.addDays( mDaysAhead - 1 );
275  dt=dt.addDays(1) ) {
276  KCal::Event::List events = mCalendar->events( dt,
277  KCal::EventSortStartDate,
278  KCal::SortDirectionAscending );
279  KCal::Event *ev;
280  KCal::Event::List::ConstIterator it;
281  for ( it=events.begin(); it!=events.end(); ++it ) {
282  ev = *it;
283  if ( !ev->categoriesStr().isEmpty() ) {
284  TQStringList::ConstIterator it2;
285  TQStringList c = ev->categories();
286  for ( it2=c.begin(); it2!=c.end(); ++it2 ) {
287 
288  // Append Birthday Event?
289  if ( mShowBirthdaysFromCal &&
290  ( ( *it2 ).upper() == i18n( "BIRTHDAY" ) ) ) {
291  SDEntry entry;
292  entry.type = IncidenceTypeEvent;
293  entry.category = CategoryBirthday;
294  entry.date = dt;
295  entry.summary = ev->summary();
296  entry.desc = ev->description();
297  dateDiff( ev->dtStart().date(), entry.daysTo, entry.yearsOld );
298  entry.span = 1;
299  dates.append( entry );
300  break;
301  }
302 
303  // Append Anniversary Event?
304  if ( mShowAnniversariesFromCal &&
305  ( ( *it2 ).upper() == i18n( "ANNIVERSARY" ) ) ) {
306  SDEntry entry;
307  entry.type = IncidenceTypeEvent;
308  entry.category = CategoryAnniversary;
309  entry.date = dt;
310  entry.summary = ev->summary();
311  entry.desc = ev->description();
312  dateDiff( ev->dtStart().date(), entry.daysTo, entry.yearsOld );
313  entry.span = 1;
314  dates.append( entry );
315  break;
316  }
317 
318  // Append Holiday Event?
319  if ( mShowHolidays &&
320  ( ( *it2 ).upper() == i18n( "HOLIDAY" ) ) ) {
321  SDEntry entry;
322  entry.type = IncidenceTypeEvent;
323  entry.category = CategoryHoliday;
324  entry.date = dt;
325  entry.summary = ev->summary();
326  entry.desc = ev->description();
327  dateDiff( dt, entry.daysTo, entry.yearsOld );
328  entry.yearsOld = -1; //ignore age of holidays
329  entry.span = span( ev );
330  if ( entry.span > 1 && dayof( ev, dt ) > 1 ) // skip days 2,3,...
331  break;
332  dates.append( entry );
333  break;
334  }
335 
336  // Append Special Occasion Event?
337  if ( mShowSpecialsFromCal &&
338  ( ( *it2 ).upper() == i18n( "SPECIAL OCCASION" ) ) ) {
339  SDEntry entry;
340  entry.type = IncidenceTypeEvent;
341  entry.category = CategoryOther;
342  entry.date = dt;
343  entry.summary = ev->summary();
344  entry.desc = ev->description();
345  dateDiff( dt, entry.daysTo, entry.yearsOld );
346  entry.yearsOld = -1; //ignore age of special occasions
347  entry.span = span( ev );
348  if ( entry.span > 1 && dayof( ev, dt ) > 1 ) // skip days 2,3,...
349  break;
350  dates.append( entry );
351  break;
352  }
353  }
354  }
355  }
356  }
357 
358  // Seach for Holidays
359  if ( mShowHolidays ) {
360  if ( initHolidays() ) {
361  for ( dt=currentDate;
362  dt<=currentDate.addDays( mDaysAhead - 1 );
363  dt=dt.addDays(1) ) {
364  TQValueList<KHoliday> holidays = mHolidays->getHolidays( dt );
365  TQValueList<KHoliday>::ConstIterator it = holidays.begin();
366  for ( ; it != holidays.end(); ++it ) {
367  SDEntry entry;
368  entry.type = IncidenceTypeEvent;
369  entry.category = ((*it).Category==KHolidays::HOLIDAY)?CategoryHoliday:CategoryOther;
370  entry.date = dt;
371  entry.summary = (*it).text;
372  dateDiff( dt, entry.daysTo, entry.yearsOld );
373  entry.yearsOld = -1; //ignore age of holidays
374  entry.span = 1;
375  dates.append( entry );
376  }
377  }
378  }
379  }
380 
381  // Sort, then Print the Special Dates
382  qHeapSort( dates );
383 
384  if ( !dates.isEmpty() ) {
385  int counter = 0;
386  TQValueList<SDEntry>::Iterator addrIt;
387  TQString lines;
388  for ( addrIt = dates.begin(); addrIt != dates.end(); ++addrIt ) {
389  bool makeBold = (*addrIt).daysTo == 0; // i.e., today
390 
391  // Pixmap
392  TQImage icon_img;
393  TQString icon_name;
394  TDEABC::Picture pic;
395  switch( (*addrIt).category ) {
396  case CategoryBirthday:
397  icon_name = "calendarbirthday";
398  pic = (*addrIt).addressee.photo();
399  if ( pic.isIntern() && !pic.data().isNull() ) {
400  TQImage img = pic.data();
401  if ( img.width() > img.height() ) {
402  icon_img = img.scaleWidth( 32 );
403  } else {
404  icon_img = img.scaleHeight( 32 );
405  }
406  }
407  break;
408  case CategoryAnniversary:
409  icon_name = "calendaranniversary";
410  pic = (*addrIt).addressee.photo();
411  if ( pic.isIntern() && !pic.data().isNull() ) {
412  TQImage img = pic.data();
413  if ( img.width() > img.height() ) {
414  icon_img = img.scaleWidth( 32 );
415  } else {
416  icon_img = img.scaleHeight( 32 );
417  }
418  }
419  break;
420  case CategoryHoliday:
421  icon_name = "calendarholiday"; break;
422  case CategoryOther:
423  icon_name = "cookie"; break;
424  }
425  label = new TQLabel( this );
426  if ( icon_img.isNull() ) {
427  label->setPixmap( TDEGlobal::iconLoader()->loadIcon( icon_name,
428  TDEIcon::Small ) );
429  } else {
430  label->setPixmap( icon_img );
431  }
432  label->setMaximumWidth( label->minimumSizeHint().width() );
433  label->setAlignment( AlignVCenter );
434  mLayout->addWidget( label, counter, 0 );
435  mLabels.append( label );
436 
437  // Event date
438  TQString datestr;
439 
440  //Muck with the year -- change to the year 'daysTo' days away
441  int year = currentDate.addDays( (*addrIt).daysTo ).year();
442  TQDate sD = TQDate( year, (*addrIt).date.month(), (*addrIt).date.day() );
443 
444  if ( (*addrIt).daysTo == 0 ) {
445  datestr = i18n( "Today" );
446  } else if ( (*addrIt).daysTo == 1 ) {
447  datestr = i18n( "Tomorrow" );
448  } else {
449  datestr = TDEGlobal::locale()->formatDate( sD );
450  }
451  // Print the date span for multiday, floating events, for the
452  // first day of the event only.
453  if ( (*addrIt).span > 1 ) {
454  TQString endstr =
455  TDEGlobal::locale()->formatDate( sD.addDays( (*addrIt).span - 1 ) );
456  datestr += " -\n " + endstr;
457  }
458 
459  label = new TQLabel( datestr, this );
460  label->setAlignment( AlignLeft | AlignVCenter );
461  mLayout->addWidget( label, counter, 1 );
462  mLabels.append( label );
463  if ( makeBold ) {
464  TQFont font = label->font();
465  font.setBold( true );
466  label->setFont( font );
467  }
468 
469  // Countdown
470  label = new TQLabel( this );
471  if ( (*addrIt).daysTo == 0 ) {
472  label->setText( i18n( "now" ) );
473  } else {
474  label->setText( i18n( "in 1 day", "in %n days", (*addrIt).daysTo ) );
475  }
476 
477  label->setAlignment( AlignLeft | AlignVCenter );
478  mLayout->addWidget( label, counter, 2 );
479  mLabels.append( label );
480 
481  // What
482  TQString what;
483  switch( (*addrIt).category ) {
484  case CategoryBirthday:
485  what = i18n( "Birthday" ); break;
486  case CategoryAnniversary:
487  what = i18n( "Anniversary" ); break;
488  case CategoryHoliday:
489  what = i18n( "Holiday" ); break;
490  case CategoryOther:
491  what = i18n( "Special Occasion" ); break;
492  }
493  label = new TQLabel( this );
494  label->setText( what );
495  label->setAlignment( AlignLeft | AlignVCenter );
496  mLayout->addWidget( label, counter, 3 );
497  mLabels.append( label );
498 
499  // Description
500  if ( (*addrIt).type == IncidenceTypeContact ) {
501  KURLLabel *urlLabel = new KURLLabel( this );
502  urlLabel->installEventFilter( this );
503  urlLabel->setURL( (*addrIt).addressee.uid() );
504  urlLabel->setText( (*addrIt).addressee.realName() );
505  urlLabel->setTextFormat( TQt::RichText );
506  mLayout->addWidget( urlLabel, counter, 4 );
507  mLabels.append( urlLabel );
508 
509  connect( urlLabel, TQ_SIGNAL( leftClickedURL( const TQString& ) ),
510  this, TQ_SLOT( mailContact( const TQString& ) ) );
511  connect( urlLabel, TQ_SIGNAL( rightClickedURL( const TQString& ) ),
512  this, TQ_SLOT( popupMenu( const TQString& ) ) );
513  } else {
514  label = new TQLabel( this );
515  label->setText( (*addrIt).summary );
516  label->setTextFormat( TQt::RichText );
517  mLayout->addWidget( label, counter, 4 );
518  mLabels.append( label );
519  if ( !(*addrIt).desc.isEmpty() ) {
520  TQToolTip::add( label, (*addrIt).desc );
521  }
522  }
523 
524  // Age
525  if ( (*addrIt).category == CategoryBirthday ||
526  (*addrIt).category == CategoryAnniversary ) {
527  label = new TQLabel( this );
528  if ( (*addrIt).yearsOld <= 0 ) {
529  label->setText( "" );
530  } else {
531  label->setText( i18n( "one year", "%n years", (*addrIt).yearsOld ) );
532  }
533  label->setAlignment( AlignLeft | AlignVCenter );
534  mLayout->addWidget( label, counter, 5 );
535  mLabels.append( label );
536  }
537 
538  counter++;
539  }
540  } else {
541  label = new TQLabel(
542  i18n( "No special dates within the next 1 day",
543  "No special dates pending within the next %n days",
544  mDaysAhead ), this, "nothing to see" );
545  label->setAlignment( AlignHCenter | AlignVCenter );
546  mLayout->addMultiCellWidget( label, 0, 0, 0, 4 );
547  mLabels.append( label );
548  }
549 
550  for ( label = mLabels.first(); label; label = mLabels.next() )
551  label->show();
552 
553  TDEGlobal::locale()->setDateFormat( savefmt );
554 }
555 
556 void SDSummaryWidget::mailContact( const TQString &uid )
557 {
558  TDEABC::StdAddressBook *ab = TDEABC::StdAddressBook::self( true );
559  TQString email = ab->findByUid( uid ).fullEmail();
560 
561  kapp->invokeMailer( email, TQString() );
562 }
563 
564 void SDSummaryWidget::viewContact( const TQString &uid )
565 {
566  if ( !mPlugin->isRunningStandalone() )
567  mPlugin->core()->selectPlugin( "kontact_kaddressbookplugin" );
568  else
569  mPlugin->bringToForeground();
570 
571  DCOPRef dcopCall( "kaddressbook", "KAddressBookIface" );
572  dcopCall.send( "showContactEditor(TQString)", uid );
573 }
574 
575 void SDSummaryWidget::popupMenu( const TQString &uid )
576 {
577  TDEPopupMenu popup( this );
578  popup.insertItem( TDEGlobal::iconLoader()->loadIcon( "kmail", TDEIcon::Small ),
579  i18n( "Send &Mail" ), 0 );
580  popup.insertItem( TDEGlobal::iconLoader()->loadIcon( "kaddressbook", TDEIcon::Small ),
581  i18n( "View &Contact" ), 1 );
582 
583  switch ( popup.exec( TQCursor::pos() ) ) {
584  case 0:
585  mailContact( uid );
586  break;
587  case 1:
588  viewContact( uid );
589  break;
590  }
591 }
592 
593 bool SDSummaryWidget::eventFilter( TQObject *obj, TQEvent* e )
594 {
595  if ( obj->inherits( "KURLLabel" ) ) {
596  KURLLabel* label = static_cast<KURLLabel*>( obj );
597  if ( e->type() == TQEvent::Enter )
598  emit message( i18n( "Mail to:\"%1\"" ).arg( label->text() ) );
599  if ( e->type() == TQEvent::Leave )
600  emit message( TQString() );
601  }
602 
603  return Kontact::Summary::eventFilter( obj, e );
604 }
605 
606 void SDSummaryWidget::dateDiff( const TQDate &date, int &days, int &years )
607 {
608  TQDate currentDate;
609  TQDate eventDate;
610 
611  if ( TQDate::leapYear( date.year() ) && date.month() == 2 && date.day() == 29 ) {
612  currentDate = TQDate( date.year(), TQDate::currentDate().month(), TQDate::currentDate().day() );
613  if ( !TQDate::leapYear( TQDate::currentDate().year() ) )
614  eventDate = TQDate( date.year(), date.month(), 28 ); // celebrate one day earlier ;)
615  else
616  eventDate = TQDate( date.year(), date.month(), date.day() );
617  } else {
618  currentDate = TQDate( 0, TQDate::currentDate().month(), TQDate::currentDate().day() );
619  eventDate = TQDate( 0, date.month(), date.day() );
620  }
621 
622  int offset = currentDate.daysTo( eventDate );
623  if ( offset < 0 ) {
624  days = 365 + offset;
625  years = TQDate::currentDate().year() + 1 - date.year();
626  } else {
627  days = offset;
628  years = TQDate::currentDate().year() - date.year();
629  }
630 }
631 
632 TQStringList SDSummaryWidget::configModules() const
633 {
634  return TQStringList( "kcmsdsummary.desktop" );
635 }
636 
637 #include "sdsummarywidget.moc"
bool isMultiDay() const
bool doesFloat() const
virtual TQDateTime dtStart() const
TQString description() const
TQStringList categories() const
TQString categoriesStr() const
TQString summary() const
Base class for all Plugins in Kontact.
Definition: plugin.h:59