28 #include <KontactInterface/Core>
29 #include <KontactInterface/Plugin>
31 #include <calendarsupport/utils.h>
32 #include <calendarsupport/calendarsingleton.h>
34 #include <Akonadi/ItemFetchJob>
35 #include <Akonadi/ItemFetchScope>
36 #include <Akonadi/EntityDisplayAttribute>
37 #include <Akonadi/Contact/ContactSearchJob>
38 #include <Akonadi/Contact/ContactViewerDialog>
40 #include <KCalCore/Calendar>
45 #include <KIconLoader>
46 #include <KConfigGroup>
47 #include <KToolInvocation>
48 #include <KSystemTimeZones>
49 #include <KHolidays/Holidays>
54 #include <QGridLayout>
56 using namespace KHolidays;
58 class BirthdaySearchJob :
public Akonadi::ItemSearchJob
61 BirthdaySearchJob(
QObject *parent,
int daysInAdvance );
64 BirthdaySearchJob::BirthdaySearchJob(
QObject *parent,
int daysInAdvance )
65 : ItemSearchJob( QString(), parent )
67 fetchScope().fetchFullPayload();
68 const QString query = QString::fromLatin1(
69 "prefix nco:<http://www.semanticdesktop.org/ontologies/2007/03/22/nco#> "
70 "prefix xsd:<http://www.w3.org/2001/XMLSchema#> "
77 " ?r a nco:PersonContact . "
78 " ?r <%1> ?akonadiItemId . "
79 " ?r nco:birthDate ?birthDate . "
80 " FILTER( bif:dayofyear(?birthDate) >= bif:dayofyear(xsd:date(\"%2\")) ) "
81 " FILTER( bif:dayofyear(?birthDate) <= bif:dayofyear(xsd:date(\"%2\")) + %3 ) "
85 " ?r a nco:PersonContact . "
86 " ?r <%1> ?akonadiItemId . "
87 " ?r nco:birthDate ?birthDate . "
88 " FILTER( bif:dayofyear(?birthDate) + 365 >= bif:dayofyear(xsd:date(\"%2\")) ) "
89 " FILTER( bif:dayofyear(?birthDate) + 365 <= bif:dayofyear(xsd:date(\"%2\")) + %3 ) "
93 arg( QString::fromLatin1( Akonadi::ItemSearchJob::akonadiItemIdUri().toEncoded() ) ).
94 arg( QDate::currentDate().toString( Qt::ISODate ) ).
96 Akonadi::ItemSearchJob::setQuery( query );
122 KABC::Addressee addressee;
125 bool operator<(
const SDEntry &entry )
const
127 return daysTo < entry.daysTo;
132 : KontactInterface::Summary( parent ), mPlugin( plugin ), mHolidays( 0 )
134 mCalendar = CalendarSupport::calendarSingleton();
136 QVBoxLayout *mainLayout =
new QVBoxLayout(
this );
137 mainLayout->setSpacing( 3 );
138 mainLayout->setMargin( 3 );
141 QWidget *header = createHeader(
142 this, QLatin1String(
"favorites"), i18n(
"Upcoming Special Dates" ) );
143 mainLayout->addWidget( header );
145 mLayout =
new QGridLayout();
146 mainLayout->addItem( mLayout );
147 mLayout->setSpacing( 3 );
148 mLayout->setRowStretch( 6, 1 );
152 mShowBirthdaysFromKAB =
true;
153 mShowBirthdaysFromCal =
true;
154 mShowAnniversariesFromKAB =
true;
155 mShowAnniversariesFromCal =
true;
156 mShowHolidays =
true;
158 mShowSpecialsFromCal =
true;
161 connect( mPlugin->core(), SIGNAL(dayChanged(QDate)),
162 this, SLOT(updateView()) );
164 connect( mCalendar.data(), SIGNAL(calendarChanged()),
165 this, SLOT(updateView()) );
166 connect( mPlugin->core(), SIGNAL(dayChanged(QDate)),
167 this, SLOT(updateView()) );
180 KConfig config( QLatin1String(
"kcmsdsummaryrc") );
182 KConfigGroup group = config.group(
"Days" );
183 mDaysAhead = group.readEntry(
"DaysToShow", 7 );
185 group = config.group(
"Show" );
186 mShowBirthdaysFromKAB = group.readEntry(
"BirthdaysFromContacts",
true );
187 mShowBirthdaysFromCal = group.readEntry(
"BirthdaysFromCalendar",
true );
189 mShowAnniversariesFromKAB = group.readEntry(
"AnniversariesFromContacts",
true );
190 mShowAnniversariesFromCal = group.readEntry(
"AnniversariesFromCalendar",
true );
192 mShowHolidays = group.readEntry(
"HolidaysFromCalendar",
true );
194 mShowSpecialsFromCal = group.readEntry(
"SpecialsFromCalendar",
true );
196 group = config.group(
"Groupware" );
197 mShowMineOnly = group.readEntry(
"ShowMineOnly",
false );
202 bool SDSummaryWidget::initHolidays()
204 KConfig _hconfig( QLatin1String(
"korganizerrc") );
205 KConfigGroup hconfig( &_hconfig,
"Time & Date" );
206 QString location = hconfig.readEntry(
"Holidays" );
207 if ( !location.isEmpty() ) {
209 mHolidays =
new HolidayRegion( location );
216 int SDSummaryWidget::span(
const KCalCore::Event::Ptr &event )
const
219 if ( event->isMultiDay() &&
event->allDay() ) {
220 QDate d =
event->dtStart().date();
221 if ( d < QDate::currentDate() ) {
222 d = QDate::currentDate();
224 while ( d < event->dtEnd().
date() ) {
233 int SDSummaryWidget::dayof(
const KCalCore::Event::Ptr &event,
const QDate &
date )
const
236 QDate d =
event->dtStart().date();
237 if ( d < QDate::currentDate() ) {
238 d = QDate::currentDate();
240 while ( d < event->dtEnd().
date() ) {
249 void SDSummaryWidget::slotBirthdayJobFinished(
KJob *job )
252 BirthdaySearchJob* bJob =
dynamic_cast< BirthdaySearchJob *
>( job );
254 foreach (
const Akonadi::Item &item, bJob->items() ) {
255 if ( item.hasPayload<KABC::Addressee>() ) {
256 const KABC::Addressee addressee = item.payload<KABC::Addressee>();
257 const QDate birthday = addressee.birthday().date();
258 if ( birthday.isValid() ) {
262 dateDiff( birthday, entry.daysTo, entry.yearsOld );
264 entry.date = birthday;
265 entry.addressee = addressee;
268 mDates.append( entry );
279 void SDSummaryWidget::createLabels()
281 KIconLoader loader( QLatin1String(
"kdepim") );
287 setUpdatesEnabled(
false );
288 foreach ( label, mLabels ) {
289 mLayout->removeWidget( label );
296 for ( dt = QDate::currentDate();
297 dt <= QDate::currentDate().addDays( mDaysAhead - 1 );
298 dt = dt.addDays( 1 ) ) {
299 KCalCore::Event::List events = mCalendar->events( dt, mCalendar->timeSpec(),
300 KCalCore::EventSortStartDate,
301 KCalCore::SortDirectionAscending );
302 foreach (
const KCalCore::Event::Ptr &ev, events ) {
312 if ( ev->customProperty(
"KABC",
"BIRTHDAY" ) == QLatin1String(
"YES") ) {
318 if ( !ev->categoriesStr().isEmpty() ) {
319 QStringList::ConstIterator it2;
320 const QStringList c = ev->categories();
321 QStringList::ConstIterator end(c.constEnd());
322 for ( it2 = c.constBegin(); it2 != end; ++it2 ) {
324 const QString itUpper((*it2).toUpper());
326 if ( mShowBirthdaysFromCal && ( itUpper == QLatin1String(
"BIRTHDAY") ) ) {
331 entry.summary = ev->summary();
332 entry.desc = ev->description();
333 dateDiff( ev->dtStart().date(), entry.daysTo, entry.yearsOld );
342 mDates.append( entry );
348 if ( mShowAnniversariesFromCal && ( itUpper == QLatin1String(
"ANNIVERSARY") ) ) {
353 entry.summary = ev->summary();
354 entry.desc = ev->description();
355 dateDiff( ev->dtStart().date(), entry.daysTo, entry.yearsOld );
358 mDates.append( entry );
364 if ( mShowHolidays && ( itUpper == QLatin1String(
"HOLIDAY") ) ) {
369 entry.summary = ev->summary();
370 entry.desc = ev->description();
371 dateDiff( dt, entry.daysTo, entry.yearsOld );
373 entry.span = span( ev );
374 if ( entry.span > 1 && dayof( ev, dt ) > 1 ) {
377 mDates.append( entry );
382 if ( mShowSpecialsFromCal && ( itUpper == QLatin1String(
"SPECIAL OCCASION") ) ) {
387 entry.summary = ev->summary();
388 entry.desc = ev->description();
389 dateDiff( dt, entry.daysTo, entry.yearsOld );
391 entry.span = span( ev );
392 if ( entry.span > 1 && dayof( ev, dt ) > 1 ) {
395 mDates.append( entry );
404 if ( mShowHolidays ) {
405 if ( initHolidays() ) {
406 for ( dt=QDate::currentDate();
407 dt<=QDate::currentDate().addDays( mDaysAhead - 1 );
411 for ( ; it != holidays.constEnd(); ++it ) {
414 entry.category = ( (*it).dayType() == Holiday::NonWorkday )?
417 entry.summary = (*it).text();
418 dateDiff( dt, entry.daysTo, entry.yearsOld );
422 mDates.append( entry );
431 if ( !mDates.isEmpty() ) {
435 for ( addrIt = mDates.begin(); addrIt != addrEnd; ++addrIt ) {
436 const bool makeBold = (*addrIt).daysTo == 0;
442 switch( (*addrIt).category ) {
444 icon_name = QLatin1String(
"view-calendar-birthday");
445 pic = (*addrIt).addressee.photo();
446 if ( pic.isIntern() && !pic.data().isNull() ) {
447 QImage img = pic.data();
448 if ( img.width() > img.height() ) {
449 icon_img = img.scaledToWidth( 32 );
451 icon_img = img.scaledToHeight( 32 );
456 icon_name = QLatin1String(
"view-calendar-wedding-anniversary");
457 pic = (*addrIt).addressee.photo();
458 if ( pic.isIntern() && !pic.data().isNull() ) {
459 QImage img = pic.data();
460 if ( img.width() > img.height() ) {
461 icon_img = img.scaledToWidth( 32 );
463 icon_img = img.scaledToHeight( 32 );
468 icon_name = QLatin1String(
"view-calendar-holiday");
471 icon_name = QLatin1String(
"favorites");
474 label =
new QLabel(
this );
475 if ( icon_img.isNull() ) {
476 label->setPixmap( KIconLoader::global()->loadIcon( icon_name, KIconLoader::Small ) );
478 label->setPixmap( QPixmap::fromImage( icon_img ) );
480 label->setMaximumWidth( label->minimumSizeHint().width() );
481 label->setAlignment( Qt::AlignVCenter );
482 mLayout->addWidget( label, counter, 0 );
483 mLabels.append( label );
489 int year = QDate::currentDate().addDays( (*addrIt).daysTo ).year();
490 QDate sD = QDate( year, (*addrIt).date.month(), (*addrIt).date.day() );
492 if ( (*addrIt).daysTo == 0 ) {
493 datestr = i18nc(
"the special day is today",
"Today" );
494 }
else if ( (*addrIt).daysTo == 1 ) {
495 datestr = i18nc(
"the special day is tomorrow",
"Tomorrow" );
497 datestr = KGlobal::locale()->formatDate( sD, KLocale::FancyLongDate );
501 if ( (*addrIt).span > 1 ) {
503 KGlobal::locale()->formatDate( sD.addDays( (*addrIt).span - 1 ) );
504 datestr += QLatin1String(
" -\n ") + endstr;
507 label =
new QLabel( datestr,
this );
508 label->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
509 mLayout->addWidget( label, counter, 1 );
510 mLabels.append( label );
512 QFont font = label->font();
513 font.setBold(
true );
514 label->setFont( font );
518 label =
new QLabel(
this );
519 if ( (*addrIt).daysTo == 0 ) {
520 label->setText( i18n(
"now" ) );
522 label->setText( i18np(
"in 1 day",
"in %1 days", (*addrIt).daysTo ) );
525 label->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
526 mLayout->addWidget( label, counter, 2 );
527 mLabels.append( label );
531 switch( (*addrIt).category ) {
533 what = i18n(
"Birthday" );
536 what = i18n(
"Anniversary" );
539 what = i18n(
"Holiday" );
542 what = i18n(
"Special Occasion" );
545 label =
new QLabel(
this );
546 label->setText( what );
547 label->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
548 mLayout->addWidget( label, counter, 3 );
549 mLabels.append( label );
553 KUrlLabel *urlLabel =
new KUrlLabel(
this );
554 urlLabel->installEventFilter(
this );
555 urlLabel->setUrl( (*addrIt).item.url( Akonadi::Item::UrlWithMimeType ).url() );
556 urlLabel->setText( (*addrIt).addressee.realName() );
557 urlLabel->setTextFormat( Qt::RichText );
558 urlLabel->setWordWrap(
true );
559 mLayout->addWidget( urlLabel, counter, 4 );
560 mLabels.append( urlLabel );
562 connect( urlLabel, SIGNAL(leftClickedUrl(QString)),
563 this, SLOT(mailContact(QString)) );
564 connect( urlLabel, SIGNAL(rightClickedUrl(QString)),
565 this, SLOT(popupMenu(QString)) );
567 label =
new QLabel(
this );
568 label->setText( (*addrIt).summary );
569 label->setTextFormat( Qt::RichText );
570 mLayout->addWidget( label, counter, 4 );
571 mLabels.append( label );
572 if ( !(*addrIt).desc.isEmpty() ) {
573 label->setToolTip( (*addrIt).desc );
580 label =
new QLabel(
this );
581 if ( (*addrIt).yearsOld <= 0 ) {
582 label->setText( QString() );
584 label->setText( i18np(
"one year",
"%1 years", (*addrIt).yearsOld ) );
586 label->setAlignment( Qt::AlignLeft | Qt::AlignVCenter );
587 mLayout->addWidget( label, counter, 5 );
588 mLabels.append( label );
595 i18np(
"No special dates within the next 1 day",
596 "No special dates pending within the next %1 days",
597 mDaysAhead ),
this );
598 label->setAlignment( Qt::AlignHCenter | Qt::AlignVCenter );
599 mLayout->addWidget( label, 0, 0 );
600 mLabels.append( label );
605 for ( lit = mLabels.constBegin(); lit != endLit; ++lit ) {
608 setUpdatesEnabled(
true );
611 void SDSummaryWidget::updateView()
634 if ( mShowBirthdaysFromKAB && !mJobRunning ) {
635 BirthdaySearchJob *job =
new BirthdaySearchJob(
this, mDaysAhead );
637 connect( job, SIGNAL(result(
KJob*)),
this, SLOT(slotBirthdayJobFinished(
KJob*)) );
645 void SDSummaryWidget::mailContact(
const QString &url )
647 const Akonadi::Item item = Akonadi::Item::fromUrl( url );
648 if ( !item.isValid() ) {
649 kDebug() << QLatin1String(
"Invalid item found");
653 Akonadi::ItemFetchJob *job =
new Akonadi::ItemFetchJob( item,
this );
654 job->fetchScope().fetchFullPayload();
655 connect( job, SIGNAL(result(
KJob*)), SLOT(slotItemFetchJobDone(
KJob*)) );
658 void SDSummaryWidget::slotItemFetchJobDone(
KJob* job)
660 if ( job->error() ) {
661 kWarning() << job->errorString();
664 const Akonadi::Item::List lst = qobject_cast<Akonadi::ItemFetchJob*>( job )->items();
665 if ( lst.isEmpty() ) {
668 const KABC::Addressee contact = lst.first().payload<KABC::Addressee>();
670 KToolInvocation::invokeMailer( contact.fullEmail(), QString() );
673 void SDSummaryWidget::viewContact(
const QString &url )
675 const Akonadi::Item item = Akonadi::Item::fromUrl( url );
676 if ( !item.isValid() ) {
677 kDebug() <<
"Invalid item found";
681 Akonadi::ContactViewerDialog dlg(
this );
682 dlg.setContact( item );
686 void SDSummaryWidget::popupMenu(
const QString &url )
689 const QAction *sendMailAction = popup.addAction(
690 KIconLoader::global()->loadIcon( QLatin1String(
"mail-message-new"), KIconLoader::Small ),
691 i18n(
"Send &Mail" ) );
692 const QAction *viewContactAction = popup.addAction(
693 KIconLoader::global()->loadIcon( QLatin1String(
"view-pim-contacts"), KIconLoader::Small ),
694 i18n(
"View &Contact" ) );
696 const QAction *ret = popup.exec( QCursor::pos() );
697 if ( ret == sendMailAction ) {
699 }
else if ( ret == viewContactAction ) {
706 if ( obj->inherits(
"KUrlLabel" ) ) {
707 KUrlLabel* label =
static_cast<KUrlLabel*
>( obj );
708 if ( e->type() == QEvent::Enter ) {
709 emit message( i18n(
"Mail to:\"%1\"", label->text() ) );
711 if ( e->type() == QEvent::Leave ) {
712 emit message( QString::null );
716 return KontactInterface::Summary::eventFilter( obj, e );
719 void SDSummaryWidget::dateDiff(
const QDate &date,
int &days,
int &years )
const
724 if ( QDate::isLeapYear( date.year() ) && date.month() == 2 && date.day() == 29 ) {
725 currentDate = QDate( date.year(), QDate::currentDate().month(), QDate::currentDate().day() );
726 if ( !QDate::isLeapYear( QDate::currentDate().year() ) ) {
727 eventDate = QDate( date.year(), date.month(), 28 );
729 eventDate = QDate( date.year(), date.month(), date.day() );
732 currentDate = QDate( QDate::currentDate().year(),
733 QDate::currentDate().month(),
734 QDate::currentDate().day() );
735 eventDate = QDate( QDate::currentDate().year(), date.month(), date.day() );
738 int offset = currentDate.daysTo( eventDate );
741 years = QDate::currentDate().year() + 1 - date.year();
744 years = QDate::currentDate().year() - date.year();
750 return QStringList() << QLatin1String(
"kcmsdsummary.desktop" );
753 #include "sdsummarywidget.moc"