File indexing completed on 2025-01-19 04:51:28
0001 /* 0002 This file is part of Kontact. 0003 0004 SPDX-FileCopyrightText: 2003 Tobias Koenig <tokoe@kde.org> 0005 SPDX-FileCopyrightText: 2004, 2009 Allen Winter <winter@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0 0008 */ 0009 0010 #include "sdsummarywidget.h" 0011 #include "korganizer_kontactplugins_specialdates_debug.h" 0012 #include <KontactInterface/Core> 0013 #include <KontactInterface/Plugin> 0014 0015 #include <Akonadi/ContactSearchJob> 0016 #include <Akonadi/ContactViewerDialog> 0017 #include <Akonadi/EntityDisplayAttribute> 0018 #include <Akonadi/ItemFetchJob> 0019 #include <Akonadi/ItemFetchScope> 0020 #include <Akonadi/SearchQuery> 0021 #include <CalendarSupport/CalendarSingleton> 0022 #include <CalendarSupport/Utils> 0023 0024 #include <KCalendarCore/Calendar> 0025 0026 #include <KConfig> 0027 #include <KConfigGroup> 0028 #include <KHolidays/HolidayRegion> 0029 #include <KLocalizedString> 0030 #include <KUrlLabel> 0031 #include <QDesktopServices> 0032 #include <QMenu> 0033 #include <QPointer> 0034 0035 #include <QDate> 0036 #include <QEvent> 0037 #include <QGridLayout> 0038 #include <QLabel> 0039 #include <QStyle> 0040 0041 using namespace KHolidays; 0042 0043 class BirthdaySearchJob : public Akonadi::ItemSearchJob 0044 { 0045 Q_OBJECT 0046 public: 0047 explicit BirthdaySearchJob(QObject *parent, int daysInAdvance); 0048 }; 0049 0050 BirthdaySearchJob::BirthdaySearchJob(QObject *parent, int daysInAdvance) 0051 : ItemSearchJob(parent) 0052 { 0053 fetchScope().fetchFullPayload(); 0054 setMimeTypes({KContacts::Addressee::mimeType()}); 0055 0056 Akonadi::SearchQuery query; 0057 query.addTerm(QStringLiteral("birthday"), QDate::currentDate().toJulianDay(), Akonadi::SearchTerm::CondGreaterOrEqual); 0058 query.addTerm(QStringLiteral("birthday"), QDate::currentDate().addDays(daysInAdvance).toJulianDay(), Akonadi::SearchTerm::CondLessOrEqual); 0059 0060 ItemSearchJob::setQuery(query); 0061 } 0062 0063 enum SDIncidenceType { IncidenceTypeContact, IncidenceTypeEvent }; 0064 0065 enum SDCategory { CategoryBirthday, CategoryAnniversary, CategoryHoliday, CategorySeasonal, CategoryOther }; 0066 0067 class SDEntry 0068 { 0069 public: 0070 SDIncidenceType type; 0071 SDCategory category; 0072 int yearsOld; 0073 int daysTo; 0074 QDate date; 0075 QString summary; 0076 QString desc; 0077 int span; // #days in the special occasion. 0078 KContacts::Addressee addressee; 0079 Akonadi::Item item; 0080 0081 bool operator<(const SDEntry &entry) const 0082 { 0083 return daysTo < entry.daysTo; 0084 } 0085 }; 0086 0087 SDSummaryWidget::SDSummaryWidget(KontactInterface::Plugin *plugin, QWidget *parent) 0088 : KontactInterface::Summary(parent) 0089 , mPlugin(plugin) 0090 { 0091 mCalendar = CalendarSupport::calendarSingleton(); 0092 // Create the Summary Layout 0093 auto mainLayout = new QVBoxLayout(this); 0094 mainLayout->setSpacing(3); 0095 mainLayout->setContentsMargins(3, 3, 3, 3); 0096 0097 QWidget *header = createHeader(this, QStringLiteral("view-calendar-special-occasion"), i18n("Upcoming Special Dates")); 0098 mainLayout->addWidget(header); 0099 0100 mLayout = new QGridLayout(); 0101 mainLayout->addItem(mLayout); 0102 mLayout->setSpacing(3); 0103 mLayout->setRowStretch(6, 1); 0104 0105 // Default settings 0106 mDaysAhead = 7; 0107 mShowBirthdaysFromKAB = true; 0108 mShowBirthdaysFromCal = true; 0109 mShowAnniversariesFromKAB = true; 0110 mShowAnniversariesFromCal = true; 0111 mShowHolidays = true; 0112 mJobRunning = false; 0113 mShowSpecialsFromCal = true; 0114 0115 // Setup the Addressbook 0116 connect(mPlugin->core(), &KontactInterface::Core::dayChanged, this, &SDSummaryWidget::updateView); 0117 0118 connect(mCalendar.data(), &Akonadi::ETMCalendar::calendarChanged, this, &SDSummaryWidget::updateView); 0119 0120 // Update Configuration 0121 configUpdated(); 0122 } 0123 0124 SDSummaryWidget::~SDSummaryWidget() 0125 { 0126 delete mHolidays; 0127 } 0128 0129 void SDSummaryWidget::configUpdated() 0130 { 0131 KConfig config(QStringLiteral("kcmsdsummaryrc")); 0132 0133 KConfigGroup group = config.group(QStringLiteral("Days")); 0134 mDaysAhead = group.readEntry("DaysToShow", 7); 0135 0136 group = config.group(QStringLiteral("Show")); 0137 mShowBirthdaysFromKAB = group.readEntry("BirthdaysFromContacts", true); 0138 mShowBirthdaysFromCal = group.readEntry("BirthdaysFromCalendar", true); 0139 0140 mShowAnniversariesFromKAB = group.readEntry("AnniversariesFromContacts", true); 0141 mShowAnniversariesFromCal = group.readEntry("AnniversariesFromCalendar", true); 0142 0143 mShowHolidays = group.readEntry("HolidaysFromCalendar", true); 0144 0145 mShowSpecialsFromCal = group.readEntry("SpecialsFromCalendar", true); 0146 0147 group = config.group(QStringLiteral("Groupware")); 0148 mShowMineOnly = group.readEntry("ShowMineOnly", false); 0149 0150 updateView(); 0151 } 0152 0153 bool SDSummaryWidget::initHolidays() 0154 { 0155 KConfig _hconfig(QStringLiteral("korganizerrc")); 0156 KConfigGroup hconfig(&_hconfig, QStringLiteral("Time & Date")); 0157 QString location = hconfig.readEntry("Holidays"); 0158 if (!location.isEmpty()) { 0159 delete mHolidays; 0160 mHolidays = new HolidayRegion(location); 0161 return true; 0162 } 0163 return false; 0164 } 0165 0166 // number of days remaining in an Event 0167 int SDSummaryWidget::span(const KCalendarCore::Event::Ptr &event) const 0168 { 0169 int span = 1; 0170 if (event->isMultiDay() && event->allDay()) { 0171 QDate d = event->dtStart().date(); 0172 if (d < QDate::currentDate()) { 0173 d = QDate::currentDate(); 0174 } 0175 while (d < event->dtEnd().date()) { 0176 span++; 0177 d = d.addDays(1); 0178 } 0179 } 0180 return span; 0181 } 0182 0183 // day of a multiday Event 0184 int SDSummaryWidget::dayof(const KCalendarCore::Event::Ptr &event, const QDate &date) const 0185 { 0186 int dayof = 1; 0187 QDate d = event->dtStart().date(); 0188 if (d < QDate::currentDate()) { 0189 d = QDate::currentDate(); 0190 } 0191 while (d < event->dtEnd().date()) { 0192 if (d < date) { 0193 dayof++; 0194 } 0195 d = d.addDays(1); 0196 } 0197 return dayof; 0198 } 0199 0200 void SDSummaryWidget::slotBirthdayJobFinished(KJob *job) 0201 { 0202 // ;) 0203 auto bJob = qobject_cast<BirthdaySearchJob *>(job); 0204 if (bJob) { 0205 const auto items = bJob->items(); 0206 for (const Akonadi::Item &item : items) { 0207 if (item.hasPayload<KContacts::Addressee>()) { 0208 const auto addressee = item.payload<KContacts::Addressee>(); 0209 const QDate birthday = addressee.birthday().date(); 0210 if (birthday.isValid()) { 0211 SDEntry entry; 0212 entry.type = IncidenceTypeContact; 0213 entry.category = CategoryBirthday; 0214 dateDiff(birthday, entry.daysTo, entry.yearsOld); 0215 if (entry.daysTo < mDaysAhead) { 0216 // We need to check the days ahead here because we don't 0217 // filter out Contact Birthdays by mDaysAhead in createLabels(). 0218 entry.date = birthday; 0219 entry.addressee = addressee; 0220 entry.item = item; 0221 entry.span = 1; 0222 mDates.append(entry); 0223 } 0224 } 0225 } 0226 } 0227 // Carry on. 0228 createLabels(); 0229 } 0230 0231 mJobRunning = false; 0232 } 0233 0234 void SDSummaryWidget::createLabels() 0235 { 0236 // Remove all special date labels from the layout and delete them, as we 0237 // will re-create all labels below. 0238 setUpdatesEnabled(false); 0239 for (QLabel *label : std::as_const(mLabels)) { 0240 mLayout->removeWidget(label); 0241 delete (label); 0242 update(); 0243 } 0244 mLabels.clear(); 0245 0246 QDate dt; 0247 for (dt = QDate::currentDate(); dt <= QDate::currentDate().addDays(mDaysAhead - 1); dt = dt.addDays(1)) { 0248 const KCalendarCore::Event::List events = 0249 mCalendar->events(dt, mCalendar->timeZone(), KCalendarCore::EventSortStartDate, KCalendarCore::SortDirectionAscending); 0250 for (const KCalendarCore::Event::Ptr &ev : events) { 0251 // Optionally, show only my Events 0252 /* if ( mShowMineOnly && 0253 !KCalendarCore::CalHelper::isMyCalendarIncidence( mCalendarAdaptor, ev. ) ) { 0254 // FIXME; does isMyCalendarIncidence work !? It's deprecated too. 0255 continue; 0256 } 0257 // TODO: CalHelper is deprecated, remove this? 0258 */ 0259 0260 if (ev->customProperty("KABC", "BIRTHDAY") == QLatin1StringView("YES")) { 0261 // Skipping, because these are got by the BirthdaySearchJob 0262 // See comments in updateView() 0263 continue; 0264 } 0265 0266 if (!mShowAnniversariesFromKAB && ev->customProperty("KABC", "ANNIVERSARY") == QLatin1StringView("YES")) { 0267 continue; 0268 } 0269 0270 if (!ev->categoriesStr().isEmpty()) { 0271 QStringList::ConstIterator it2; 0272 const QStringList c = ev->categories(); 0273 QStringList::ConstIterator end(c.constEnd()); 0274 for (it2 = c.constBegin(); it2 != end; ++it2) { 0275 const QString itUpper((*it2).toUpper()); 0276 // Append Birthday Event? 0277 if (mShowBirthdaysFromCal && (itUpper == QLatin1StringView("BIRTHDAY"))) { 0278 SDEntry entry; 0279 entry.type = IncidenceTypeEvent; 0280 entry.category = CategoryBirthday; 0281 entry.date = dt; 0282 entry.summary = ev->summary(); 0283 entry.desc = ev->description(); 0284 dateDiff(ev->dtStart().date(), entry.daysTo, entry.yearsOld); 0285 entry.span = 1; 0286 0287 /* The following check is to prevent duplicate entries, 0288 * so in case of having a KCal incidence with category birthday 0289 * with summary and date equal to some KABC Attendee we don't show it 0290 * FIXME: port to akonadi, it's kresource based 0291 * */ 0292 if (/*!check( bdayRes, dt, ev->summary() )*/ true) { 0293 mDates.append(entry); 0294 } 0295 break; 0296 } 0297 0298 // Append Anniversary Event? 0299 if (itUpper == QLatin1StringView("ANNIVERSARY")) { 0300 // !mShowAnniversariesFromKAB was handled above. 0301 const bool isKAB = ev->customProperty("KABC", "ANNIVERSARY") == QLatin1StringView("YES"); 0302 if (isKAB || mShowAnniversariesFromCal) { 0303 SDEntry entry; 0304 entry.type = IncidenceTypeEvent; 0305 entry.category = CategoryAnniversary; 0306 entry.date = dt; 0307 entry.summary = ev->summary(); 0308 entry.desc = ev->description(); 0309 dateDiff(ev->dtStart().date(), entry.daysTo, entry.yearsOld); 0310 entry.span = 1; 0311 mDates.append(entry); 0312 } 0313 break; 0314 } 0315 0316 // Append Holiday Event? 0317 if (mShowHolidays && (itUpper == QLatin1StringView("HOLIDAY"))) { 0318 SDEntry entry; 0319 entry.type = IncidenceTypeEvent; 0320 entry.category = CategoryHoliday; 0321 entry.date = dt; 0322 entry.summary = ev->summary(); 0323 entry.desc = ev->description(); 0324 dateDiff(dt, entry.daysTo, entry.yearsOld); 0325 entry.yearsOld = -1; // ignore age of holidays 0326 entry.span = span(ev); 0327 if (entry.span > 1 && dayof(ev, dt) > 1) { // skip days 2,3,... 0328 break; 0329 } 0330 mDates.append(entry); 0331 break; 0332 } 0333 0334 // Append Special Occasion Event? 0335 if (mShowSpecialsFromCal && (itUpper == QLatin1StringView("SPECIAL OCCASION"))) { 0336 SDEntry entry; 0337 entry.type = IncidenceTypeEvent; 0338 entry.category = CategoryOther; 0339 entry.date = dt; 0340 entry.summary = ev->summary(); 0341 entry.desc = ev->description(); 0342 dateDiff(dt, entry.daysTo, entry.yearsOld); 0343 entry.yearsOld = -1; // ignore age of special occasions 0344 entry.span = span(ev); 0345 if (entry.span > 1 && dayof(ev, dt) > 1) { // skip days 2,3,... 0346 break; 0347 } 0348 mDates.append(entry); 0349 break; 0350 } 0351 } 0352 } 0353 } 0354 } 0355 0356 // Search for Holidays 0357 if (mShowHolidays) { 0358 if (initHolidays()) { 0359 for (dt = QDate::currentDate(); dt <= QDate::currentDate().addDays(mDaysAhead - 1); dt = dt.addDays(1)) { 0360 QList<Holiday> holidays = mHolidays->rawHolidaysWithAstroSeasons(dt); 0361 QList<Holiday>::ConstIterator it = holidays.constBegin(); 0362 for (; it != holidays.constEnd(); ++it) { 0363 SDEntry entry; 0364 entry.type = IncidenceTypeEvent; 0365 if ((*it).categoryList().contains(QLatin1StringView("seasonal"))) { 0366 entry.category = CategorySeasonal; 0367 } else if ((*it).categoryList().contains(QLatin1StringView("public"))) { 0368 entry.category = CategoryHoliday; 0369 } else { 0370 entry.category = CategoryOther; 0371 } 0372 entry.date = dt; 0373 entry.summary = (*it).name(); 0374 dateDiff(dt, entry.daysTo, entry.yearsOld); 0375 entry.yearsOld = -1; // ignore age of holidays 0376 entry.span = 1; 0377 0378 mDates.append(entry); 0379 } 0380 } 0381 } 0382 } 0383 0384 // Sort, then Print the Special Dates 0385 std::sort(mDates.begin(), mDates.end()); 0386 0387 if (!mDates.isEmpty()) { 0388 int counter = 0; 0389 QList<SDEntry>::Iterator addrIt; 0390 QList<SDEntry>::Iterator addrEnd(mDates.end()); 0391 for (addrIt = mDates.begin(); addrIt != addrEnd; ++addrIt) { 0392 const bool makeBold = (*addrIt).daysTo == 0; // i.e., today 0393 0394 // Pixmap 0395 QImage icon_img; 0396 QString icon_name; 0397 KContacts::Picture pic; 0398 switch ((*addrIt).category) { 0399 case CategoryBirthday: 0400 icon_name = QStringLiteral("view-calendar-birthday"); 0401 pic = (*addrIt).addressee.photo(); 0402 if (pic.isIntern() && !pic.data().isNull()) { 0403 QImage img = pic.data(); 0404 if (img.width() > img.height()) { 0405 icon_img = img.scaledToWidth(32); 0406 } else { 0407 icon_img = img.scaledToHeight(32); 0408 } 0409 } 0410 break; 0411 case CategoryAnniversary: 0412 icon_name = QStringLiteral("view-calendar-wedding-anniversary"); 0413 pic = (*addrIt).addressee.photo(); 0414 if (pic.isIntern() && !pic.data().isNull()) { 0415 QImage img = pic.data(); 0416 if (img.width() > img.height()) { 0417 icon_img = img.scaledToWidth(32); 0418 } else { 0419 icon_img = img.scaledToHeight(32); 0420 } 0421 } 0422 break; 0423 case CategoryHoliday: 0424 icon_name = QStringLiteral("view-calendar-holiday"); 0425 break; 0426 case CategorySeasonal: 0427 case CategoryOther: 0428 icon_name = QStringLiteral("view-calendar-special-occasion"); 0429 break; 0430 } 0431 auto label = new QLabel(this); 0432 if (icon_img.isNull()) { 0433 label->setPixmap(QIcon::fromTheme(icon_name).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize))); 0434 } else { 0435 label->setPixmap(QPixmap::fromImage(icon_img)); 0436 } 0437 label->setMaximumWidth(label->minimumSizeHint().width()); 0438 label->setAlignment(Qt::AlignVCenter); 0439 mLayout->addWidget(label, counter, 0); 0440 mLabels.append(label); 0441 0442 // Event date 0443 QString datestr; 0444 0445 // Muck with the year -- change to the year 'daysTo' days away 0446 int year = QDate::currentDate().addDays((*addrIt).daysTo).year(); 0447 QDate sD = QDate(year, (*addrIt).date.month(), (*addrIt).date.day()); 0448 0449 if ((*addrIt).daysTo == 0) { 0450 datestr = i18nc("the special day is today", "Today"); 0451 } else if ((*addrIt).daysTo == 1) { 0452 datestr = i18nc("the special day is tomorrow", "Tomorrow"); 0453 } else { 0454 const auto locale = QLocale::system(); 0455 for (int i = 3; i < 8; ++i) { 0456 if ((*addrIt).daysTo < i) { 0457 datestr = locale.dayName(sD.dayOfWeek(), QLocale::LongFormat); 0458 break; 0459 } 0460 } 0461 if (datestr.isEmpty()) { 0462 datestr = locale.toString(sD, QLocale::ShortFormat); 0463 } 0464 } 0465 // Print the date span for multiday, floating events, for the 0466 // first day of the event only. 0467 if ((*addrIt).span > 1) { 0468 QString endstr = QLocale::system().toString(sD.addDays((*addrIt).span - 1), QLocale::LongFormat); 0469 datestr += QLatin1StringView(" -\n ") + endstr; 0470 } 0471 0472 label = new QLabel(datestr, this); 0473 label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0474 mLayout->addWidget(label, counter, 1); 0475 mLabels.append(label); 0476 if (makeBold) { 0477 QFont font = label->font(); 0478 font.setBold(true); 0479 label->setFont(font); 0480 } 0481 0482 // Countdown 0483 label = new QLabel(this); 0484 if ((*addrIt).daysTo == 0) { 0485 label->setText(i18n("now")); 0486 } else { 0487 label->setText(i18np("in 1 day", "in %1 days", (*addrIt).daysTo)); 0488 } 0489 0490 label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0491 mLayout->addWidget(label, counter, 2); 0492 mLabels.append(label); 0493 0494 // What 0495 QString what; 0496 switch ((*addrIt).category) { 0497 case CategoryBirthday: 0498 what = i18n("Birthday"); 0499 break; 0500 case CategoryAnniversary: 0501 what = i18n("Anniversary"); 0502 break; 0503 case CategoryHoliday: 0504 what = i18n("Holiday"); 0505 break; 0506 case CategorySeasonal: 0507 what = i18n("Change of Seasons"); 0508 break; 0509 case CategoryOther: 0510 what = i18n("Special Occasion"); 0511 break; 0512 } 0513 label = new QLabel(this); 0514 label->setText(what); 0515 label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0516 mLayout->addWidget(label, counter, 3); 0517 mLabels.append(label); 0518 0519 // Description 0520 if ((*addrIt).type == IncidenceTypeContact) { 0521 auto urlLabel = new KUrlLabel(this); 0522 urlLabel->installEventFilter(this); 0523 urlLabel->setUrl((*addrIt).item.url(Akonadi::Item::UrlWithMimeType).url()); 0524 urlLabel->setText((*addrIt).addressee.realName()); 0525 urlLabel->setTextFormat(Qt::RichText); 0526 urlLabel->setWordWrap(true); 0527 mLayout->addWidget(urlLabel, counter, 4); 0528 mLabels.append(urlLabel); 0529 connect(urlLabel, &KUrlLabel::leftClickedUrl, this, [this, urlLabel] { 0530 mailContact(urlLabel->url()); 0531 }); 0532 connect(urlLabel, &KUrlLabel::rightClickedUrl, this, [this, urlLabel] { 0533 popupMenu(urlLabel->url()); 0534 }); 0535 } else { 0536 label = new QLabel(this); 0537 label->setText((*addrIt).summary); 0538 label->setTextFormat(Qt::RichText); 0539 mLayout->addWidget(label, counter, 4); 0540 mLabels.append(label); 0541 if (!(*addrIt).desc.isEmpty()) { 0542 label->setToolTip((*addrIt).desc); 0543 } 0544 } 0545 0546 // Age 0547 if ((*addrIt).category == CategoryBirthday || (*addrIt).category == CategoryAnniversary) { 0548 label = new QLabel(this); 0549 if ((*addrIt).yearsOld <= 0) { 0550 label->setText(QString()); 0551 } else { 0552 label->setText(i18np("one year", "%1 years", (*addrIt).yearsOld)); 0553 } 0554 label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0555 mLayout->addWidget(label, counter, 5); 0556 mLabels.append(label); 0557 } 0558 0559 counter++; 0560 } 0561 } else { 0562 auto label = new QLabel(i18np("No special dates within the next 1 day", "No special dates pending within the next %1 days", mDaysAhead), this); 0563 label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); 0564 mLayout->addWidget(label, 0, 0); 0565 mLabels.append(label); 0566 } 0567 0568 QList<QLabel *>::ConstIterator lit; 0569 QList<QLabel *>::ConstIterator endLit(mLabels.constEnd()); 0570 for (lit = mLabels.constBegin(); lit != endLit; ++lit) { 0571 (*lit)->show(); 0572 } 0573 setUpdatesEnabled(true); 0574 } 0575 0576 void SDSummaryWidget::updateView() 0577 { 0578 mDates.clear(); 0579 0580 /* KABC Birthdays are got through a ItemSearchJob/SPARQL Query 0581 * I then added an ETM/CalendarModel because we need to search 0582 * for calendar entries that have birthday/anniversary categories too. 0583 * 0584 * Also, we can't get KABC Anniversaries through nepomuk because the 0585 * current S.D.O doesn't support it, so i also them through the ETM. 0586 * 0587 * So basically we have: 0588 * Calendar anniversaries - ETM 0589 * Calendar birthdays - ETM 0590 * KABC birthdays - BirthdaySearchJob 0591 * KABC anniversaries - ETM ( needs Birthday Agent running ) 0592 * 0593 * We could remove thomas' BirthdaySearchJob and use the ETM for that 0594 * but it has the advantage that we don't need a Birthday agent running. 0595 * 0596 **/ 0597 0598 if (mShowBirthdaysFromKAB) { 0599 if (!mJobRunning) { 0600 auto job = new BirthdaySearchJob(this, mDaysAhead); 0601 connect(job, &BirthdaySearchJob::result, this, &SDSummaryWidget::slotBirthdayJobFinished); 0602 job->start(); 0603 mJobRunning = true; 0604 // The result slot will trigger the rest of the update. 0605 } 0606 } else { 0607 createLabels(); 0608 } 0609 } 0610 0611 void SDSummaryWidget::mailContact(const QString &url) 0612 { 0613 const Akonadi::Item item = Akonadi::Item::fromUrl(QUrl(url)); 0614 if (!item.isValid()) { 0615 qCDebug(KORGANIZER_KONTACTPLUGINS_SPECIALDATES_LOG) << QStringLiteral("Invalid item found"); 0616 return; 0617 } 0618 0619 auto job = new Akonadi::ItemFetchJob(item, this); 0620 job->fetchScope().fetchFullPayload(); 0621 connect(job, &Akonadi::ItemFetchJob::result, this, &SDSummaryWidget::slotItemFetchJobDone); 0622 } 0623 0624 void SDSummaryWidget::slotItemFetchJobDone(KJob *job) 0625 { 0626 if (job->error()) { 0627 qCWarning(KORGANIZER_KONTACTPLUGINS_SPECIALDATES_LOG) << job->errorString(); 0628 return; 0629 } 0630 const Akonadi::Item::List lst = qobject_cast<Akonadi::ItemFetchJob *>(job)->items(); 0631 if (lst.isEmpty()) { 0632 return; 0633 } 0634 const auto contact = lst.first().payload<KContacts::Addressee>(); 0635 0636 QDesktopServices::openUrl(QUrl(contact.fullEmail())); 0637 } 0638 0639 void SDSummaryWidget::viewContact(const QString &url) 0640 { 0641 const Akonadi::Item item = Akonadi::Item::fromUrl(QUrl(url)); 0642 if (!item.isValid()) { 0643 qCDebug(KORGANIZER_KONTACTPLUGINS_SPECIALDATES_LOG) << "Invalid item found"; 0644 return; 0645 } 0646 0647 QPointer<Akonadi::ContactViewerDialog> dlg = new Akonadi::ContactViewerDialog(this); 0648 dlg->setContact(item); 0649 dlg->exec(); 0650 delete dlg; 0651 } 0652 0653 void SDSummaryWidget::popupMenu(const QString &url) 0654 { 0655 QMenu popup(this); 0656 const QAction *sendMailAction = popup.addAction(QIcon::fromTheme(QStringLiteral("mail-message-new")), i18n("Send &Mail")); 0657 const QAction *viewContactAction = popup.addAction(QIcon::fromTheme(QStringLiteral("view-pim-contacts")), i18n("View &Contact")); 0658 0659 const QAction *ret = popup.exec(QCursor::pos()); 0660 if (ret == sendMailAction) { 0661 mailContact(url); 0662 } else if (ret == viewContactAction) { 0663 viewContact(url); 0664 } 0665 } 0666 0667 bool SDSummaryWidget::eventFilter(QObject *obj, QEvent *e) 0668 { 0669 if (obj->inherits("KUrlLabel")) { 0670 auto label = static_cast<KUrlLabel *>(obj); 0671 if (e->type() == QEvent::Enter) { 0672 Q_EMIT message(i18n("Mail to:\"%1\"", label->text())); 0673 } 0674 if (e->type() == QEvent::Leave) { 0675 Q_EMIT message(QString()); 0676 } 0677 } 0678 0679 return KontactInterface::Summary::eventFilter(obj, e); 0680 } 0681 0682 void SDSummaryWidget::dateDiff(const QDate &date, int &days, int &years) const 0683 { 0684 QDate currentDate; 0685 QDate eventDate; 0686 0687 if (QDate::isLeapYear(date.year()) && date.month() == 2 && date.day() == 29) { 0688 currentDate = QDate(date.year(), QDate::currentDate().month(), QDate::currentDate().day()); 0689 if (!QDate::isLeapYear(QDate::currentDate().year())) { 0690 eventDate = QDate(date.year(), date.month(), 28); // celebrate one day earlier ;) 0691 } else { 0692 eventDate = QDate(date.year(), date.month(), date.day()); 0693 } 0694 } else { 0695 currentDate = QDate(QDate::currentDate().year(), QDate::currentDate().month(), QDate::currentDate().day()); 0696 eventDate = QDate(QDate::currentDate().year(), date.month(), date.day()); 0697 } 0698 0699 int offset = currentDate.daysTo(eventDate); 0700 if (offset < 0) { 0701 days = 365 + offset; 0702 years = QDate::currentDate().year() + 1 - date.year(); 0703 } else { 0704 days = offset; 0705 years = QDate::currentDate().year() - date.year(); 0706 } 0707 } 0708 0709 #include "sdsummarywidget.moc" 0710 0711 #include "moc_sdsummarywidget.cpp"