kontact

sdsummarywidget.cpp

00001 /*
00002     This file is part of Kontact.
00003     Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>
00004     Copyright (c) 2004 Allen Winter <winter@kde.org>
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 
00020     As a special exception, permission is given to link this program
00021     with any edition of Qt, and distribute the resulting executable,
00022     without including the source code for Qt in the source distribution.
00023 */
00024 
00025 #include <qcursor.h>
00026 #include <qlabel.h>
00027 #include <qlayout.h>
00028 #include <qimage.h>
00029 #include <qtooltip.h>
00030 
00031 #include <dcopclient.h>
00032 #include <dcopref.h>
00033 #include <kabc/stdaddressbook.h>
00034 #include <libkdepim/stdcalendar.h>
00035 #include <kapplication.h>
00036 #include <kdialog.h>
00037 #include <kglobal.h>
00038 #include <kiconloader.h>
00039 #include <klocale.h>
00040 #include <kparts/part.h>
00041 #include <kpopupmenu.h>
00042 #include <kstandarddirs.h>
00043 #include <kurllabel.h>
00044 #include <libkcal/event.h>
00045 #include <libkcal/resourcecalendar.h>
00046 #include <libkcal/resourcelocal.h>
00047 #include <libkdepim/kpimprefs.h>
00048 
00049 #include "core.h"
00050 #include "plugin.h"
00051 
00052 #include "sdsummarywidget.h"
00053 
00054 enum SDIncidenceType {
00055   IncidenceTypeContact, IncidenceTypeEvent
00056 };
00057 enum SDCategory {
00058   CategoryBirthday, CategoryAnniversary, CategoryHoliday, CategoryOther
00059 };
00060 
00061 class SDEntry
00062 {
00063   public:
00064     SDIncidenceType type;
00065     SDCategory category;
00066     int yearsOld;
00067     int daysTo;
00068     QDate date;
00069     QString summary;
00070     QString desc;
00071     int span; // #days in the special occassion.
00072     KABC::Addressee addressee;
00073 
00074     bool operator<( const SDEntry &entry ) const
00075     {
00076       return daysTo < entry.daysTo;
00077     }
00078 };
00079 
00080 SDSummaryWidget::SDSummaryWidget( Kontact::Plugin *plugin, QWidget *parent,
00081                                     const char *name )
00082   : Kontact::Summary( parent, name ), mPlugin( plugin ), mCalendar( 0 ), mHolidays( 0 )
00083 {
00084   // Create the Summary Layout
00085   QVBoxLayout *mainLayout = new QVBoxLayout( this, 3, 3 );
00086 
00087   QPixmap icon = KGlobal::iconLoader()->loadIcon( "cookie",
00088                     KIcon::Desktop, KIcon::SizeMedium );
00089 
00090   QWidget *header = createHeader( this, icon, i18n( "Special Dates" ) );
00091   mainLayout->addWidget(header);
00092 
00093   mLayout = new QGridLayout( mainLayout, 7, 6, 3 );
00094   mLayout->setRowStretch( 6, 1 );
00095 
00096   // Setup the Addressbook
00097   KABC::StdAddressBook *ab = KABC::StdAddressBook::self( true );
00098   connect( ab, SIGNAL( addressBookChanged( AddressBook* ) ),
00099            this, SLOT( updateView() ) );
00100   connect( mPlugin->core(), SIGNAL( dayChanged( const QDate& ) ),
00101            this, SLOT( updateView() ) );
00102 
00103   // Setup the Calendar
00104   mCalendar = KCal::StdCalendar::self();
00105 
00106   connect( mCalendar, SIGNAL( calendarChanged() ),
00107            this, SLOT( updateView() ) );
00108   connect( mPlugin->core(), SIGNAL( dayChanged( const QDate& ) ),
00109            this, SLOT( updateView() ) );
00110 
00111   // Update Configuration
00112   configUpdated();
00113 }
00114 
00115 void SDSummaryWidget::configUpdated()
00116 {
00117   KConfig config( "kcmsdsummaryrc" );
00118 
00119   config.setGroup( "Days" );
00120   mDaysAhead = config.readNumEntry( "DaysToShow", 7 );
00121 
00122   config.setGroup( "EventTypes" );
00123   mShowBirthdaysFromKAB =
00124     config.readBoolEntry( "ShowBirthdaysFromContacts", true );
00125   mShowBirthdaysFromCal =
00126     config.readBoolEntry( "ShowBirthdaysFromCalendar", true );
00127 
00128   mShowAnniversariesFromKAB =
00129     config.readBoolEntry( "ShowAnniversariesFromContacts", true );
00130   mShowAnniversariesFromCal =
00131     config.readBoolEntry( "ShowAnniversariesFromCalendar", true );
00132 
00133   mShowHolidays =
00134     config.readBoolEntry( "ShowHolidays", true );
00135 
00136   mShowSpecialsFromCal =
00137     config.readBoolEntry( "ShowSpecialsFromCalendar", true );
00138 
00139   updateView();
00140 }
00141 
00142 bool SDSummaryWidget::initHolidays()
00143 {
00144   KConfig hconfig( "korganizerrc" );
00145   hconfig.setGroup( "Time & Date" );
00146   QString location = hconfig.readEntry( "Holidays" );
00147   if ( !location.isEmpty() ) {
00148     if ( mHolidays ) delete mHolidays;
00149     mHolidays = new KHolidays( location );
00150     return true;
00151   }
00152   return false;
00153 }
00154 
00155 // number of days remaining in an Event
00156 int SDSummaryWidget::span( KCal::Event *event )
00157 {
00158   int span=1;
00159   if ( event->isMultiDay() && event->doesFloat() ) {
00160     QDate d = event->dtStart().date();
00161     if ( d < QDate::currentDate() ) {
00162       d = QDate::currentDate();
00163     }
00164     while ( d < event->dtEnd().date() ) {
00165       span++;
00166       d=d.addDays( 1 );
00167     }
00168   }
00169   return span;
00170 }
00171 
00172 // day of a multiday Event
00173 int SDSummaryWidget::dayof( KCal::Event *event, const QDate& date )
00174 {
00175   int dayof=1;
00176   QDate d = event->dtStart().date();
00177   if ( d < QDate::currentDate() ) {
00178     d = QDate::currentDate();
00179   }
00180   while ( d < event->dtEnd().date() ) {
00181     if ( d < date ) {
00182       dayof++;
00183     }
00184     d = d.addDays( 1 );
00185   }
00186   return dayof;
00187 }
00188 
00189 
00190 
00191 void SDSummaryWidget::updateView()
00192 {
00193   mLabels.setAutoDelete( true );
00194   mLabels.clear();
00195   mLabels.setAutoDelete( false );
00196 
00197   KIconLoader loader( "kdepim" );
00198 
00199   KABC::StdAddressBook *ab = KABC::StdAddressBook::self( true );
00200   QValueList<SDEntry> dates;
00201   QLabel *label = 0;
00202 
00203   // No reason to show the date year
00204   QString savefmt = KGlobal::locale()->dateFormat();
00205   KGlobal::locale()->setDateFormat( KGlobal::locale()->
00206                                     dateFormat().replace( 'Y', ' ' ) );
00207 
00208   // Search for Birthdays and Anniversaries in the Addressbook
00209   KABC::AddressBook::Iterator it;
00210   for ( it = ab->begin(); it != ab->end(); ++it ) {
00211     QDate birthday = (*it).birthday().date();
00212     if ( birthday.isValid() && mShowBirthdaysFromKAB ) {
00213       SDEntry entry;
00214       entry.type = IncidenceTypeContact;
00215       entry.category = CategoryBirthday;
00216       dateDiff( birthday, entry.daysTo, entry.yearsOld );
00217 
00218       entry.date = birthday;
00219       entry.addressee = *it;
00220       entry.span = 1;
00221       if ( entry.daysTo <= mDaysAhead )
00222         dates.append( entry );
00223     }
00224 
00225     QString anniversaryAsString =
00226       (*it).custom( "KADDRESSBOOK" , "X-Anniversary" );
00227     if ( !anniversaryAsString.isEmpty() ) {
00228       QDate anniversary = QDate::fromString( anniversaryAsString, Qt::ISODate );
00229       if ( anniversary.isValid() && mShowAnniversariesFromKAB ) {
00230         SDEntry entry;
00231         entry.type = IncidenceTypeContact;
00232         entry.category = CategoryAnniversary;
00233         dateDiff( anniversary, entry.daysTo, entry.yearsOld );
00234 
00235         entry.date = anniversary;
00236         entry.addressee = *it;
00237         entry.span = 1;
00238         if ( entry.daysTo <= mDaysAhead )
00239           dates.append( entry );
00240       }
00241     }
00242   }
00243 
00244   // Search for Birthdays, Anniversaries, Holidays, and Special Occasions
00245   // in the Calendar
00246   QDate dt;
00247   QDate currentDate = QDate::currentDate();
00248   for ( dt=currentDate;
00249         dt<=currentDate.addDays( mDaysAhead - 1 );
00250         dt=dt.addDays(1) ) {
00251     KCal::Event::List events = mCalendar->events( dt,
00252                                                   KCal::EventSortStartDate,
00253                                                   KCal::SortDirectionAscending );
00254     KCal::Event *ev;
00255     KCal::Event::List::ConstIterator it;
00256     for ( it=events.begin(); it!=events.end(); ++it ) {
00257       ev = *it;
00258       if ( !ev->categoriesStr().isEmpty() ) {
00259         QStringList::ConstIterator it2;
00260         QStringList c = ev->categories();
00261         for ( it2=c.begin(); it2!=c.end(); ++it2 ) {
00262 
00263           // Append Birthday Event?
00264           if ( mShowBirthdaysFromCal &&
00265                ( ( *it2 ).upper() == i18n( "BIRTHDAY" ) ) ) {
00266             SDEntry entry;
00267             entry.type = IncidenceTypeEvent;
00268             entry.category = CategoryBirthday;
00269             entry.date = dt;
00270             entry.summary = ev->summary();
00271             entry.desc = ev->description();
00272             dateDiff( ev->dtStart().date(), entry.daysTo, entry.yearsOld );
00273             entry.span = 1;
00274             dates.append( entry );
00275             break;
00276           }
00277 
00278           // Append Anniversary Event?
00279           if ( mShowAnniversariesFromCal &&
00280                ( ( *it2 ).upper() == i18n( "ANNIVERSARY" ) ) ) {
00281             SDEntry entry;
00282             entry.type = IncidenceTypeEvent;
00283             entry.category = CategoryAnniversary;
00284             entry.date = dt;
00285             entry.summary = ev->summary();
00286             entry.desc = ev->description();
00287             dateDiff( ev->dtStart().date(), entry.daysTo, entry.yearsOld );
00288             entry.span = 1;
00289             dates.append( entry );
00290             break;
00291           }
00292 
00293           // Append Holiday Event?
00294           if ( mShowHolidays &&
00295                ( ( *it2 ).upper() == i18n( "HOLIDAY" ) ) ) {
00296             SDEntry entry;
00297             entry.type = IncidenceTypeEvent;
00298             entry.category = CategoryHoliday;
00299             entry.date = dt;
00300             entry.summary = ev->summary();
00301             entry.desc = ev->description();
00302             dateDiff( dt, entry.daysTo, entry.yearsOld );
00303             entry.yearsOld = -1; //ignore age of holidays
00304             entry.span = span( ev );
00305             if ( entry.span > 1 && dayof( ev, dt ) > 1 ) // skip days 2,3,...
00306               break;
00307             dates.append( entry );
00308             break;
00309           }
00310 
00311           // Append Special Occasion Event?
00312           if ( mShowSpecialsFromCal &&
00313                ( ( *it2 ).upper() == i18n( "SPECIAL OCCASION" ) ) ) {
00314             SDEntry entry;
00315             entry.type = IncidenceTypeEvent;
00316             entry.category = CategoryOther;
00317             entry.date = dt;
00318             entry.summary = ev->summary();
00319             entry.desc = ev->description();
00320             dateDiff( dt, entry.daysTo, entry.yearsOld );
00321             entry.yearsOld = -1; //ignore age of special occasions
00322             entry.span = span( ev );
00323             if ( entry.span > 1 && dayof( ev, dt ) > 1 ) // skip days 2,3,...
00324               break;
00325             dates.append( entry );
00326             break;
00327           }
00328         }
00329       }
00330     }
00331   }
00332 
00333   // Seach for Holidays
00334   if ( mShowHolidays ) {
00335     if ( initHolidays() ) {
00336       for ( dt=currentDate;
00337             dt<=currentDate.addDays( mDaysAhead - 1 );
00338             dt=dt.addDays(1) ) {
00339         QValueList<KHoliday> holidays = mHolidays->getHolidays( dt );
00340         QValueList<KHoliday>::ConstIterator it = holidays.begin();
00341         for ( ; it != holidays.end(); ++it ) {
00342           SDEntry entry;
00343           entry.type = IncidenceTypeEvent;
00344           entry.category = ((*it).Category==KHolidays::HOLIDAY)?CategoryHoliday:CategoryOther;
00345           entry.date = dt;
00346           entry.summary = (*it).text;
00347           dateDiff( dt, entry.daysTo, entry.yearsOld );
00348           entry.yearsOld = -1; //ignore age of holidays
00349           entry.span = 1;
00350           dates.append( entry );
00351         }
00352       }
00353     }
00354   }
00355 
00356   // Sort, then Print the Special Dates
00357   qHeapSort( dates );
00358 
00359   if ( !dates.isEmpty() ) {
00360     int counter = 0;
00361     QValueList<SDEntry>::Iterator addrIt;
00362     QString lines;
00363     for ( addrIt = dates.begin(); addrIt != dates.end(); ++addrIt ) {
00364       bool makeBold = (*addrIt).daysTo == 0; // i.e., today
00365 
00366       // Pixmap
00367       QImage icon_img;
00368       QString icon_name;
00369       KABC::Picture pic;
00370       switch( (*addrIt).category ) {
00371       case CategoryBirthday:
00372         icon_name = "calendarbirthday";
00373         pic = (*addrIt).addressee.photo();
00374         if ( pic.isIntern() && !pic.data().isNull() ) {
00375           QImage img = pic.data();
00376           if ( img.width() > img.height() ) {
00377             icon_img = img.scaleWidth( 32 );
00378           } else {
00379             icon_img = img.scaleHeight( 32 );
00380           }
00381         }
00382         break;
00383       case CategoryAnniversary:
00384         icon_name = "calendaranniversary";
00385         pic = (*addrIt).addressee.photo();
00386         if ( pic.isIntern() && !pic.data().isNull() ) {
00387           QImage img = pic.data();
00388           if ( img.width() > img.height() ) {
00389             icon_img = img.scaleWidth( 32 );
00390           } else {
00391             icon_img = img.scaleHeight( 32 );
00392           }
00393         }
00394         break;
00395       case CategoryHoliday:
00396         icon_name = "calendarholiday"; break;
00397       case CategoryOther:
00398         icon_name = "cookie"; break;
00399       }
00400       label = new QLabel( this );
00401       if ( icon_img.isNull() ) {
00402         label->setPixmap( KGlobal::iconLoader()->loadIcon( icon_name,
00403                                                            KIcon::Small ) );
00404       } else {
00405         label->setPixmap( icon_img );
00406       }
00407       label->setMaximumWidth( label->minimumSizeHint().width() );
00408       label->setAlignment( AlignVCenter );
00409       mLayout->addWidget( label, counter, 0 );
00410       mLabels.append( label );
00411 
00412       // Event date
00413       QString datestr;
00414 
00415       //Muck with the year -- change to the year 'daysTo' days away
00416       int year = currentDate.addDays( (*addrIt).daysTo ).year();
00417       QDate sD = QDate( year, (*addrIt).date.month(), (*addrIt).date.day() );
00418 
00419       if ( (*addrIt).daysTo == 0 ) {
00420         datestr = i18n( "Today" );
00421       } else if ( (*addrIt).daysTo == 1 ) {
00422         datestr = i18n( "Tomorrow" );
00423       } else {
00424         datestr = KGlobal::locale()->formatDate( sD );
00425       }
00426       // Print the date span for multiday, floating events, for the
00427       // first day of the event only.
00428       if ( (*addrIt).span > 1 ) {
00429         QString endstr =
00430           KGlobal::locale()->formatDate( sD.addDays( (*addrIt).span - 1 ) );
00431         datestr += " -\n " + endstr;
00432       }
00433 
00434       label = new QLabel( datestr, this );
00435       label->setAlignment( AlignLeft | AlignVCenter );
00436       mLayout->addWidget( label, counter, 1 );
00437       mLabels.append( label );
00438       if ( makeBold ) {
00439         QFont font = label->font();
00440         font.setBold( true );
00441         label->setFont( font );
00442       }
00443 
00444       // Countdown
00445       label = new QLabel( this );
00446       if ( (*addrIt).daysTo == 0 ) {
00447         label->setText( i18n( "now" ) );
00448       } else {
00449         label->setText( i18n( "in 1 day", "in %n days", (*addrIt).daysTo ) );
00450       }
00451 
00452       label->setAlignment( AlignLeft | AlignVCenter );
00453       mLayout->addWidget( label, counter, 2 );
00454       mLabels.append( label );
00455 
00456       // What
00457       QString what;
00458       switch( (*addrIt).category ) {
00459       case CategoryBirthday:
00460         what = i18n( "Birthday" ); break;
00461       case CategoryAnniversary:
00462         what = i18n( "Anniversary" ); break;
00463       case CategoryHoliday:
00464         what = i18n( "Holiday" ); break;
00465       case CategoryOther:
00466         what = i18n( "Special Occasion" ); break;
00467       }
00468       label = new QLabel( this );
00469       label->setText( what );
00470       label->setAlignment( AlignLeft | AlignVCenter );
00471       mLayout->addWidget( label, counter, 3 );
00472       mLabels.append( label );
00473 
00474       // Description
00475       if ( (*addrIt).type == IncidenceTypeContact ) {
00476         KURLLabel *urlLabel = new KURLLabel( this );
00477         urlLabel->installEventFilter( this );
00478         urlLabel->setURL( (*addrIt).addressee.uid() );
00479         urlLabel->setText( (*addrIt).addressee.realName() );
00480         urlLabel->setTextFormat( Qt::RichText );
00481         mLayout->addWidget( urlLabel, counter, 4 );
00482         mLabels.append( urlLabel );
00483 
00484         connect( urlLabel, SIGNAL( leftClickedURL( const QString& ) ),
00485                  this, SLOT( mailContact( const QString& ) ) );
00486         connect( urlLabel, SIGNAL( rightClickedURL( const QString& ) ),
00487                  this, SLOT( popupMenu( const QString& ) ) );
00488       } else {
00489         label = new QLabel( this );
00490         label->setText( (*addrIt).summary );
00491         label->setTextFormat( Qt::RichText );
00492         mLayout->addWidget( label, counter, 4 );
00493         mLabels.append( label );
00494         if ( !(*addrIt).desc.isEmpty() ) {
00495           QToolTip::add( label, (*addrIt).desc );
00496         }
00497       }
00498 
00499      // Age
00500       if ( (*addrIt).category == CategoryBirthday ||
00501            (*addrIt).category == CategoryAnniversary ) {
00502         label = new QLabel( this );
00503         if ( (*addrIt).yearsOld <= 0 ) {
00504           label->setText( "" );
00505         } else {
00506           label->setText( i18n( "one year", "%n years", (*addrIt).yearsOld  ) );
00507         }
00508         label->setAlignment( AlignLeft | AlignVCenter );
00509         mLayout->addWidget( label, counter, 5 );
00510         mLabels.append( label );
00511       }
00512 
00513       counter++;
00514     }
00515   } else {
00516     label = new QLabel(
00517         i18n( "No special dates within the next 1 day",
00518               "No special dates pending within the next %n days",
00519               mDaysAhead ), this, "nothing to see" );
00520     label->setAlignment( AlignHCenter | AlignVCenter );
00521     mLayout->addMultiCellWidget( label, 0, 0, 0, 4 );
00522     mLabels.append( label );
00523   }
00524 
00525   for ( label = mLabels.first(); label; label = mLabels.next() )
00526     label->show();
00527 
00528   KGlobal::locale()->setDateFormat( savefmt );
00529 }
00530 
00531 void SDSummaryWidget::mailContact( const QString &uid )
00532 {
00533   KABC::StdAddressBook *ab = KABC::StdAddressBook::self( true );
00534   QString email = ab->findByUid( uid ).fullEmail();
00535 
00536   kapp->invokeMailer( email, QString::null );
00537 }
00538 
00539 void SDSummaryWidget::viewContact( const QString &uid )
00540 {
00541   if ( !mPlugin->isRunningStandalone() )
00542     mPlugin->core()->selectPlugin( "kontact_kaddressbookplugin" );
00543   else
00544     mPlugin->bringToForeground();
00545 
00546   DCOPRef dcopCall( "kaddressbook", "KAddressBookIface" );
00547   dcopCall.send( "showContactEditor(QString)", uid );
00548 }
00549 
00550 void SDSummaryWidget::popupMenu( const QString &uid )
00551 {
00552   KPopupMenu popup( this );
00553   popup.insertItem( KGlobal::iconLoader()->loadIcon( "kmail", KIcon::Small ),
00554                     i18n( "Send &Mail" ), 0 );
00555   popup.insertItem( KGlobal::iconLoader()->loadIcon( "kaddressbook", KIcon::Small ),
00556                     i18n( "View &Contact" ), 1 );
00557 
00558   switch ( popup.exec( QCursor::pos() ) ) {
00559     case 0:
00560       mailContact( uid );
00561       break;
00562     case 1:
00563       viewContact( uid );
00564       break;
00565   }
00566 }
00567 
00568 bool SDSummaryWidget::eventFilter( QObject *obj, QEvent* e )
00569 {
00570   if ( obj->inherits( "KURLLabel" ) ) {
00571     KURLLabel* label = static_cast<KURLLabel*>( obj );
00572     if ( e->type() == QEvent::Enter )
00573       emit message( i18n( "Mail to:\"%1\"" ).arg( label->text() ) );
00574     if ( e->type() == QEvent::Leave )
00575       emit message( QString::null );
00576   }
00577 
00578   return Kontact::Summary::eventFilter( obj, e );
00579 }
00580 
00581 void SDSummaryWidget::dateDiff( const QDate &date, int &days, int &years )
00582 {
00583   QDate currentDate;
00584   QDate eventDate;
00585 
00586   if ( QDate::leapYear( date.year() ) && date.month() == 2 && date.day() == 29 ) {
00587     currentDate = QDate( date.year(), QDate::currentDate().month(), QDate::currentDate().day() );
00588     if ( !QDate::leapYear( QDate::currentDate().year() ) )
00589       eventDate = QDate( date.year(), date.month(), 28 ); // celebrate one day earlier ;)
00590     else
00591       eventDate = QDate( date.year(), date.month(), date.day() );
00592   } else {
00593     currentDate = QDate( 0, QDate::currentDate().month(), QDate::currentDate().day() );
00594     eventDate = QDate( 0, date.month(), date.day() );
00595   }
00596 
00597   int offset = currentDate.daysTo( eventDate );
00598   if ( offset < 0 ) {
00599     days = 365 + offset;
00600     years = QDate::currentDate().year() + 1 - date.year();
00601   } else {
00602     days = offset;
00603     years = QDate::currentDate().year() - date.year();
00604   }
00605 }
00606 
00607 QStringList SDSummaryWidget::configModules() const
00608 {
00609   return QStringList( "kcmsdsummary.desktop" );
00610 }
00611 
00612 #include "sdsummarywidget.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys