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 <tdestandarddirs.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
54enum SDIncidenceType {
55 IncidenceTypeContact, IncidenceTypeEvent
56};
57enum SDCategory {
58 CategoryBirthday, CategoryAnniversary, CategoryHoliday, CategoryOther
59};
60
61class 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
80SDSummaryWidget::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
140void 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
167bool 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
181int 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
198int 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
216void 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
556void SDSummaryWidget::mailContact( const TQString &uid )
557{
558 TDEABC::StdAddressBook *ab = TDEABC::StdAddressBook::self( true );
559 TQString email = ab->findByUid( uid ).fullEmail();
560
561 tdeApp->invokeMailer( email, TQString() );
562}
563
564void 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
575void 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
593bool 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
606void 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
632TQStringList 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