45 #include <kcalcore/visitor.h>
46 using namespace KCalCore;
48 #include <kpimutils/email.h>
49 #include <kpimutils/linklocator.h>
51 #include <KCalendarSystem>
53 #include <KEMailSettings>
54 #include <KIconLoader>
55 #include <KLocalizedString>
58 #include <KSystemTimeZone>
60 #include <QtCore/QBitArray>
61 #include <QApplication>
63 #include <QTextDocument>
65 using namespace KCalUtils;
66 using namespace IncidenceFormatter;
73 static QString string2HTML(
const QString &str)
77 return KPIMUtils::LinkLocator::convertToHtml(str);
80 static QString htmlAddLink(
const QString &ref,
const QString &text,
83 QString tmpStr(
"<a href=\"" + ref +
"\">" + text +
"</a>");
90 static QString htmlAddMailtoLink(
const QString &email,
const QString &name)
94 if (!email.isEmpty()) {
95 Person person(name, email);
96 QString path = person.fullName().simplified();
97 if (path.isEmpty() || path.startsWith(
'"')) {
101 mailto.setProtocol(
"mailto");
102 mailto.setPath(path);
103 const QString iconPath =
104 KIconLoader::global()->iconPath(
"mail-message-new", KIconLoader::Small);
105 str = htmlAddLink(mailto.url(),
"<img valign=\"top\" src=\"" + iconPath +
"\">");
110 static QString htmlAddUidLink(
const QString &email,
const QString &name,
const QString &uid)
114 if (!uid.isEmpty()) {
116 if (name.isEmpty()) {
118 str += htmlAddLink(
"uid:" + uid, email);
120 str += htmlAddLink(
"uid:" + uid, name);
126 static QString htmlAddTag(
const QString &tag,
const QString &text)
128 int numLineBreaks = text.count(
"\n");
129 QString str =
'<' + tag +
'>';
130 QString tmpText = text;
131 QString tmpStr = str;
132 if (numLineBreaks >= 0) {
133 if (numLineBreaks > 0) {
136 for (
int i = 0; i <= numLineBreaks; ++i) {
137 pos = tmpText.indexOf(
"\n");
138 tmp = tmpText.left(pos);
139 tmpText = tmpText.right(tmpText.length() - pos - 1);
140 tmpStr += tmp +
"<br>";
146 tmpStr +=
"</" + tag +
'>';
150 static QPair<QString, QString> searchNameAndUid(
const QString &email,
const QString &name,
156 QPair<QString, QString>s;
159 if (!email.isEmpty() && (name.isEmpty() || uid.isEmpty())) {
165 static QString searchName(
const QString &email,
const QString &name)
167 const QString printName = name.isEmpty() ? email : name;
176 KEMailSettings settings;
177 QStringList profiles = settings.profiles();
178 for (QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it) {
179 settings.setProfile(*it);
180 if (settings.getSetting(KEMailSettings::EmailAddress) == attendee->email()) {
188 static bool iamOrganizer(Incidence::Ptr incidence)
197 KEMailSettings settings;
198 QStringList profiles = settings.profiles();
199 for (QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it) {
200 settings.setProfile(*it);
201 if (settings.getSetting(KEMailSettings::EmailAddress) == incidence->organizer()->email()) {
209 static bool senderIsOrganizer(Incidence::Ptr incidence,
const QString &sender)
213 if (!incidence || sender.isEmpty()) {
218 QString senderName, senderEmail;
219 if (KPIMUtils::extractEmailAddressAndName(sender, senderEmail, senderName)) {
221 if (incidence->organizer()->email() != senderEmail &&
222 incidence->organizer()->name() != senderName) {
229 static bool attendeeIsOrganizer(
const Incidence::Ptr &incidence,
const Attendee::Ptr &attendee)
231 if (incidence && attendee &&
232 (incidence->organizer()->email() == attendee->email())) {
239 static QString organizerName(
const Incidence::Ptr incidence,
const QString &defName)
242 if (!defName.isEmpty()) {
245 tName = i18n(
"Organizer Unknown");
250 name = incidence->organizer()->name();
251 if (name.isEmpty()) {
252 name = incidence->organizer()->email();
255 if (name.isEmpty()) {
261 static QString firstAttendeeName(
const Incidence::Ptr &incidence,
const QString &defName)
264 if (!defName.isEmpty()) {
267 tName = i18n(
"Sender");
273 if (attendees.count() > 0) {
275 name = attendee->name();
276 if (name.isEmpty()) {
277 name = attendee->email();
281 if (name.isEmpty()) {
292 iconPath = KIconLoader::global()->iconPath(
"dialog-ok-apply", KIconLoader::Small);
295 iconPath = KIconLoader::global()->iconPath(
"dialog-cancel", KIconLoader::Small);
298 iconPath = KIconLoader::global()->iconPath(
"help-about", KIconLoader::Small);
301 iconPath = KIconLoader::global()->iconPath(
"help-about", KIconLoader::Small);
304 iconPath = KIconLoader::global()->iconPath(
"dialog-ok", KIconLoader::Small);
307 iconPath = KIconLoader::global()->iconPath(
"mail-forward", KIconLoader::Small);
310 iconPath = KIconLoader::global()->iconPath(
"mail-mark-read", KIconLoader::Small);
324 static QString displayViewFormatPerson(
const QString &email,
const QString &name,
325 const QString &uid,
const QString &iconPath)
328 QPair<QString, QString> s = searchNameAndUid(email, name, uid);
329 const QString printName = s.first;
330 const QString printUid = s.second;
332 QString personString;
333 if (!iconPath.isEmpty()) {
334 personString +=
"<img valign=\"top\" src=\"" + iconPath +
"\">" +
" ";
338 if (!printUid.isEmpty()) {
339 personString += htmlAddUidLink(email, printName, printUid);
342 personString += (printName.isEmpty() ? email : printName);
345 #ifndef KDEPIM_MOBILE_UI
347 if (!email.isEmpty()) {
348 personString +=
" " + htmlAddMailtoLink(email, printName);
355 static QString displayViewFormatPerson(
const QString &email,
const QString &name,
358 return displayViewFormatPerson(email, name, uid, rsvpStatusIconPath(status));
361 static bool incOrganizerOwnsCalendar(
const Calendar::Ptr &calendar,
362 const Incidence::Ptr &incidence)
368 return iamOrganizer(incidence);
371 static QString displayViewFormatAttendeeRoleList(Incidence::Ptr incidence,
Attendee::Role role,
375 Attendee::List::ConstIterator it;
378 for (it = attendees.constBegin(); it != attendees.constEnd(); ++it) {
380 if (a->role() != role) {
384 if (attendeeIsOrganizer(incidence, a)) {
388 tmpStr += displayViewFormatPerson(a->email(), a->name(), a->uid(),
390 if (!a->delegator().isEmpty()) {
391 tmpStr += i18n(
" (delegated by %1)", a->delegator());
393 if (!a->delegate().isEmpty()) {
394 tmpStr += i18n(
" (delegated to %1)", a->delegate());
398 if (tmpStr.endsWith(QLatin1String(
"<br>"))) {
404 static QString displayViewFormatAttendees(
Calendar::Ptr calendar, Incidence::Ptr incidence)
409 int attendeeCount = incidence->attendees().count();
410 if (attendeeCount > 1 ||
411 (attendeeCount == 1 &&
412 !attendeeIsOrganizer(incidence, incidence->attendees().first()))) {
414 QPair<QString, QString> s = searchNameAndUid(incidence->organizer()->email(),
415 incidence->organizer()->name(),
418 tmpStr +=
"<td><b>" + i18n(
"Organizer:") +
"</b></td>";
419 const QString iconPath =
420 KIconLoader::global()->iconPath(
"meeting-organizer", KIconLoader::Small);
421 tmpStr +=
"<td>" + displayViewFormatPerson(incidence->organizer()->email(),
422 s.first, s.second, iconPath) +
429 bool showStatus = incOrganizerOwnsCalendar(calendar, incidence);
432 str = displayViewFormatAttendeeRoleList(incidence,
Attendee::Chair, showStatus);
433 if (!str.isEmpty()) {
435 tmpStr +=
"<td><b>" + i18n(
"Chair:") +
"</b></td>";
436 tmpStr +=
"<td>" + str +
"</td>";
442 if (!str.isEmpty()) {
444 tmpStr +=
"<td><b>" + i18n(
"Required Participants:") +
"</b></td>";
445 tmpStr +=
"<td>" + str +
"</td>";
451 if (!str.isEmpty()) {
453 tmpStr +=
"<td><b>" + i18n(
"Optional Participants:") +
"</b></td>";
454 tmpStr +=
"<td>" + str +
"</td>";
460 if (!str.isEmpty()) {
462 tmpStr +=
"<td><b>" + i18n(
"Observers:") +
"</b></td>";
463 tmpStr +=
"<td>" + str +
"</td>";
470 static QString displayViewFormatAttachments(Incidence::Ptr incidence)
474 Attachment::List::ConstIterator it;
476 for (it = as.constBegin(); it != as.constEnd(); ++it) {
478 if ((*it)->isUri()) {
480 if ((*it)->uri().startsWith(QLatin1String(
"kmail:"))) {
481 name = i18n(
"Show mail");
483 if ((*it)->label().isEmpty()) {
486 name = (*it)->label();
489 tmpStr += htmlAddLink((*it)->uri(), name);
491 tmpStr += htmlAddLink(QString::fromLatin1(
"ATTACH:%1").
492 arg(QString::fromUtf8((*it)->label().toUtf8().toBase64())),
495 if (count < as.count()) {
502 static QString displayViewFormatCategories(Incidence::Ptr incidence)
505 return incidence->categories().join(
", ");
508 static QString displayViewFormatCreationDate(Incidence::Ptr incidence, KDateTime::Spec spec)
510 KDateTime kdt = incidence->created().toTimeSpec(spec);
511 return i18n(
"Creation date: %1",
dateTimeToString(incidence->created(),
false,
true, spec));
514 static QString displayViewFormatBirthday(
Event::Ptr event)
519 if (event->customProperty(
"KABC",
"BIRTHDAY") !=
"YES" &&
520 event->customProperty(
"KABC",
"ANNIVERSARY") !=
"YES") {
524 QString uid_1 =
event->customProperty(
"KABC",
"UID-1");
525 QString name_1 =
event->customProperty(
"KABC",
"NAME-1");
526 QString email_1=
event->customProperty(
"KABC",
"EMAIL-1");
528 QString tmpStr = displayViewFormatPerson(email_1, name_1, uid_1, QString());
532 static QString displayViewFormatHeader(Incidence::Ptr incidence)
534 QString tmpStr =
"<table><tr>";
537 KIconLoader *iconLoader = KIconLoader::global();
541 if (incidence->customProperty(
"KABC",
"BIRTHDAY") ==
"YES") {
542 iconPath = iconLoader->iconPath(
"view-calendar-birthday", KIconLoader::Small);
543 }
else if (incidence->customProperty(
"KABC",
"ANNIVERSARY") ==
"YES") {
544 iconPath = iconLoader->iconPath(
"view-calendar-wedding-anniversary", KIconLoader::Small);
546 iconPath = iconLoader->iconPath(incidence->iconName(), KIconLoader::Small);
548 tmpStr +=
"<img valign=\"top\" src=\"" + iconPath +
"\">";
550 if (incidence->hasEnabledAlarms()) {
551 tmpStr +=
"<img valign=\"top\" src=\"" +
552 iconLoader->iconPath(
"preferences-desktop-notification-bell", KIconLoader::Small) +
555 if (incidence->recurs()) {
556 tmpStr +=
"<img valign=\"top\" src=\"" +
557 iconLoader->iconPath(
"edit-redo", KIconLoader::Small) +
560 if (incidence->isReadOnly()) {
561 tmpStr +=
"<img valign=\"top\" src=\"" +
562 iconLoader->iconPath(
"object-locked", KIconLoader::Small) +
568 tmpStr +=
"<b><u>" + incidence->richSummary() +
"</u></b>";
571 tmpStr +=
"</tr></table>";
576 static QString displayViewFormatEvent(
const Calendar::Ptr calendar,
const QString &sourceName,
578 const QDate &date, KDateTime::Spec spec)
584 QString tmpStr = displayViewFormatHeader(event);
587 tmpStr +=
"<col width=\"25%\"/>";
588 tmpStr +=
"<col width=\"75%\"/>";
590 const QString calStr = calendar ?
resourceString(calendar, event) : sourceName;
591 if (!calStr.isEmpty()) {
593 tmpStr +=
"<td><b>" + i18n(
"Calendar:") +
"</b></td>";
594 tmpStr +=
"<td>" + calStr +
"</td>";
598 if (!event->location().isEmpty()) {
600 tmpStr +=
"<td><b>" + i18n(
"Location:") +
"</b></td>";
601 tmpStr +=
"<td>" +
event->richLocation() +
"</td>";
605 KDateTime startDt =
event->dtStart();
606 KDateTime endDt =
event->dtEnd();
607 if (event->recurs()) {
608 if (date.isValid()) {
609 KDateTime kdt(date, QTime(0, 0, 0), KSystemTimeZones::local());
610 int diffDays = startDt.daysTo(kdt);
611 kdt = kdt.addSecs(-1);
612 startDt.setDate(event->recurrence()->getNextDateTime(kdt).date());
613 if (event->hasEndDate()) {
614 endDt = endDt.addDays(diffDays);
615 if (startDt > endDt) {
616 startDt.setDate(event->recurrence()->getPreviousDateTime(kdt).date());
617 endDt = startDt.addDays(event->dtStart().daysTo(event->dtEnd()));
624 if (event->allDay()) {
625 if (event->isMultiDay()) {
626 tmpStr +=
"<td><b>" + i18n(
"Date:") +
"</b></td>";
628 i18nc(
"<beginTime> - <endTime>",
"%1 - %2",
633 tmpStr +=
"<td><b>" + i18n(
"Date:") +
"</b></td>";
635 i18nc(
"date as string",
"%1",
640 if (event->isMultiDay()) {
641 tmpStr +=
"<td><b>" + i18n(
"Date:") +
"</b></td>";
643 i18nc(
"<beginTime> - <endTime>",
"%1 - %2",
648 tmpStr +=
"<td><b>" + i18n(
"Date:") +
"</b></td>";
650 i18nc(
"date as string",
"%1",
654 tmpStr +=
"</tr><tr>";
655 tmpStr +=
"<td><b>" + i18n(
"Time:") +
"</b></td>";
656 if (event->hasEndDate() && startDt != endDt) {
658 i18nc(
"<beginTime> - <endTime>",
"%1 - %2",
672 if (!durStr.isEmpty()) {
674 tmpStr +=
"<td><b>" + i18n(
"Duration:") +
"</b></td>";
675 tmpStr +=
"<td>" + durStr +
"</td>";
679 if (event->recurs() ||
event->hasRecurrenceId()) {
681 tmpStr +=
"<td><b>" + i18n(
"Recurrence:") +
"</b></td>";
684 if (event->hasRecurrenceId()) {
685 str = i18n(
"Exception");
690 tmpStr +=
"<td>" + str +
695 const bool isBirthday =
event->customProperty(
"KABC",
"BIRTHDAY") ==
"YES";
696 const bool isAnniversary =
event->customProperty(
"KABC",
"ANNIVERSARY") ==
"YES";
698 if (isBirthday || isAnniversary) {
701 tmpStr +=
"<td><b>" + i18n(
"Anniversary:") +
"</b></td>";
703 tmpStr +=
"<td><b>" + i18n(
"Birthday:") +
"</b></td>";
705 tmpStr +=
"<td>" + displayViewFormatBirthday(event) +
"</td>";
707 tmpStr +=
"</table>";
711 if (!event->description().isEmpty()) {
713 if (!event->descriptionIsRich() &&
714 !
event->description().startsWith(QLatin1String(
"<!DOCTYPE HTML")))
716 descStr = string2HTML(event->description());
718 if (!event->description().startsWith(QLatin1String(
"<!DOCTYPE HTML"))) {
719 descStr =
event->richDescription();
721 descStr =
event->description();
725 tmpStr +=
"<td><b>" + i18n(
"Description:") +
"</b></td>";
726 tmpStr +=
"<td>" + descStr +
"</td>";
732 int reminderCount =
event->alarms().count();
733 if (reminderCount > 0 && event->hasEnabledAlarms()) {
735 tmpStr +=
"<td><b>" +
736 i18np(
"Reminder:",
"Reminders:", reminderCount) +
742 tmpStr += displayViewFormatAttendees(calendar, event);
744 int categoryCount =
event->categories().count();
745 if (categoryCount > 0) {
748 tmpStr += i18np(
"Category:",
"Categories:", categoryCount) +
750 tmpStr +=
"<td>" + displayViewFormatCategories(event) +
"</td>";
754 int attachmentCount =
event->attachments().count();
755 if (attachmentCount > 0) {
757 tmpStr +=
"<td><b>" +
758 i18np(
"Attachment:",
"Attachments:", attachmentCount) +
760 tmpStr +=
"<td>" + displayViewFormatAttachments(event) +
"</td>";
763 tmpStr +=
"</table>";
765 tmpStr +=
"<p><em>" + displayViewFormatCreationDate(event, spec) +
"</em>";
770 static QString displayViewFormatTodo(
const Calendar::Ptr &calendar,
const QString &sourceName,
772 const QDate &date, KDateTime::Spec spec)
775 kDebug() <<
"IncidenceFormatter::displayViewFormatTodo was called without to-do, quitting";
779 QString tmpStr = displayViewFormatHeader(todo);
782 tmpStr +=
"<col width=\"25%\"/>";
783 tmpStr +=
"<col width=\"75%\"/>";
785 const QString calStr = calendar ?
resourceString(calendar, todo) : sourceName;
786 if (!calStr.isEmpty()) {
788 tmpStr +=
"<td><b>" + i18n(
"Calendar:") +
"</b></td>";
789 tmpStr +=
"<td>" + calStr +
"</td>";
793 if (!todo->location().isEmpty()) {
795 tmpStr +=
"<td><b>" + i18n(
"Location:") +
"</b></td>";
796 tmpStr +=
"<td>" + todo->richLocation() +
"</td>";
800 const bool hastStartDate = todo->hasStartDate() && todo->dtStart().isValid();
801 const bool hasDueDate = todo->hasDueDate() && todo->dtDue().isValid();
804 KDateTime startDt = todo->dtStart(
true );
805 if (todo->recurs()) {
806 if (date.isValid()) {
809 const int length = startDt.daysTo(todo->dtDue(
true ));
811 startDt.setDate(date.addDays(-length));
813 kError() <<
"DTSTART is bigger than DTDUE, todo->uid() is " << todo->uid();
814 startDt.setDate(date);
817 kError() <<
"To-do is recurring but has no DTDUE set, todo->uid() is " << todo->uid();
818 startDt.setDate(date);
823 tmpStr +=
"<td><b>" +
824 i18nc(
"to-do start date/time",
"Start:") +
833 KDateTime dueDt = todo->dtDue();
834 if (todo->recurs()) {
835 if (date.isValid()) {
836 KDateTime kdt(date, QTime(0, 0, 0), KSystemTimeZones::local());
837 kdt = kdt.addSecs(-1);
838 dueDt.setDate(todo->recurrence()->getNextDateTime(kdt).date());
842 tmpStr +=
"<td><b>" +
843 i18nc(
"to-do due date/time",
"Due:") +
852 if (!durStr.isEmpty()) {
854 tmpStr +=
"<td><b>" + i18n(
"Duration:") +
"</b></td>";
855 tmpStr +=
"<td>" + durStr +
"</td>";
859 if (todo->recurs() || todo->hasRecurrenceId()) {
861 tmpStr +=
"<td><b>" + i18n(
"Recurrence:") +
"</b></td>";
863 if (todo->hasRecurrenceId()) {
864 str = i18n(
"Exception");
874 if (!todo->description().isEmpty()) {
876 tmpStr +=
"<td><b>" + i18n(
"Description:") +
"</b></td>";
877 tmpStr +=
"<td>" + todo->richDescription() +
"</td>";
883 int reminderCount = todo->alarms().count();
884 if (reminderCount > 0 && todo->hasEnabledAlarms()) {
886 tmpStr +=
"<td><b>" +
887 i18np(
"Reminder:",
"Reminders:", reminderCount) +
893 tmpStr += displayViewFormatAttendees(calendar, todo);
895 int categoryCount = todo->categories().count();
896 if (categoryCount > 0) {
898 tmpStr +=
"<td><b>" +
899 i18np(
"Category:",
"Categories:", categoryCount) +
901 tmpStr +=
"<td>" + displayViewFormatCategories(todo) +
"</td>";
905 if (todo->priority() > 0) {
907 tmpStr +=
"<td><b>" + i18n(
"Priority:") +
"</b></td>";
909 tmpStr += QString::number(todo->priority());
915 if (todo->isCompleted()) {
916 tmpStr +=
"<td><b>" + i18nc(
"Completed: date",
"Completed:") +
"</b></td>";
920 tmpStr +=
"<td><b>" + i18n(
"Percent Done:") +
"</b></td>";
922 tmpStr += i18n(
"%1%", todo->percentComplete());
927 int attachmentCount = todo->attachments().count();
928 if (attachmentCount > 0) {
930 tmpStr +=
"<td><b>" +
931 i18np(
"Attachment:",
"Attachments:", attachmentCount) +
933 tmpStr +=
"<td>" + displayViewFormatAttachments(todo) +
"</td>";
936 tmpStr +=
"</table>";
938 tmpStr +=
"<p><em>" + displayViewFormatCreationDate(todo, spec) +
"</em>";
943 static QString displayViewFormatJournal(
const Calendar::Ptr &calendar,
const QString &sourceName,
950 QString tmpStr = displayViewFormatHeader(journal);
953 tmpStr +=
"<col width=\"25%\"/>";
954 tmpStr +=
"<col width=\"75%\"/>";
956 const QString calStr = calendar ?
resourceString(calendar, journal) : sourceName;
957 if (!calStr.isEmpty()) {
959 tmpStr +=
"<td><b>" + i18n(
"Calendar:") +
"</b></td>";
960 tmpStr +=
"<td>" + calStr +
"</td>";
965 tmpStr +=
"<td><b>" + i18n(
"Date:") +
"</b></td>";
971 if (!journal->description().isEmpty()) {
973 tmpStr +=
"<td><b>" + i18n(
"Description:") +
"</b></td>";
974 tmpStr +=
"<td>" + journal->richDescription() +
"</td>";
978 int categoryCount = journal->categories().count();
979 if (categoryCount > 0) {
981 tmpStr +=
"<td><b>" +
982 i18np(
"Category:",
"Categories:", categoryCount) +
984 tmpStr +=
"<td>" + displayViewFormatCategories(journal) +
"</td>";
988 tmpStr +=
"</table>";
990 tmpStr +=
"<p><em>" + displayViewFormatCreationDate(journal, spec) +
"</em>";
995 static QString displayViewFormatFreeBusy(
const Calendar::Ptr &calendar,
const QString &sourceName,
999 Q_UNUSED(sourceName);
1006 "h2", i18n(
"Free/Busy information for %1", fb->organizer()->fullName())));
1008 tmpStr += htmlAddTag(
"h4",
1009 i18n(
"Busy times in date range %1 - %2:",
1015 htmlAddTag(
"b", i18nc(
"tag for busy periods list",
"Busy:")));
1017 Period::List periods = fb->busyPeriods();
1018 Period::List::iterator it;
1019 for (it = periods.begin(); it != periods.end(); ++it) {
1021 if (per.hasDuration()) {
1022 int dur = per.duration().asSeconds();
1025 cont += i18ncp(
"hours part of duration",
"1 hour ",
"%1 hours ", dur / 3600);
1029 cont += i18ncp(
"minutes part duration",
"1 minute ",
"%1 minutes ", dur / 60);
1033 cont += i18ncp(
"seconds part of duration",
"1 second",
"%1 seconds", dur);
1035 text += i18nc(
"startDate for duration",
"%1 for %2",
1040 if (per.start().date() == per.end().date()) {
1041 text += i18nc(
"date, fromTime - toTime ",
"%1, %2 - %3",
1046 text += i18nc(
"fromDateTime - toDateTime",
"%1 - %2",
1053 tmpStr += htmlAddTag(
"p", text);
1059 class KCalUtils::IncidenceFormatter::EventViewerVisitor :
public Visitor
1062 EventViewerVisitor()
1063 : mCalendar(0), mSpec(KDateTime::Spec()), mResult(
"") {}
1065 bool act(
const Calendar::Ptr &calendar, IncidenceBase::Ptr incidence,
const QDate &date,
1066 KDateTime::Spec spec=KDateTime::Spec())
1068 mCalendar = calendar;
1069 mSourceName.clear();
1073 return incidence->accept(*
this, incidence);
1076 bool act(
const QString &sourceName, IncidenceBase::Ptr incidence,
const QDate &date,
1077 KDateTime::Spec spec=KDateTime::Spec())
1079 mSourceName = sourceName;
1083 return incidence->accept(*
this, incidence);
1086 QString result()
const {
1093 mResult = displayViewFormatEvent(mCalendar, mSourceName, event, mDate, mSpec);
1094 return !mResult.isEmpty();
1098 mResult = displayViewFormatTodo(mCalendar, mSourceName, todo, mDate, mSpec);
1099 return !mResult.isEmpty();
1103 mResult = displayViewFormatJournal(mCalendar, mSourceName, journal, mSpec);
1104 return !mResult.isEmpty();
1108 mResult = displayViewFormatFreeBusy(mCalendar, mSourceName, fb, mSpec);
1109 return !mResult.isEmpty();
1114 QString mSourceName;
1116 KDateTime::Spec mSpec;
1122 const IncidenceBase::Ptr &incidence,
1124 KDateTime::Spec spec)
1130 EventViewerVisitor v;
1131 if (v.act(calendar, incidence, date, spec)) {
1139 const IncidenceBase::Ptr &incidence,
1141 KDateTime::Spec spec)
1147 EventViewerVisitor v;
1148 if (v.act(sourceName, incidence, date, spec)) {
1159 static QString cleanHtml(
const QString &html)
1161 QRegExp rx(
"<body[^>]*>(.*)</body>", Qt::CaseInsensitive);
1163 QString body = rx.cap(1);
1165 return Qt::escape(body.remove(QRegExp(
"<[^>]*>")).trimmed());
1168 static QString invitationSummary(
const Incidence::Ptr &incidence,
bool noHtmlMode)
1170 QString summaryStr = i18n(
"Summary unspecified");
1171 if (!incidence->summary().isEmpty()) {
1172 if (!incidence->summaryIsRich()) {
1173 summaryStr = Qt::escape(incidence->summary());
1175 summaryStr = incidence->richSummary();
1177 summaryStr = cleanHtml(summaryStr);
1184 static QString invitationLocation(
const Incidence::Ptr &incidence,
bool noHtmlMode)
1186 QString locationStr = i18n(
"Location unspecified");
1187 if (!incidence->location().isEmpty()) {
1188 if (!incidence->locationIsRich()) {
1189 locationStr = Qt::escape(incidence->location());
1191 locationStr = incidence->richLocation();
1193 locationStr = cleanHtml(locationStr);
1200 static QString eventStartTimeStr(
const Event::Ptr &event)
1203 if (!event->allDay()) {
1204 tmp = i18nc(
"%1: Start Date, %2: Start Time",
"%1 %2",
1205 dateToString(event->dtStart(),
true, KSystemTimeZones::local()),
1206 timeToString(event->dtStart(),
true, KSystemTimeZones::local()));
1208 tmp = i18nc(
"%1: Start Date",
"%1 (all day)",
1209 dateToString(event->dtStart(),
true, KSystemTimeZones::local()));
1214 static QString eventEndTimeStr(
const Event::Ptr &event)
1217 if (event->hasEndDate() &&
event->dtEnd().isValid()) {
1218 if (!event->allDay()) {
1219 tmp = i18nc(
"%1: End Date, %2: End Time",
"%1 %2",
1220 dateToString(event->dtEnd(),
true, KSystemTimeZones::local()),
1221 timeToString(event->dtEnd(),
true, KSystemTimeZones::local()));
1223 tmp = i18nc(
"%1: End Date",
"%1 (all day)",
1224 dateToString(event->dtEnd(),
true, KSystemTimeZones::local()));
1230 static QString htmlInvitationDetailsBegin()
1232 QString dir = (QApplication::isRightToLeft() ?
"rtl" :
"ltr");
1233 return QString(
"<div dir=\"%1\">\n").arg(dir);
1236 static QString htmlInvitationDetailsEnd()
1241 static QString htmlInvitationDetailsTableBegin()
1244 return "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
1248 static QString htmlInvitationDetailsTableEnd()
1250 return "</table>\n";
1253 static QString diffColor()
1258 return QColor(Qt::red).name();
1261 static QString noteColor()
1264 return qApp->palette().color(QPalette::Active, QPalette::Highlight).name();
1267 static QString htmlRow(
const QString &title,
const QString &value)
1269 if (!value.isEmpty()) {
1270 return "<tr><td>" + title +
"</td><td>" + value +
"</td></tr>\n";
1276 static QString htmlRow(
const QString &title,
const QString &value,
const QString &oldvalue)
1279 if (value.isEmpty()) {
1284 if (oldvalue.isEmpty() || value == oldvalue) {
1285 return htmlRow(title, value);
1289 QString color = diffColor();
1290 QString newtitle =
"<font color=\"" + color +
"\">" + title +
"</font>";
1291 QString newvalue =
"<font color=\"" + color +
"\">" + value +
"</font>" +
1293 "(<strike>" + oldvalue +
"</strike>)";
1294 return htmlRow(newtitle, newvalue);
1298 static Attendee::Ptr findDelegatedFromMyAttendee(
const Incidence::Ptr &incidence)
1307 KEMailSettings settings;
1308 QStringList profiles = settings.profiles();
1309 for (QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it) {
1310 settings.setProfile(*it);
1312 QString delegatorName, delegatorEmail;
1314 Attendee::List::ConstIterator it2;
1315 for (it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2) {
1317 KPIMUtils::extractEmailAddressAndName(a->delegator(), delegatorEmail, delegatorName);
1318 if (settings.getSetting(KEMailSettings::EmailAddress) == delegatorEmail) {
1327 static Attendee::Ptr findMyAttendee(
const Incidence::Ptr &incidence)
1336 KEMailSettings settings;
1337 QStringList profiles = settings.profiles();
1338 for (QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it) {
1339 settings.setProfile(*it);
1342 Attendee::List::ConstIterator it2;
1343 for (it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2) {
1345 if (settings.getSetting(KEMailSettings::EmailAddress) == a->email()) {
1354 static Attendee::Ptr findAttendee(
const Incidence::Ptr &incidence,
1355 const QString &email)
1365 Attendee::List::ConstIterator it;
1366 for (it = attendees.constBegin(); it != attendees.constEnd(); ++it) {
1368 if (email == a->email()) {
1376 static bool rsvpRequested(
const Incidence::Ptr &incidence)
1386 Attendee::List::ConstIterator it;
1387 for (it = attendees.constBegin(); it != attendees.constEnd(); ++it) {
1388 if (it == attendees.constBegin()) {
1389 rsvp = (*it)->RSVP();
1391 if ((*it)->RSVP() != rsvp) {
1400 static QString rsvpRequestedStr(
bool rsvpRequested,
const QString &role)
1402 if (rsvpRequested) {
1403 if (role.isEmpty()) {
1404 return i18n(
"Your response is requested");
1406 return i18n(
"Your response as <b>%1</b> is requested", role);
1409 if (role.isEmpty()) {
1410 return i18n(
"No response is necessary");
1412 return i18n(
"No response as <b>%1</b> is necessary", role);
1417 static QString myStatusStr(Incidence::Ptr incidence)
1423 ret = i18n(
"(<b>Note</b>: the Organizer preset your response to <b>%1</b>)",
1424 Stringify::attendeeStatus(a->status()));
1429 static QString invitationNote(
const QString &title,
const QString ¬e,
1430 const QString &tag,
const QString &color)
1433 if (!note.isEmpty()) {
1434 noteStr +=
"<table border=\"0\" style=\"margin-top:4px;\">";
1435 noteStr +=
"<tr><center><td>";
1436 if (!color.isEmpty()) {
1437 noteStr +=
"<font color=\"" + color +
"\">";
1439 if (!title.isEmpty()) {
1440 if (!tag.isEmpty()) {
1441 noteStr += htmlAddTag(tag, title);
1446 noteStr +=
" " + note;
1447 if (!color.isEmpty()) {
1448 noteStr +=
"</font>";
1450 noteStr +=
"</td></center></tr>";
1451 noteStr +=
"</table>";
1456 static QString invitationPerson(
const QString &email,
const QString &name,
const QString &uid,
1457 const QString &comment)
1459 QPair<QString, QString> s = searchNameAndUid(email, name, uid);
1460 const QString printName = s.first;
1461 const QString printUid = s.second;
1463 QString personString;
1465 if (!printUid.isEmpty()) {
1466 personString = htmlAddUidLink(email, printName, printUid);
1469 personString = (printName.isEmpty() ? email : printName);
1471 if (!comment.isEmpty()) {
1472 personString = i18nc(
"name (comment)",
"%1 (%2)", personString, comment);
1474 personString +=
'\n';
1477 if (!email.isEmpty()) {
1478 personString +=
" " + htmlAddMailtoLink(email, printName);
1480 personString +=
'\n';
1482 return personString;
1485 static QString invitationDetailsIncidence(
const Incidence::Ptr &incidence,
bool noHtmlMode)
1493 QStringList comments;
1495 if (incidence->comments().isEmpty()) {
1496 if (!incidence->description().isEmpty()) {
1498 if (!incidence->descriptionIsRich() &&
1499 !incidence->description().startsWith(QLatin1String(
"<!DOCTYPE HTML"))) {
1500 comments << string2HTML(incidence->description());
1502 if (!incidence->description().startsWith(QLatin1String(
"<!DOCTYPE HTML"))) {
1503 comments << incidence->richDescription();
1505 comments << incidence->description();
1508 comments[0] = cleanHtml(comments[0]);
1510 comments[0] = htmlAddTag(
"p", comments[0]);
1516 foreach(
const QString &c, incidence->comments()) {
1519 if (!Qt::mightBeRichText(c)) {
1520 comments << string2HTML(c);
1523 comments << cleanHtml(cleanHtml(
"<body>" + c +
"</body>"));
1530 if (!incidence->description().isEmpty()) {
1532 if (!incidence->descriptionIsRich() &&
1533 !incidence->description().startsWith(QLatin1String(
"<!DOCTYPE HTML"))) {
1534 descr = string2HTML(incidence->description());
1536 if (!incidence->description().startsWith(QLatin1String(
"<!DOCTYPE HTML"))) {
1537 descr = incidence->richDescription();
1539 descr = incidence->description();
1542 descr = cleanHtml(descr);
1544 descr = htmlAddTag(
"p", descr);
1549 if (!descr.isEmpty()) {
1551 html +=
"<table border=\"0\" style=\"margin-top:4px;\">";
1552 html +=
"<tr><td><center>" +
1553 htmlAddTag(
"u", i18n(
"Description:")) +
1554 "</center></td></tr>";
1555 html +=
"<tr><td>" + descr +
"</td></tr>";
1559 if (!comments.isEmpty()) {
1561 html +=
"<table border=\"0\" style=\"margin-top:4px;\">";
1562 html +=
"<tr><td><center>" +
1563 htmlAddTag(
"u", i18n(
"Comments:")) +
1564 "</center></td></tr>";
1566 if (comments.count() > 1) {
1568 for (
int i=0; i < comments.count(); ++i) {
1569 html +=
"<li>" + comments[i] +
"</li>";
1573 html += comments[0];
1575 html +=
"</td></tr>";
1581 static QString invitationDetailsEvent(
const Event::Ptr &event,
bool noHtmlMode,
1582 KDateTime::Spec spec)
1589 QString html = htmlInvitationDetailsBegin();
1590 html += htmlInvitationDetailsTableBegin();
1593 html += htmlRow(i18n(
"What:"), invitationSummary(event, noHtmlMode));
1594 html += htmlRow(i18n(
"Where:"), invitationLocation(event, noHtmlMode));
1597 if (event->dtStart().date() ==
event->dtEnd().date()) {
1598 html += htmlRow(i18n(
"Date:"),
dateToString(event->dtStart(),
false, spec));
1599 if (!event->allDay()) {
1600 html += htmlRow(i18n(
"Time:"),
1606 html += htmlRow(i18nc(
"starting date",
"From:"),
1608 if (!event->allDay()) {
1609 html += htmlRow(i18nc(
"starting time",
"At:"),
1612 if (event->hasEndDate()) {
1613 html += htmlRow(i18nc(
"ending date",
"To:"),
1615 if (!event->allDay()) {
1616 html += htmlRow(i18nc(
"ending time",
"At:"),
1620 html += htmlRow(i18nc(
"ending date",
"To:"), i18n(
"no end date specified"));
1628 if (event->recurs()) {
1632 html += htmlInvitationDetailsTableEnd();
1633 html += invitationDetailsIncidence(event, noHtmlMode);
1634 html += htmlInvitationDetailsEnd();
1641 KDateTime::Spec spec)
1644 return invitationDetailsEvent(event, noHtmlMode, spec);
1652 html += invitationNote(QString(),
1653 i18n(
"Please respond again to the original proposal."),
1654 QString(), noteColor());
1657 html += htmlInvitationDetailsBegin();
1658 html += htmlInvitationDetailsTableBegin();
1660 html += htmlRow(i18n(
"What:"),
1661 invitationSummary(event, noHtmlMode),
1662 invitationSummary(oldevent, noHtmlMode));
1664 html += htmlRow(i18n(
"Where:"),
1665 invitationLocation(event, noHtmlMode),
1666 invitationLocation(oldevent, noHtmlMode));
1669 if (event->dtStart().date() ==
event->dtEnd().date()) {
1670 html += htmlRow(i18n(
"Date:"),
1673 QString spanStr, oldspanStr;
1674 if (!event->allDay()) {
1679 if (!oldevent->allDay()) {
1680 oldspanStr =
timeToString(oldevent->dtStart(),
true, spec) +
1684 html += htmlRow(i18n(
"Time:"), spanStr, oldspanStr);
1686 html += htmlRow(i18nc(
"Starting date of an event",
"From:"),
1689 QString startStr, oldstartStr;
1690 if (!event->allDay()) {
1693 if (!oldevent->allDay()) {
1694 oldstartStr =
timeToString(oldevent->dtStart(),
true, spec);
1696 html += htmlRow(i18nc(
"Starting time of an event",
"At:"), startStr, oldstartStr);
1697 if (event->hasEndDate()) {
1698 html += htmlRow(i18nc(
"Ending date of an event",
"To:"),
1701 QString endStr, oldendStr;
1702 if (!event->allDay()) {
1705 if (!oldevent->allDay()) {
1706 oldendStr =
timeToString(oldevent->dtEnd(),
true, spec);
1708 html += htmlRow(i18nc(
"Starting time of an event",
"At:"), endStr, oldendStr);
1710 QString endStr = i18n(
"no end date specified");
1712 if (!oldevent->hasEndDate()) {
1713 oldendStr = i18n(
"no end date specified");
1715 oldendStr =
dateTimeToString(oldevent->dtEnd(), oldevent->allDay(),
false);
1717 html += htmlRow(i18nc(
"Ending date of an event",
"To:"), endStr, oldendStr);
1723 QString recurStr, oldrecurStr;
1724 if (event->recurs() || oldevent->recurs()) {
1728 html += htmlRow(i18n(
"Recurrence:"), recurStr, oldrecurStr);
1730 html += htmlInvitationDetailsTableEnd();
1731 html += invitationDetailsIncidence(event, noHtmlMode);
1732 html += htmlInvitationDetailsEnd();
1737 static QString invitationDetailsTodo(
const Todo::Ptr &todo,
bool noHtmlMode,
1738 KDateTime::Spec spec)
1745 QString html = htmlInvitationDetailsBegin();
1746 html += htmlInvitationDetailsTableBegin();
1749 html += htmlRow(i18n(
"What:"), invitationSummary(todo, noHtmlMode));
1750 html += htmlRow(i18n(
"Where:"), invitationLocation(todo, noHtmlMode));
1752 if (todo->hasStartDate() && todo->dtStart().isValid()) {
1753 html += htmlRow(i18n(
"Start Date:"),
dateToString(todo->dtStart(),
false, spec));
1754 if (!todo->allDay()) {
1755 html += htmlRow(i18n(
"Start Time:"),
timeToString(todo->dtStart(),
false, spec));
1758 if (todo->hasDueDate() && todo->dtDue().isValid()) {
1759 html += htmlRow(i18n(
"Due Date:"),
dateToString(todo->dtDue(),
false, spec));
1760 if (!todo->allDay()) {
1761 html += htmlRow(i18n(
"Due Time:"),
timeToString(todo->dtDue(),
false, spec));
1764 html += htmlRow(i18n(
"Due Date:"), i18nc(
"Due Date: None",
"None"));
1771 if (todo->percentComplete() > 0) {
1772 html += htmlRow(i18n(
"Percent Done:"), i18n(
"%1%", todo->percentComplete()));
1776 if (todo->recurs()) {
1780 html += htmlInvitationDetailsTableEnd();
1781 html += invitationDetailsIncidence(todo, noHtmlMode);
1782 html += htmlInvitationDetailsEnd();
1787 static QString invitationDetailsTodo(
const Todo::Ptr &todo,
const Todo::Ptr &oldtodo,
1789 KDateTime::Spec spec)
1792 return invitationDetailsTodo(todo, noHtmlMode, spec);
1800 html += invitationNote(QString(),
1801 i18n(
"Please respond again to the original proposal."),
1802 QString(), noteColor());
1805 html += htmlInvitationDetailsBegin();
1806 html += htmlInvitationDetailsTableBegin();
1808 html += htmlRow(i18n(
"What:"),
1809 invitationSummary(todo, noHtmlMode),
1810 invitationSummary(todo, noHtmlMode));
1812 html += htmlRow(i18n(
"Where:"),
1813 invitationLocation(todo, noHtmlMode),
1814 invitationLocation(oldtodo, noHtmlMode));
1816 if (todo->hasStartDate() && todo->dtStart().isValid()) {
1817 html += htmlRow(i18n(
"Start Date:"),
1820 QString startTimeStr, oldstartTimeStr;
1821 if (!todo->allDay() || !oldtodo->allDay()) {
1822 startTimeStr = todo->allDay() ?
1823 i18n(
"All day") :
timeToString(todo->dtStart(), false, spec);
1824 oldstartTimeStr = oldtodo->allDay() ?
1825 i18n(
"All day") :
timeToString(oldtodo->dtStart(), false, spec);
1827 html += htmlRow(i18n(
"Start Time:"), startTimeStr, oldstartTimeStr);
1829 if (todo->hasDueDate() && todo->dtDue().isValid()) {
1830 html += htmlRow(i18n(
"Due Date:"),
1833 QString endTimeStr, oldendTimeStr;
1834 if (!todo->allDay() || !oldtodo->allDay()) {
1835 endTimeStr = todo->allDay() ?
1836 i18n(
"All day") :
timeToString(todo->dtDue(), false, spec);
1837 oldendTimeStr = oldtodo->allDay() ?
1838 i18n(
"All day") :
timeToString(oldtodo->dtDue(), false, spec);
1840 html += htmlRow(i18n(
"Due Time:"), endTimeStr, oldendTimeStr);
1842 QString dueStr = i18nc(
"Due Date: None",
"None");
1844 if (!oldtodo->hasDueDate() || !oldtodo->dtDue().isValid()) {
1845 olddueStr = i18nc(
"Due Date: None",
"None");
1849 html += htmlRow(i18n(
"Due Date:"), dueStr, olddueStr);
1854 QString completionStr, oldcompletionStr;
1855 if (todo->percentComplete() > 0 || oldtodo->percentComplete() > 0) {
1856 completionStr = i18n(
"%1%", todo->percentComplete());
1857 oldcompletionStr = i18n(
"%1%", oldtodo->percentComplete());
1859 html += htmlRow(i18n(
"Percent Done:"), completionStr, oldcompletionStr);
1861 QString recurStr, oldrecurStr;
1862 if (todo->recurs() || oldtodo->recurs()) {
1866 html += htmlRow(i18n(
"Recurrence:"), recurStr, oldrecurStr);
1868 html += htmlInvitationDetailsTableEnd();
1869 html += invitationDetailsIncidence(todo, noHtmlMode);
1871 html += htmlInvitationDetailsEnd();
1876 static QString invitationDetailsJournal(
const Journal::Ptr &journal,
bool noHtmlMode,
1877 KDateTime::Spec spec)
1883 QString html = htmlInvitationDetailsBegin();
1884 html += htmlInvitationDetailsTableBegin();
1886 html += htmlRow(i18n(
"Summary:"), invitationSummary(journal, noHtmlMode));
1887 html += htmlRow(i18n(
"Date:"),
dateToString(journal->dtStart(),
false, spec));
1889 html += htmlInvitationDetailsTableEnd();
1890 html += invitationDetailsIncidence(journal, noHtmlMode);
1891 html += htmlInvitationDetailsEnd();
1896 static QString invitationDetailsJournal(
const Journal::Ptr &journal,
1898 bool noHtmlMode, KDateTime::Spec spec)
1901 return invitationDetailsJournal(journal, noHtmlMode, spec);
1904 QString html = htmlInvitationDetailsBegin();
1905 html += htmlInvitationDetailsTableBegin();
1907 html += htmlRow(i18n(
"What:"),
1908 invitationSummary(journal, noHtmlMode),
1909 invitationSummary(oldjournal, noHtmlMode));
1911 html += htmlRow(i18n(
"Date:"),
1915 html += htmlInvitationDetailsTableEnd();
1916 html += invitationDetailsIncidence(journal, noHtmlMode);
1917 html += htmlInvitationDetailsEnd();
1922 static QString invitationDetailsFreeBusy(
const FreeBusy::Ptr &fb,
bool noHtmlMode,
1923 KDateTime::Spec spec)
1925 Q_UNUSED(noHtmlMode);
1931 QString html = htmlInvitationDetailsTableBegin();
1933 html += htmlRow(i18n(
"Person:"), fb->organizer()->fullName());
1934 html += htmlRow(i18n(
"Start date:"),
dateToString(fb->dtStart(),
true, spec));
1935 html += htmlRow(i18n(
"End date:"),
dateToString(fb->dtEnd(),
true, spec));
1937 html +=
"<tr><td colspan=2><hr></td></tr>\n";
1938 html +=
"<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
1940 Period::List periods = fb->busyPeriods();
1941 Period::List::iterator it;
1942 for (it = periods.begin(); it != periods.end(); ++it) {
1944 if (per.hasDuration()) {
1945 int dur = per.duration().asSeconds();
1948 cont += i18ncp(
"hours part of duration",
"1 hour ",
"%1 hours ", dur / 3600);
1952 cont += i18ncp(
"minutes part of duration",
"1 minute",
"%1 minutes ", dur / 60);
1956 cont += i18ncp(
"seconds part of duration",
"1 second",
"%1 seconds", dur);
1958 html += htmlRow(QString(),
1959 i18nc(
"startDate for duration",
"%1 for %2",
1961 per.start().dateTime(), KLocale::LongDate),
1965 if (per.start().date() == per.end().date()) {
1966 cont = i18nc(
"date, fromTime - toTime ",
"%1, %2 - %3",
1967 KGlobal::locale()->
formatDate(per.start().date()),
1968 KGlobal::locale()->formatTime(per.start().time()),
1969 KGlobal::locale()->formatTime(per.end().time()));
1971 cont = i18nc(
"fromDateTime - toDateTime",
"%1 - %2",
1973 per.start().dateTime(), KLocale::LongDate),
1974 KGlobal::locale()->formatDateTime(
1975 per.end().dateTime(), KLocale::LongDate));
1978 html += htmlRow(QString(), cont);
1982 html += htmlInvitationDetailsTableEnd();
1987 bool noHtmlMode, KDateTime::Spec spec)
1990 return invitationDetailsFreeBusy(fb, noHtmlMode, spec);
1993 static bool replyMeansCounter(
const Incidence::Ptr &incidence)
1995 Q_UNUSED(incidence);
2013 static QString invitationHeaderEvent(
const Event::Ptr &event,
2014 const Incidence::Ptr &existingIncidence,
2017 if (!msg || !event) {
2021 switch (msg->method()) {
2023 return i18n(
"This invitation has been published");
2025 if (existingIncidence && event->revision() > 0) {
2026 QString orgStr = organizerName(event, sender);
2027 if (senderIsOrganizer(event, sender)) {
2028 return i18n(
"This invitation has been updated by the organizer %1", orgStr);
2030 return i18n(
"This invitation has been updated by %1 as a representative of %2",
2034 if (iamOrganizer(event)) {
2035 return i18n(
"I created this invitation");
2037 QString orgStr = organizerName(event, sender);
2038 if (senderIsOrganizer(event, sender)) {
2039 return i18n(
"You received an invitation from %1", orgStr);
2041 return i18n(
"You received an invitation from %1 as a representative of %2",
2046 return i18n(
"This invitation was refreshed");
2048 if (iamOrganizer(event)) {
2049 return i18n(
"This invitation has been canceled");
2051 return i18n(
"The organizer has revoked the invitation");
2054 return i18n(
"Addition to the invitation");
2057 if (replyMeansCounter(event)) {
2058 return i18n(
"%1 makes this counter proposal", firstAttendeeName(event, sender));
2062 if (attendees.count() == 0) {
2063 kDebug() <<
"No attendees in the iCal reply!";
2066 if (attendees.count() != 1) {
2067 kDebug() <<
"Warning: attendeecount in the reply should be 1"
2068 <<
"but is" << attendees.count();
2070 QString attendeeName = firstAttendeeName(event, sender);
2072 QString delegatorName, dummy;
2074 KPIMUtils::extractEmailAddressAndName(attendee->delegator(), dummy, delegatorName);
2075 if (delegatorName.isEmpty()) {
2076 delegatorName = attendee->delegator();
2079 switch (attendee->status()) {
2081 return i18n(
"%1 indicates this invitation still needs some action", attendeeName);
2083 if (event->revision() > 0) {
2084 if (!sender.isEmpty()) {
2085 return i18n(
"This invitation has been updated by attendee %1", sender);
2087 return i18n(
"This invitation has been updated by an attendee");
2090 if (delegatorName.isEmpty()) {
2091 return i18n(
"%1 accepts this invitation", attendeeName);
2093 return i18n(
"%1 accepts this invitation on behalf of %2",
2094 attendeeName, delegatorName);
2098 if (delegatorName.isEmpty()) {
2099 return i18n(
"%1 tentatively accepts this invitation", attendeeName);
2101 return i18n(
"%1 tentatively accepts this invitation on behalf of %2",
2102 attendeeName, delegatorName);
2105 if (delegatorName.isEmpty()) {
2106 return i18n(
"%1 declines this invitation", attendeeName);
2108 return i18n(
"%1 declines this invitation on behalf of %2",
2109 attendeeName, delegatorName);
2113 QString delegate, dummy;
2114 KPIMUtils::extractEmailAddressAndName(attendee->delegate(), dummy, delegate);
2115 if (delegate.isEmpty()) {
2116 delegate = attendee->delegate();
2118 if (!delegate.isEmpty()) {
2119 return i18n(
"%1 has delegated this invitation to %2", attendeeName, delegate);
2121 return i18n(
"%1 has delegated this invitation", attendeeName);
2125 return i18n(
"This invitation is now completed");
2127 return i18n(
"%1 is still processing the invitation", attendeeName);
2129 return i18n(
"Unknown response to this invitation");
2134 return i18n(
"%1 makes this counter proposal",
2135 firstAttendeeName(event, i18n(
"Sender")));
2139 QString orgStr = organizerName(event, sender);
2140 if (senderIsOrganizer(event, sender)) {
2141 return i18n(
"%1 declines your counter proposal", orgStr);
2143 return i18n(
"%1 declines your counter proposal on behalf of %2", sender, orgStr);
2148 return i18n(
"Error: Event iTIP message with unknown method");
2150 kError() <<
"encountered an iTIP method that we do not support";
2154 static QString invitationHeaderTodo(
const Todo::Ptr &todo,
2155 const Incidence::Ptr &existingIncidence,
2158 if (!msg || !todo) {
2162 switch (msg->method()) {
2164 return i18n(
"This to-do has been published");
2166 if (existingIncidence && todo->revision() > 0) {
2167 QString orgStr = organizerName(todo, sender);
2168 if (senderIsOrganizer(todo, sender)) {
2169 return i18n(
"This to-do has been updated by the organizer %1", orgStr);
2171 return i18n(
"This to-do has been updated by %1 as a representative of %2",
2175 if (iamOrganizer(todo)) {
2176 return i18n(
"I created this to-do");
2178 QString orgStr = organizerName(todo, sender);
2179 if (senderIsOrganizer(todo, sender)) {
2180 return i18n(
"You have been assigned this to-do by %1", orgStr);
2182 return i18n(
"You have been assigned this to-do by %1 as a representative of %2",
2188 return i18n(
"This to-do was refreshed");
2190 if (iamOrganizer(todo)) {
2191 return i18n(
"This to-do was canceled");
2193 return i18n(
"The organizer has revoked this to-do");
2196 return i18n(
"Addition to the to-do");
2199 if (replyMeansCounter(todo)) {
2200 return i18n(
"%1 makes this counter proposal", firstAttendeeName(todo, sender));
2204 if (attendees.count() == 0) {
2205 kDebug() <<
"No attendees in the iCal reply!";
2208 if (attendees.count() != 1) {
2209 kDebug() <<
"Warning: attendeecount in the reply should be 1"
2210 <<
"but is" << attendees.count();
2212 QString attendeeName = firstAttendeeName(todo, sender);
2214 QString delegatorName, dummy;
2216 KPIMUtils::extractEmailAddressAndName(attendee->delegate(), dummy, delegatorName);
2217 if (delegatorName.isEmpty()) {
2218 delegatorName = attendee->delegator();
2221 switch (attendee->status()) {
2223 return i18n(
"%1 indicates this to-do assignment still needs some action",
2226 if (todo->revision() > 0) {
2227 if (!sender.isEmpty()) {
2228 if (todo->isCompleted()) {
2229 return i18n(
"This to-do has been completed by assignee %1", sender);
2231 return i18n(
"This to-do has been updated by assignee %1", sender);
2234 if (todo->isCompleted()) {
2235 return i18n(
"This to-do has been completed by an assignee");
2237 return i18n(
"This to-do has been updated by an assignee");
2241 if (delegatorName.isEmpty()) {
2242 return i18n(
"%1 accepts this to-do", attendeeName);
2244 return i18n(
"%1 accepts this to-do on behalf of %2",
2245 attendeeName, delegatorName);
2249 if (delegatorName.isEmpty()) {
2250 return i18n(
"%1 tentatively accepts this to-do", attendeeName);
2252 return i18n(
"%1 tentatively accepts this to-do on behalf of %2",
2253 attendeeName, delegatorName);
2256 if (delegatorName.isEmpty()) {
2257 return i18n(
"%1 declines this to-do", attendeeName);
2259 return i18n(
"%1 declines this to-do on behalf of %2",
2260 attendeeName, delegatorName);
2264 QString delegate, dummy;
2265 KPIMUtils::extractEmailAddressAndName(attendee->delegate(), dummy, delegate);
2266 if (delegate.isEmpty()) {
2267 delegate = attendee->delegate();
2269 if (!delegate.isEmpty()) {
2270 return i18n(
"%1 has delegated this to-do to %2", attendeeName, delegate);
2272 return i18n(
"%1 has delegated this to-do", attendeeName);
2276 return i18n(
"The request for this to-do is now completed");
2278 return i18n(
"%1 is still processing the to-do", attendeeName);
2280 return i18n(
"Unknown response to this to-do");
2285 return i18n(
"%1 makes this counter proposal", firstAttendeeName(todo, sender));
2289 QString orgStr = organizerName(todo, sender);
2290 if (senderIsOrganizer(todo, sender)) {
2291 return i18n(
"%1 declines the counter proposal", orgStr);
2293 return i18n(
"%1 declines the counter proposal on behalf of %2", sender, orgStr);
2298 return i18n(
"Error: To-do iTIP message with unknown method");
2300 kError() <<
"encountered an iTIP method that we do not support";
2304 static QString invitationHeaderJournal(
const Journal::Ptr &journal,
2307 if (!msg || !journal) {
2311 switch (msg->method()) {
2313 return i18n(
"This journal has been published");
2315 return i18n(
"You have been assigned this journal");
2317 return i18n(
"This journal was refreshed");
2319 return i18n(
"This journal was canceled");
2321 return i18n(
"Addition to the journal");
2324 if (replyMeansCounter(journal)) {
2325 return i18n(
"Sender makes this counter proposal");
2329 if (attendees.count() == 0) {
2330 kDebug() <<
"No attendees in the iCal reply!";
2333 if (attendees.count() != 1) {
2334 kDebug() <<
"Warning: attendeecount in the reply should be 1 "
2335 <<
"but is " << attendees.count();
2339 switch (attendee->status()) {
2341 return i18n(
"Sender indicates this journal assignment still needs some action");
2343 return i18n(
"Sender accepts this journal");
2345 return i18n(
"Sender tentatively accepts this journal");
2347 return i18n(
"Sender declines this journal");
2349 return i18n(
"Sender has delegated this request for the journal");
2351 return i18n(
"The request for this journal is now completed");
2353 return i18n(
"Sender is still processing the invitation");
2355 return i18n(
"Unknown response to this journal");
2360 return i18n(
"Sender makes this counter proposal");
2362 return i18n(
"Sender declines the counter proposal");
2364 return i18n(
"Error: Journal iTIP message with unknown method");
2366 kError() <<
"encountered an iTIP method that we do not support";
2370 static QString invitationHeaderFreeBusy(
const FreeBusy::Ptr &fb,
2377 switch (msg->method()) {
2379 return i18n(
"This free/busy list has been published");
2381 return i18n(
"The free/busy list has been requested");
2383 return i18n(
"This free/busy list was refreshed");
2385 return i18n(
"This free/busy list was canceled");
2387 return i18n(
"Addition to the free/busy list");
2389 return i18n(
"Reply to the free/busy list");
2391 return i18n(
"Sender makes this counter proposal");
2393 return i18n(
"Sender declines the counter proposal");
2395 return i18n(
"Error: Free/Busy iTIP message with unknown method");
2397 kError() <<
"encountered an iTIP method that we do not support";
2402 static QString invitationAttendeeList(
const Incidence::Ptr &incidence)
2408 if (incidence->type() == Incidence::TypeTodo) {
2409 tmpStr += i18n(
"Assignees");
2411 tmpStr += i18n(
"Invitation List");
2416 if (!attendees.isEmpty()) {
2417 QStringList comments;
2418 Attendee::List::ConstIterator it;
2419 for (it = attendees.constBegin(); it != attendees.constEnd(); ++it) {
2421 if (!iamAttendee(a)) {
2424 tmpStr +=
"<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\">";
2429 if (attendeeIsOrganizer(incidence, a)) {
2430 comments << i18n(
"organizer");
2432 if (!a->delegator().isEmpty()) {
2433 comments << i18n(
" (delegated by %1)", a->delegator());
2435 if (!a->delegate().isEmpty()) {
2436 comments << i18n(
" (delegated to %1)", a->delegate());
2438 tmpStr += invitationPerson(a->email(), a->name(), QString(), comments.join(
","));
2445 tmpStr +=
"</table>";
2453 static QString invitationRsvpList(
const Incidence::Ptr &incidence,
const Attendee::Ptr &sender)
2459 if (incidence->type() == Incidence::TypeTodo) {
2460 tmpStr += i18n(
"Assignees");
2462 tmpStr += i18n(
"Invitation List");
2467 if (!attendees.isEmpty()) {
2468 QStringList comments;
2469 Attendee::List::ConstIterator it;
2470 for (it = attendees.constBegin(); it != attendees.constEnd(); ++it) {
2472 if (!attendeeIsOrganizer(incidence, a)) {
2473 QString statusStr = Stringify::attendeeStatus(a->status());
2474 if (sender && (a->email() == sender->email())) {
2477 if (a->status() != sender->status()) {
2478 statusStr = i18n(
"%1 (<i>unrecorded</i>)",
2479 Stringify::attendeeStatus(sender->status()));
2485 tmpStr +=
"<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\">";
2490 if (iamAttendee(a)) {
2491 comments << i18n(
"myself");
2493 if (!a->delegator().isEmpty()) {
2494 comments << i18n(
" (delegated by %1)", a->delegator());
2496 if (!a->delegate().isEmpty()) {
2497 comments << i18n(
" (delegated to %1)", a->delegate());
2499 tmpStr += invitationPerson(a->email(), a->name(), QString(), comments.join(
","));
2501 tmpStr +=
"<td>" + statusStr +
"</td>";
2507 tmpStr +=
"</table>";
2509 tmpStr +=
"<i> " + i18nc(
"no attendees",
"None") +
"</i>";
2515 static QString invitationAttachments(InvitationFormatterHelper *helper,
2516 const Incidence::Ptr &incidence)
2523 if (incidence->type() == Incidence::TypeFreeBusy) {
2529 if (!attachments.isEmpty()) {
2530 tmpStr += i18n(
"Attached Documents:") +
"<ol>";
2532 Attachment::List::ConstIterator it;
2533 for (it = attachments.constBegin(); it != attachments.constEnd(); ++it) {
2537 KMimeType::Ptr
mimeType = KMimeType::mimeType(a->mimeType());
2538 const QString iconStr = (mimeType ?
2539 mimeType->iconName(a->uri()) :
2540 QString(
"application-octet-stream"));
2541 const QString iconPath = KIconLoader::global()->iconPath(iconStr, KIconLoader::Small);
2542 if (!iconPath.isEmpty()) {
2543 tmpStr +=
"<img valign=\"top\" src=\"" + iconPath +
"\">";
2545 tmpStr += helper->makeLink(
"ATTACH:" + a->label().toUtf8().toBase64(), a->label());
2555 class KCalUtils::IncidenceFormatter::ScheduleMessageVisitor :
public Visitor
2558 ScheduleMessageVisitor() : mMessage(0) {
2561 bool act(
const IncidenceBase::Ptr &incidence,
2562 const Incidence::Ptr &existingIncidence,
2565 mExistingIncidence = existingIncidence;
2568 return incidence->accept(*
this, incidence);
2570 QString result()
const {
2576 Incidence::Ptr mExistingIncidence;
2581 class KCalUtils::IncidenceFormatter::InvitationHeaderVisitor :
2582 public IncidenceFormatter::ScheduleMessageVisitor
2587 mResult = invitationHeaderEvent(event, mExistingIncidence, mMessage, mSender);
2588 return !mResult.isEmpty();
2592 mResult = invitationHeaderTodo(todo, mExistingIncidence, mMessage, mSender);
2593 return !mResult.isEmpty();
2597 mResult = invitationHeaderJournal(journal, mMessage);
2598 return !mResult.isEmpty();
2602 mResult = invitationHeaderFreeBusy(fb, mMessage);
2603 return !mResult.isEmpty();
2607 class KCalUtils::IncidenceFormatter::InvitationBodyVisitor
2608 :
public IncidenceFormatter::ScheduleMessageVisitor
2611 InvitationBodyVisitor(
bool noHtmlMode, KDateTime::Spec spec)
2612 : ScheduleMessageVisitor(), mNoHtmlMode(noHtmlMode), mSpec(spec) {}
2618 mResult = invitationDetailsEvent(event, oldevent, mMessage, mNoHtmlMode, mSpec);
2619 return !mResult.isEmpty();
2624 mResult = invitationDetailsTodo(todo, oldtodo, mMessage, mNoHtmlMode, mSpec);
2625 return !mResult.isEmpty();
2630 mResult = invitationDetailsJournal(journal, oldjournal, mNoHtmlMode, mSpec);
2631 return !mResult.isEmpty();
2635 mResult = invitationDetailsFreeBusy(fb,
FreeBusy::Ptr(), mNoHtmlMode, mSpec);
2636 return !mResult.isEmpty();
2641 KDateTime::Spec mSpec;
2645 InvitationFormatterHelper::InvitationFormatterHelper()
2650 InvitationFormatterHelper::~InvitationFormatterHelper()
2654 QString InvitationFormatterHelper::generateLinkURL(
const QString &
id)
2660 class IncidenceFormatter::IncidenceCompareVisitor :
public Visitor
2663 IncidenceCompareVisitor() {}
2664 bool act(
const IncidenceBase::Ptr &incidence,
2665 const Incidence::Ptr &existingIncidence)
2667 if (!existingIncidence) {
2670 Incidence::Ptr inc = incidence.staticCast<
Incidence>();
2671 if (!inc || !existingIncidence ||
2672 inc->revision() <= existingIncidence->revision()) {
2675 mExistingIncidence = existingIncidence;
2676 return incidence->
accept(*
this, incidence);
2679 QString result()
const
2681 if (mChanges.isEmpty()) {
2684 QString html =
"<div align=\"left\"><ul><li>";
2685 html += mChanges.join(
"</li><li>");
2686 html +=
"</li><ul></div>";
2693 compareEvents(event, mExistingIncidence.dynamicCast<
Event>());
2694 compareIncidences(event, mExistingIncidence);
2695 return !mChanges.isEmpty();
2699 compareTodos(todo, mExistingIncidence.dynamicCast<
Todo>());
2700 compareIncidences(todo, mExistingIncidence);
2701 return !mChanges.isEmpty();
2705 compareIncidences(journal, mExistingIncidence);
2706 return !mChanges.isEmpty();
2711 return !mChanges.isEmpty();
2715 void compareEvents(
const Event::Ptr &newEvent,
2718 if (!oldEvent || !newEvent) {
2721 if (oldEvent->dtStart() != newEvent->dtStart() ||
2722 oldEvent->allDay() != newEvent->allDay()) {
2723 mChanges += i18n(
"The invitation starting time has been changed from %1 to %2",
2724 eventStartTimeStr(oldEvent), eventStartTimeStr(newEvent));
2726 if (oldEvent->dtEnd() != newEvent->dtEnd() ||
2727 oldEvent->allDay() != newEvent->allDay()) {
2728 mChanges += i18n(
"The invitation ending time has been changed from %1 to %2",
2729 eventEndTimeStr(oldEvent), eventEndTimeStr(newEvent));
2733 void compareTodos(
const Todo::Ptr &newTodo,
2736 if (!oldTodo || !newTodo) {
2740 if (!oldTodo->isCompleted() && newTodo->isCompleted()) {
2741 mChanges += i18n(
"The to-do has been completed");
2743 if (oldTodo->isCompleted() && !newTodo->isCompleted()) {
2744 mChanges += i18n(
"The to-do is no longer completed");
2746 if (oldTodo->percentComplete() != newTodo->percentComplete()) {
2747 const QString oldPer = i18n(
"%1%", oldTodo->percentComplete());
2748 const QString newPer = i18n(
"%1%", newTodo->percentComplete());
2749 mChanges += i18n(
"The task completed percentage has changed from %1 to %2",
2753 if (!oldTodo->hasStartDate() && newTodo->hasStartDate()) {
2754 mChanges += i18n(
"A to-do starting time has been added");
2756 if (oldTodo->hasStartDate() && !newTodo->hasStartDate()) {
2757 mChanges += i18n(
"The to-do starting time has been removed");
2759 if (oldTodo->hasStartDate() && newTodo->hasStartDate() &&
2760 oldTodo->dtStart() != newTodo->dtStart()) {
2761 mChanges += i18n(
"The to-do starting time has been changed from %1 to %2",
2766 if (!oldTodo->hasDueDate() && newTodo->hasDueDate()) {
2767 mChanges += i18n(
"A to-do due time has been added");
2769 if (oldTodo->hasDueDate() && !newTodo->hasDueDate()) {
2770 mChanges += i18n(
"The to-do due time has been removed");
2772 if (oldTodo->hasDueDate() && newTodo->hasDueDate() &&
2773 oldTodo->dtDue() != newTodo->dtDue()) {
2774 mChanges += i18n(
"The to-do due time has been changed from %1 to %2",
2780 void compareIncidences(
const Incidence::Ptr &newInc,
2781 const Incidence::Ptr &oldInc)
2783 if (!oldInc || !newInc) {
2787 if (oldInc->summary() != newInc->summary()) {
2788 mChanges += i18n(
"The summary has been changed to: \"%1\"",
2789 newInc->richSummary());
2792 if (oldInc->location() != newInc->location()) {
2793 mChanges += i18n(
"The location has been changed to: \"%1\"",
2794 newInc->richLocation());
2797 if (oldInc->description() != newInc->description()) {
2798 mChanges += i18n(
"The description has been changed to: \"%1\"",
2799 newInc->richDescription());
2804 for (Attendee::List::ConstIterator it = newAttendees.constBegin();
2805 it != newAttendees.constEnd(); ++it) {
2806 Attendee::Ptr oldAtt = oldInc->attendeeByMail((*it)->email());
2808 mChanges += i18n(
"Attendee %1 has been added", (*it)->fullName());
2810 if (oldAtt->status() != (*it)->status()) {
2811 mChanges += i18n(
"The status of attendee %1 has been changed to: %2",
2812 (*it)->fullName(), Stringify::attendeeStatus((*it)->status()));
2817 for (Attendee::List::ConstIterator it = oldAttendees.constBegin();
2818 it != oldAttendees.constEnd(); ++it) {
2819 if (!attendeeIsOrganizer(oldInc, (*it))) {
2820 Attendee::Ptr newAtt = newInc->attendeeByMail((*it)->email());
2822 mChanges += i18n(
"Attendee %1 has been removed", (*it)->fullName());
2829 Incidence::Ptr mExistingIncidence;
2830 QStringList mChanges;
2834 QString InvitationFormatterHelper::makeLink(
const QString &
id,
const QString &text)
2836 if (!
id.startsWith(QLatin1String(
"ATTACH:"))) {
2837 QString res = QString(
"<a href=\"%1\"><font size=\"-1\"><b>%2</b></font></a>").
2838 arg(generateLinkURL(
id), text);
2842 QString res = QString(
"<a href=\"%1\">%2</a>").
2843 arg(generateLinkURL(
id), text);
2850 static bool incidenceOwnedByMe(
const Calendar::Ptr &calendar,
2851 const Incidence::Ptr &incidence)
2854 Q_UNUSED(incidence);
2858 static QString inviteButton(InvitationFormatterHelper *helper,
2859 const QString &
id,
const QString &text)
2866 html +=
"<td style=\"border-width:2px;border-style:outset\">";
2867 if (!
id.isEmpty()) {
2868 html += helper->makeLink(
id, text);
2876 static QString inviteLink(InvitationFormatterHelper *helper,
2877 const QString &
id,
const QString &text)
2881 if (helper && !
id.isEmpty()) {
2882 html += helper->makeLink(
id, text);
2889 static QString responseButtons(
const Incidence::Ptr &incidence,
2890 bool rsvpReq,
bool rsvpRec,
2891 InvitationFormatterHelper *helper)
2898 if (!rsvpReq && (incidence && incidence->revision() == 0)) {
2900 html += inviteButton(helper,
"record", i18n(
"Record"));
2903 html += inviteButton(helper,
"delete", i18n(
"Move to Trash"));
2908 html += inviteButton(helper,
"accept",
2909 i18nc(
"accept invitation",
"Accept"));
2912 html += inviteButton(helper,
"accept_conditionally",
2913 i18nc(
"Accept invitation conditionally",
"Accept cond."));
2916 html += inviteButton(helper,
"counter",
2917 i18nc(
"invitation counter proposal",
"Counter proposal"));
2920 html += inviteButton(helper,
"decline",
2921 i18nc(
"decline invitation",
"Decline"));
2924 if (!rsvpRec || (incidence && incidence->revision() > 0)) {
2926 html += inviteButton(helper,
"delegate",
2927 i18nc(
"delegate inviation to another",
"Delegate"));
2930 html += inviteButton(helper,
"forward", i18nc(
"forward request to another",
"Forward"));
2933 if (incidence && incidence->type() == Incidence::TypeEvent) {
2934 html += inviteButton(helper,
"check_calendar",
2935 i18nc(
"look for scheduling conflicts",
"Check my calendar"));
2941 static QString counterButtons(
const Incidence::Ptr &incidence,
2942 InvitationFormatterHelper *helper)
2950 html += inviteButton(helper,
"accept_counter", i18n(
"Accept"));
2953 html += inviteButton(helper,
"decline_counter", i18n(
"Decline"));
2957 if (incidence->type() == Incidence::TypeTodo) {
2958 html += inviteButton(helper,
"check_calendar", i18n(
"Check my to-do list"));
2960 html += inviteButton(helper,
"check_calendar", i18n(
"Check my calendar"));
2966 static QString recordButtons(
const Incidence::Ptr &incidence,
2967 InvitationFormatterHelper *helper)
2975 if (incidence->type() == Incidence::TypeTodo) {
2976 html += inviteLink(helper,
"reply",
2977 i18n(
"Record invitation in my to-do list"));
2979 html += inviteLink(helper,
"reply",
2980 i18n(
"Record invitation in my calendar"));
2986 static QString recordResponseButtons(
const Incidence::Ptr &incidence,
2987 InvitationFormatterHelper *helper)
2995 if (incidence->type() == Incidence::TypeTodo) {
2996 html += inviteLink(helper,
"reply",
2997 i18n(
"Record response in my to-do list"));
2999 html += inviteLink(helper,
"reply",
3000 i18n(
"Record response in my calendar"));
3006 static QString cancelButtons(
const Incidence::Ptr &incidence,
3007 InvitationFormatterHelper *helper)
3016 if (incidence->type() == Incidence::TypeTodo) {
3017 html += inviteButton(helper,
"cancel",
3018 i18n(
"Remove invitation from my to-do list"));
3020 html += inviteButton(helper,
"cancel",
3021 i18n(
"Remove invitation from my calendar"));
3032 static QString formatICalInvitationHelper(QString invitation,
3034 InvitationFormatterHelper *helper,
3036 KDateTime::Spec spec,
3037 const QString &sender,
3038 bool outlookCompareStyle)
3040 if (invitation.isEmpty()) {
3050 kDebug() <<
"Failed to parse the scheduling message";
3056 IncidenceBase::Ptr incBase = msg->event();
3058 incBase->shiftTimes(mCalendar->timeSpec(), KDateTime::Spec::LocalZone());
3061 Incidence::Ptr existingIncidence;
3062 if (incBase && helper->calendar()) {
3063 existingIncidence = helper->calendar()->incidence(incBase->uid());
3065 if (!incidenceOwnedByMe(helper->calendar(), existingIncidence)) {
3066 existingIncidence.clear();
3068 if (!existingIncidence) {
3069 const Incidence::List list = helper->calendar()->incidences();
3070 for (Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it) {
3071 if ((*it)->schedulingID() == incBase->uid() &&
3072 incidenceOwnedByMe(helper->calendar(), *it)) {
3073 existingIncidence = *it;
3080 Incidence::Ptr inc = incBase.staticCast<
Incidence>();
3084 int incRevision = 0;
3085 if (inc && inc->type() != Incidence::TypeFreeBusy) {
3086 incRevision = inc->revision();
3091 html +=
"<div align=\"center\" style=\"border:solid 1px;\">";
3093 IncidenceFormatter::InvitationHeaderVisitor headerVisitor;
3095 if (!headerVisitor.act(inc, existingIncidence, msg, sender)) {
3098 html += htmlAddTag(
"h3", headerVisitor.result());
3100 if (outlookCompareStyle ||
3103 IncidenceFormatter::InvitationBodyVisitor bodyVisitor(noHtmlMode, spec);
3107 if (inc && existingIncidence &&
3108 incRevision < existingIncidence->revision()) {
3109 bodyOk = bodyVisitor.act(existingIncidence, inc, msg, sender);
3111 bodyOk = bodyVisitor.act(inc, existingIncidence, msg, sender);
3114 bodyOk = bodyVisitor.act(inc, Incidence::Ptr(), msg, sender);
3117 html += bodyVisitor.result();
3123 InvitationBodyVisitor bodyVisitor(noHtmlMode, spec);
3124 if (!bodyVisitor.act(inc, Incidence::Ptr(), msg, sender)) {
3127 html += bodyVisitor.result();
3130 IncidenceFormatter::IncidenceCompareVisitor compareVisitor;
3131 if (compareVisitor.act(inc, existingIncidence)) {
3132 html +=
"<p align=\"left\">";
3133 if (senderIsOrganizer(inc, sender)) {
3134 html += i18n(
"The following changes have been made by the organizer:");
3135 }
else if (!sender.isEmpty()) {
3136 html += i18n(
"The following changes have been made by %1:", sender);
3138 html += i18n(
"The following changes have been made:");
3141 html += compareVisitor.result();
3145 IncidenceCompareVisitor compareVisitor;
3146 if (compareVisitor.act(inc, existingIncidence)) {
3147 html +=
"<p align=\"left\">";
3148 if (!sender.isEmpty()) {
3149 html += i18n(
"The following changes have been made by %1:", sender);
3151 html += i18n(
"The following changes have been made by an attendee:");
3154 html += compareVisitor.result();
3160 bool myInc = iamOrganizer(inc);
3163 bool rsvpRec =
false;
3166 Incidence::Ptr rsvpIncidence = existingIncidence;
3167 if (!rsvpIncidence && inc && incRevision > 0) {
3168 rsvpIncidence = inc;
3170 if (rsvpIncidence) {
3171 ea = findMyAttendee(rsvpIncidence);
3174 (ea->status() == Attendee::Accepted ||
3175 ea->status() == Attendee::Declined ||
3176 ea->status() == Attendee::Tentative)) {
3183 bool isDelegated =
false;
3186 if (!inc->attendees().isEmpty()) {
3187 a = inc->attendees().first();
3191 isDelegated = (a->status() == Attendee::Delegated);
3192 role = Stringify::attendeeRole(a->role());
3196 bool rsvpReq = rsvpRequested(inc);
3199 if (rsvpRec && inc) {
3200 if (incRevision == 0) {
3201 tStr = i18n(
"Your <b>%1</b> response has been recorded",
3202 Stringify::attendeeStatus(ea->status()));
3204 tStr = i18n(
"Your status for this invitation is <b>%1</b>",
3205 Stringify::attendeeStatus(ea->status()));
3209 tStr = i18n(
"This invitation was canceled");
3210 }
else if (msg->method() ==
iTIPAdd) {
3211 tStr = i18n(
"This invitation was accepted");
3214 tStr = rsvpRequestedStr(rsvpReq, role);
3217 tStr = rsvpRequestedStr(rsvpReq, role);
3219 tStr = i18n(
"Awaiting delegation response");
3223 html +=
"<i><u>" + tStr +
"</u></i>";
3228 if (inc && incRevision == 0) {
3229 QString statStr = myStatusStr(inc);
3230 if (!statStr.isEmpty()) {
3232 html +=
"<i>" + statStr +
"</i>";
3240 html +=
"<table border=\"0\" align=\"center\" cellspacing=\"4\"><tr>";
3242 switch (msg->method()) {
3248 if (inc && incRevision > 0 && (existingIncidence || !helper->calendar())) {
3249 html += recordButtons(inc, helper);
3254 html += responseButtons(inc, rsvpReq, rsvpRec, helper);
3256 html += responseButtons(inc,
false,
false, helper);
3263 html += cancelButtons(inc, helper);
3273 if (replyMeansCounter(inc)) {
3274 html +=
"<tr>" + counterButtons(inc, helper) +
"</tr>";
3283 a = findDelegatedFromMyAttendee(inc);
3285 if (a->status() != Attendee::Accepted ||
3286 a->status() != Attendee::Tentative) {
3287 html += responseButtons(inc, rsvpReq, rsvpRec, helper);
3293 if (!inc->attendees().isEmpty()) {
3294 a = inc->attendees().first();
3296 if (a && helper->calendar()) {
3297 ea = findAttendee(existingIncidence, a->email());
3300 if (ea && (ea->status() != Attendee::NeedsAction) && (ea->status() == a->status())) {
3301 const QString tStr = i18n(
"The <b>%1</b> response has been recorded",
3302 Stringify::attendeeStatus(ea->status()));
3303 html += inviteButton(helper, QString(), htmlAddTag(
"i", tStr));
3306 html += recordResponseButtons(inc, helper);
3314 html += counterButtons(inc, helper);
3318 html += responseButtons(inc, rsvpReq, rsvpRec, helper);
3326 html +=
"</tr></table>";
3330 html += invitationRsvpList(existingIncidence, a);
3332 html += invitationAttendeeList(inc);
3339 html += invitationAttachments(helper, inc);
3347 InvitationFormatterHelper *helper,
3348 bool outlookCompareStyle)
3350 return formatICalInvitationHelper(invitation, calendar, helper,
false,
3351 KSystemTimeZones::local(), QString(),
3352 outlookCompareStyle);
3357 InvitationFormatterHelper *helper,
3358 const QString &sender,
3359 bool outlookCompareStyle)
3361 return formatICalInvitationHelper(invitation, calendar, helper,
true,
3362 KSystemTimeZones::local(), sender,
3363 outlookCompareStyle);
3371 class KCalUtils::IncidenceFormatter::ToolTipVisitor :
public Visitor
3375 : mRichText(true), mSpec(KDateTime::Spec()), mResult(
"") {}
3378 const IncidenceBase::Ptr &incidence,
3379 const QDate &date=QDate(),
bool richText=
true,
3380 KDateTime::Spec spec=KDateTime::Spec())
3382 mCalendar = calendar;
3385 mRichText = richText;
3388 return incidence ? incidence->
accept(*
this, incidence) :
false;
3391 bool act(
const QString &location,
const IncidenceBase::Ptr &incidence,
3392 const QDate &date=QDate(),
bool richText=
true,
3393 KDateTime::Spec spec=KDateTime::Spec())
3395 mLocation = location;
3397 mRichText = richText;
3400 return incidence ? incidence->
accept(*
this, incidence) :
false;
3403 QString result()
const {
3413 QString dateRangeText(
const Event::Ptr &event,
const QDate &date);
3414 QString dateRangeText(
const Todo::Ptr &todo,
const QDate &date);
3418 QString generateToolTip(
const Incidence::Ptr &incidence, QString dtRangeText);
3425 KDateTime::Spec mSpec;
3429 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const Event::Ptr &event,
3436 KDateTime startDt =
event->dtStart();
3437 KDateTime endDt =
event->dtEnd();
3438 if (event->recurs()) {
3439 if (date.isValid()) {
3440 KDateTime kdt(date, QTime(0, 0, 0), KSystemTimeZones::local());
3441 int diffDays = startDt.daysTo(kdt);
3442 kdt = kdt.addSecs(-1);
3443 startDt.setDate(event->recurrence()->getNextDateTime(kdt).date());
3444 if (event->hasEndDate()) {
3445 endDt = endDt.addDays(diffDays);
3446 if (startDt > endDt) {
3447 startDt.setDate(event->recurrence()->getPreviousDateTime(kdt).date());
3448 endDt = startDt.addDays(event->dtStart().daysTo(event->dtEnd()));
3454 if (event->isMultiDay()) {
3456 ret +=
"<br>" + i18nc(
"Event start",
"<i>From:</i> %1", tmp);
3459 ret +=
"<br>" + i18nc(
"Event end",
"<i>To:</i> %1", tmp);
3464 i18n(
"<i>Date:</i> %1",
dateToString(startDt,
false, mSpec));
3465 if (!event->allDay()) {
3466 const QString dtStartTime =
timeToString(startDt,
true, mSpec);
3467 const QString dtEndTime =
timeToString(endDt,
true, mSpec);
3468 if (dtStartTime == dtEndTime) {
3471 i18nc(
"time for event",
"<i>Time:</i> %1",
3475 i18nc(
"time range for event",
3476 "<i>Time:</i> %1 - %2",
3477 dtStartTime, dtEndTime);
3482 return ret.replace(
' ',
" ");
3485 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const Todo::Ptr &todo,
3490 if (todo->hasStartDate() && todo->dtStart().isValid()) {
3491 KDateTime startDt = todo->dtStart();
3492 if (todo->recurs()) {
3493 if (date.isValid()) {
3494 startDt.setDate(date);
3498 i18n(
"<i>Start:</i> %1",
dateToString(startDt,
false, mSpec));
3501 if (todo->hasDueDate() && todo->dtDue().isValid()) {
3502 KDateTime dueDt = todo->dtDue();
3503 if (todo->recurs()) {
3504 if (date.isValid()) {
3505 KDateTime kdt(date, QTime(0, 0, 0), KSystemTimeZones::local());
3506 kdt = kdt.addSecs(-1);
3507 dueDt.setDate(todo->recurrence()->getNextDateTime(kdt).date());
3511 i18n(
"<i>Due:</i> %1",
3517 if (todo->priority() > 0) {
3519 ret +=
"<i>" + i18n(
"Priority:") +
"</i>" +
" ";
3520 ret += QString::number(todo->priority());
3524 if (todo->isCompleted()) {
3525 ret +=
"<i>" + i18nc(
"Completed: date",
"Completed:") +
"</i>" +
" ";
3528 ret +=
"<i>" + i18n(
"Percent Done:") +
"</i>" +
" ";
3529 ret += i18n(
"%1%", todo->percentComplete());
3532 return ret.replace(
' ',
" ");
3535 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const Journal::Ptr &journal)
3539 if (journal->dtStart().isValid()) {
3541 i18n(
"<i>Date:</i> %1",
dateToString(journal->dtStart(),
false, mSpec));
3543 return ret.replace(
' ',
" ");
3546 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const FreeBusy::Ptr &fb)
3551 i18n(
"<i>Period start:</i> %1",
3554 i18n(
"<i>Period start:</i> %1",
3556 return ret.replace(
' ',
" ");
3561 mResult = generateToolTip(event, dateRangeText(event, mDate));
3562 return !mResult.isEmpty();
3567 mResult = generateToolTip(todo, dateRangeText(todo, mDate));
3568 return !mResult.isEmpty();
3573 mResult = generateToolTip(journal, dateRangeText(journal));
3574 return !mResult.isEmpty();
3580 mResult =
"<qt><b>" +
3581 i18n(
"Free/Busy information for %1", fb->organizer()->fullName()) +
3583 mResult += dateRangeText(fb);
3585 return !mResult.isEmpty();
3588 static QString tooltipPerson(
const QString &email,
const QString &name,
Attendee::PartStat status)
3591 const QString printName = searchName(email, name);
3594 const QString iconPath = rsvpStatusIconPath(status);
3597 QString personString;
3598 if (!iconPath.isEmpty()) {
3599 personString +=
"<img valign=\"top\" src=\"" + iconPath +
"\">" +
" ";
3601 if (status != Attendee::None) {
3602 personString += i18nc(
"attendee name (attendee status)",
"%1 (%2)",
3603 printName.isEmpty() ? email : printName,
3604 Stringify::attendeeStatus(status));
3606 personString += i18n(
"%1", printName.isEmpty() ? email : printName);
3608 return personString;
3611 static QString tooltipFormatOrganizer(
const QString &email,
const QString &name)
3614 const QString printName = searchName(email, name);
3617 const QString iconPath =
3618 KIconLoader::global()->iconPath(
"meeting-organizer", KIconLoader::Small);
3621 QString personString;
3622 personString +=
"<img valign=\"top\" src=\"" + iconPath +
"\">" +
" ";
3623 personString += (printName.isEmpty() ? email : printName);
3624 return personString;
3627 static QString tooltipFormatAttendeeRoleList(
const Incidence::Ptr &incidence,
3631 const QString etc = i18nc(
"elipsis",
"...");
3635 Attendee::List::ConstIterator it;
3638 for (it = attendees.constBegin(); it != attendees.constEnd(); ++it) {
3640 if (a->role() != role) {
3644 if (attendeeIsOrganizer(incidence, a)) {
3648 if (i == maxNumAtts) {
3649 tmpStr +=
" " + etc;
3652 tmpStr +=
" " + tooltipPerson(a->email(), a->name(),
3653 showStatus ? a->status() : Attendee::None);
3654 if (!a->delegator().isEmpty()) {
3655 tmpStr += i18n(
" (delegated by %1)", a->delegator());
3657 if (!a->delegate().isEmpty()) {
3658 tmpStr += i18n(
" (delegated to %1)", a->delegate());
3663 if (tmpStr.endsWith(QLatin1String(
"<br>"))) {
3669 static QString tooltipFormatAttendees(
const Calendar::Ptr &calendar,
3670 const Incidence::Ptr &incidence)
3672 QString tmpStr, str;
3675 int attendeeCount = incidence->attendees().count();
3676 if (attendeeCount > 1 ||
3677 (attendeeCount == 1 &&
3678 !attendeeIsOrganizer(incidence, incidence->attendees().first()))) {
3679 tmpStr +=
"<i>" + i18n(
"Organizer:") +
"</i>" +
"<br>";
3680 tmpStr +=
" " + tooltipFormatOrganizer(incidence->organizer()->email(),
3681 incidence->organizer()->name());
3686 const bool showStatus = attendeeCount > 0 && incOrganizerOwnsCalendar(calendar, incidence);
3689 str = tooltipFormatAttendeeRoleList(incidence, Attendee::Chair, showStatus);
3690 if (!str.isEmpty()) {
3691 tmpStr +=
"<br><i>" + i18n(
"Chair:") +
"</i>" +
"<br>";
3696 str = tooltipFormatAttendeeRoleList(incidence, Attendee::ReqParticipant, showStatus);
3697 if (!str.isEmpty()) {
3698 tmpStr +=
"<br><i>" + i18n(
"Required Participants:") +
"</i>" +
"<br>";
3703 str = tooltipFormatAttendeeRoleList(incidence, Attendee::OptParticipant, showStatus);
3704 if (!str.isEmpty()) {
3705 tmpStr +=
"<br><i>" + i18n(
"Optional Participants:") +
"</i>" +
"<br>";
3710 str = tooltipFormatAttendeeRoleList(incidence, Attendee::NonParticipant, showStatus);
3711 if (!str.isEmpty()) {
3712 tmpStr +=
"<br><i>" + i18n(
"Observers:") +
"</i>" +
"<br>";
3719 QString IncidenceFormatter::ToolTipVisitor::generateToolTip(
const Incidence::Ptr &incidence,
3720 QString dtRangeText)
3722 int maxDescLen = 120;
3729 QString tmp =
"<qt>";
3732 tmp +=
"<b>" + incidence->richSummary() +
"</b>";
3735 QString calStr = mLocation;
3739 if (!calStr.isEmpty()) {
3740 tmp +=
"<i>" + i18n(
"Calendar:") +
"</i>" +
" ";
3746 if (!incidence->location().isEmpty()) {
3748 tmp +=
"<i>" + i18n(
"Location:") +
"</i>" +
" ";
3749 tmp += incidence->richLocation();
3753 if (!durStr.isEmpty()) {
3755 tmp +=
"<i>" + i18n(
"Duration:") +
"</i>" +
" ";
3759 if (incidence->recurs()) {
3761 tmp +=
"<i>" + i18n(
"Recurrence:") +
"</i>" +
" ";
3765 if (incidence->hasRecurrenceId()) {
3767 tmp +=
"<i>" + i18n(
"Recurrence:") +
"</i>" +
" ";
3768 tmp += i18n(
"Exception");
3771 if (!incidence->description().isEmpty()) {
3772 QString desc(incidence->description());
3773 if (!incidence->descriptionIsRich()) {
3774 if (desc.length() > maxDescLen) {
3775 desc = desc.left(maxDescLen) + i18nc(
"elipsis",
"...");
3777 desc = Qt::escape(desc).replace(
'\n',
"<br>");
3782 tmp +=
"<i>" + i18n(
"Description:") +
"</i>" +
"<br>";
3787 int reminderCount = incidence->alarms().count();
3788 if (reminderCount > 0 && incidence->hasEnabledAlarms()) {
3790 tmp +=
"<i>" + i18np(
"Reminder:",
"Reminders:", reminderCount) +
"</i>" +
" ";
3795 tmp += tooltipFormatAttendees(mCalendar, incidence);
3797 int categoryCount = incidence->categories().count();
3798 if (categoryCount > 0) {
3800 tmp +=
"<i>" + i18np(
"Category:",
"Categories:", categoryCount) +
"</i>" +
" ";
3801 tmp += incidence->categories().join(
", ");
3810 const IncidenceBase::Ptr &incidence,
3813 KDateTime::Spec spec)
3816 if (incidence && v.act(sourceName, incidence, date, richText, spec)) {
3828 static QString mailBodyIncidence(
const Incidence::Ptr &incidence)
3831 if (!incidence->summary().isEmpty()) {
3832 body += i18n(
"Summary: %1\n", incidence->richSummary());
3834 if (!incidence->organizer()->isEmpty()) {
3835 body += i18n(
"Organizer: %1\n", incidence->organizer()->fullName());
3837 if (!incidence->location().isEmpty()) {
3838 body += i18n(
"Location: %1\n", incidence->richLocation());
3845 class KCalUtils::IncidenceFormatter::MailBodyVisitor :
public Visitor
3849 : mSpec(KDateTime::Spec()), mResult(
"") {}
3851 bool act(IncidenceBase::Ptr incidence, KDateTime::Spec spec=KDateTime::Spec())
3855 return incidence ? incidence->accept(*
this, incidence) :
false;
3857 QString result()
const
3868 mResult = i18n(
"This is a Free Busy Object");
3869 return !mResult.isEmpty();
3872 KDateTime::Spec mSpec;
3878 QString recurrence[]= {
3879 i18nc(
"no recurrence",
"None"),
3880 i18nc(
"event recurs by minutes",
"Minutely"),
3881 i18nc(
"event recurs by hours",
"Hourly"),
3882 i18nc(
"event recurs by days",
"Daily"),
3883 i18nc(
"event recurs by weeks",
"Weekly"),
3884 i18nc(
"event recurs same position (e.g. first monday) each month",
"Monthly Same Position"),
3885 i18nc(
"event recurs same day each month",
"Monthly Same Day"),
3886 i18nc(
"event recurs same month each year",
"Yearly Same Month"),
3887 i18nc(
"event recurs same day each year",
"Yearly Same Day"),
3888 i18nc(
"event recurs same position (e.g. first monday) each year",
"Yearly Same Position")
3891 mResult = mailBodyIncidence(event);
3892 mResult += i18n(
"Start Date: %1\n",
dateToString(event->dtStart(),
true, mSpec));
3893 if (!event->allDay()) {
3894 mResult += i18n(
"Start Time: %1\n",
timeToString(event->dtStart(),
true, mSpec));
3896 if (event->dtStart() !=
event->dtEnd()) {
3897 mResult += i18n(
"End Date: %1\n",
dateToString(event->dtEnd(),
true, mSpec));
3899 if (!event->allDay()) {
3900 mResult += i18n(
"End Time: %1\n",
timeToString(event->dtEnd(),
true, mSpec));
3902 if (event->recurs()) {
3905 mResult += i18n(
"Recurs: %1\n", recurrence[ recur->
recurrenceType() ]);
3906 mResult += i18n(
"Frequency: %1\n", event->recurrence()->frequency());
3909 mResult += i18np(
"Repeats once",
"Repeats %1 times", recur->
duration());
3915 if (event->allDay()) {
3916 endstr = KGlobal::locale()->formatDate(recur->
endDate());
3918 endstr = KGlobal::locale()->formatDateTime(recur->
endDateTime().dateTime());
3920 mResult += i18n(
"Repeat until: %1\n", endstr);
3922 mResult += i18n(
"Repeats forever\n");
3927 if (!event->description().isEmpty()) {
3929 if (event->descriptionIsRich() ||
3930 event->description().startsWith(QLatin1String(
"<!DOCTYPE HTML")))
3932 descStr = cleanHtml(event->description());
3934 descStr =
event->description();
3936 if (!descStr.isEmpty()) {
3937 mResult += i18n(
"Details:\n%1\n", descStr);
3940 return !mResult.isEmpty();
3945 mResult = mailBodyIncidence(todo);
3947 if (todo->hasStartDate() && todo->dtStart().isValid()) {
3948 mResult += i18n(
"Start Date: %1\n",
dateToString(todo->dtStart(
false),
true, mSpec));
3949 if (!todo->allDay()) {
3950 mResult += i18n(
"Start Time: %1\n",
timeToString(todo->dtStart(
false),
true, mSpec));
3953 if (todo->hasDueDate() && todo->dtDue().isValid()) {
3954 mResult += i18n(
"Due Date: %1\n",
dateToString(todo->dtDue(),
true, mSpec));
3955 if (!todo->allDay()) {
3956 mResult += i18n(
"Due Time: %1\n",
timeToString(todo->dtDue(),
true, mSpec));
3959 QString details = todo->richDescription();
3960 if (!details.isEmpty()) {
3961 mResult += i18n(
"Details:\n%1\n", details);
3963 return !mResult.isEmpty();
3968 mResult = mailBodyIncidence(journal);
3969 mResult += i18n(
"Date: %1\n",
dateToString(journal->dtStart(),
true, mSpec));
3970 if (!journal->allDay()) {
3971 mResult += i18n(
"Time: %1\n",
timeToString(journal->dtStart(),
true, mSpec));
3973 if (!journal->description().isEmpty()) {
3974 mResult += i18n(
"Text of the journal:\n%1\n", journal->richDescription());
3976 return !mResult.isEmpty();
3981 KDateTime::Spec spec)
3988 if (v.act(incidence, spec)) {
3995 static QString recurEnd(
const Incidence::Ptr &incidence)
3998 if (incidence->allDay()) {
3999 endstr = KGlobal::locale()->formatDate(incidence->recurrence()->endDate());
4001 endstr = KGlobal::locale()->formatDateTime(incidence->recurrence()->endDateTime());
4013 if (incidence->hasRecurrenceId()) {
4014 return QLatin1String(
"Recurrence exception");
4017 if (!incidence->recurs()) {
4018 return i18n(
"No recurrence");
4020 static QStringList dayList;
4021 if (dayList.isEmpty()) {
4022 dayList.append(i18n(
"31st Last"));
4023 dayList.append(i18n(
"30th Last"));
4024 dayList.append(i18n(
"29th Last"));
4025 dayList.append(i18n(
"28th Last"));
4026 dayList.append(i18n(
"27th Last"));
4027 dayList.append(i18n(
"26th Last"));
4028 dayList.append(i18n(
"25th Last"));
4029 dayList.append(i18n(
"24th Last"));
4030 dayList.append(i18n(
"23rd Last"));
4031 dayList.append(i18n(
"22nd Last"));
4032 dayList.append(i18n(
"21st Last"));
4033 dayList.append(i18n(
"20th Last"));
4034 dayList.append(i18n(
"19th Last"));
4035 dayList.append(i18n(
"18th Last"));
4036 dayList.append(i18n(
"17th Last"));
4037 dayList.append(i18n(
"16th Last"));
4038 dayList.append(i18n(
"15th Last"));
4039 dayList.append(i18n(
"14th Last"));
4040 dayList.append(i18n(
"13th Last"));
4041 dayList.append(i18n(
"12th Last"));
4042 dayList.append(i18n(
"11th Last"));
4043 dayList.append(i18n(
"10th Last"));
4044 dayList.append(i18n(
"9th Last"));
4045 dayList.append(i18n(
"8th Last"));
4046 dayList.append(i18n(
"7th Last"));
4047 dayList.append(i18n(
"6th Last"));
4048 dayList.append(i18n(
"5th Last"));
4049 dayList.append(i18n(
"4th Last"));
4050 dayList.append(i18n(
"3rd Last"));
4051 dayList.append(i18n(
"2nd Last"));
4052 dayList.append(i18nc(
"last day of the month",
"Last"));
4053 dayList.append(i18nc(
"unknown day of the month",
"unknown"));
4054 dayList.append(i18n(
"1st"));
4055 dayList.append(i18n(
"2nd"));
4056 dayList.append(i18n(
"3rd"));
4057 dayList.append(i18n(
"4th"));
4058 dayList.append(i18n(
"5th"));
4059 dayList.append(i18n(
"6th"));
4060 dayList.append(i18n(
"7th"));
4061 dayList.append(i18n(
"8th"));
4062 dayList.append(i18n(
"9th"));
4063 dayList.append(i18n(
"10th"));
4064 dayList.append(i18n(
"11th"));
4065 dayList.append(i18n(
"12th"));
4066 dayList.append(i18n(
"13th"));
4067 dayList.append(i18n(
"14th"));
4068 dayList.append(i18n(
"15th"));
4069 dayList.append(i18n(
"16th"));
4070 dayList.append(i18n(
"17th"));
4071 dayList.append(i18n(
"18th"));
4072 dayList.append(i18n(
"19th"));
4073 dayList.append(i18n(
"20th"));
4074 dayList.append(i18n(
"21st"));
4075 dayList.append(i18n(
"22nd"));
4076 dayList.append(i18n(
"23rd"));
4077 dayList.append(i18n(
"24th"));
4078 dayList.append(i18n(
"25th"));
4079 dayList.append(i18n(
"26th"));
4080 dayList.append(i18n(
"27th"));
4081 dayList.append(i18n(
"28th"));
4082 dayList.append(i18n(
"29th"));
4083 dayList.append(i18n(
"30th"));
4084 dayList.append(i18n(
"31st"));
4087 const int weekStart = KGlobal::locale()->weekStartDay();
4089 const KCalendarSystem *calSys = KGlobal::locale()->calendar();
4093 QString txt, recurStr;
4094 static QString noRecurrence = i18n(
"No recurrence");
4096 case Recurrence::rNone:
4097 return noRecurrence;
4099 case Recurrence::rMinutely:
4101 recurStr = i18np(
"Recurs every minute until %2",
4102 "Recurs every %1 minutes until %2",
4103 recur->
frequency(), recurEnd(incidence));
4105 recurStr += i18nc(
"number of occurrences",
4106 " (<numid>%1</numid> occurrences)",
4110 recurStr = i18np(
"Recurs every minute",
4111 "Recurs every %1 minutes", recur->
frequency());
4115 case Recurrence::rHourly:
4117 recurStr = i18np(
"Recurs hourly until %2",
4118 "Recurs every %1 hours until %2",
4119 recur->
frequency(), recurEnd(incidence));
4121 recurStr += i18nc(
"number of occurrences",
4122 " (<numid>%1</numid> occurrences)",
4126 recurStr = i18np(
"Recurs hourly",
"Recurs every %1 hours", recur->
frequency());
4130 case Recurrence::rDaily:
4132 recurStr = i18np(
"Recurs daily until %2",
4133 "Recurs every %1 days until %2",
4134 recur->
frequency(), recurEnd(incidence));
4136 recurStr += i18nc(
"number of occurrences",
4137 " (<numid>%1</numid> occurrences)",
4141 recurStr = i18np(
"Recurs daily",
"Recurs every %1 days", recur->
frequency());
4145 case Recurrence::rWeekly:
4147 bool addSpace =
false;
4148 for (
int i = 0; i < 7; ++i) {
4149 if (recur->
days().testBit((i + weekStart + 6) % 7)) {
4151 dayNames.append(i18nc(
"separator for list of days",
", "));
4153 dayNames.append(calSys->weekDayName(((i + weekStart + 6) % 7) + 1,
4154 KCalendarSystem::ShortDayName));
4158 if (dayNames.isEmpty()) {
4159 dayNames = i18nc(
"Recurs weekly on no days",
"no days");
4162 recurStr = i18ncp(
"Recurs weekly on [list of days] until end-date",
4163 "Recurs weekly on %2 until %3",
4164 "Recurs every <numid>%1</numid> weeks on %2 until %3",
4165 recur->
frequency(), dayNames, recurEnd(incidence));
4167 recurStr += i18nc(
"number of occurrences",
4168 " (<numid>%1</numid> occurrences)",
4172 recurStr = i18ncp(
"Recurs weekly on [list of days]",
4173 "Recurs weekly on %2",
4174 "Recurs every <numid>%1</numid> weeks on %2",
4179 case Recurrence::rMonthlyPos:
4184 recurStr = i18ncp(
"Recurs every N months on the [2nd|3rd|...]"
4185 " weekdayname until end-date",
4186 "Recurs every month on the %2 %3 until %4",
4187 "Recurs every <numid>%1</numid> months on the %2 %3 until %4",
4189 dayList[rule.pos() + 31],
4190 calSys->weekDayName(rule.day(), KCalendarSystem::LongDayName),
4191 recurEnd(incidence));
4193 recurStr += i18nc(
"number of occurrences",
4194 " (<numid>%1</numid> occurrences)",
4198 recurStr = i18ncp(
"Recurs every N months on the [2nd|3rd|...] weekdayname",
4199 "Recurs every month on the %2 %3",
4200 "Recurs every %1 months on the %2 %3",
4202 dayList[rule.pos() + 31],
4203 calSys->weekDayName(rule.day(), KCalendarSystem::LongDayName));
4208 case Recurrence::rMonthlyDay:
4213 recurStr = i18ncp(
"Recurs monthly on the [1st|2nd|...] day until end-date",
4214 "Recurs monthly on the %2 day until %3",
4215 "Recurs every %1 months on the %2 day until %3",
4218 recurEnd(incidence));
4220 recurStr += i18nc(
"number of occurrences",
4221 " (<numid>%1</numid> occurrences)",
4225 recurStr = i18ncp(
"Recurs monthly on the [1st|2nd|...] day",
4226 "Recurs monthly on the %2 day",
4227 "Recurs every <numid>%1</numid> month on the %2 day",
4229 dayList[days + 31]);
4234 case Recurrence::rYearlyMonth:
4238 recurStr = i18ncp(
"Recurs Every N years on month-name [1st|2nd|...]"
4240 "Recurs yearly on %2 %3 until %4",
4241 "Recurs every %1 years on %2 %3 until %4",
4245 recurEnd(incidence));
4247 recurStr += i18nc(
"number of occurrences",
4248 " (<numid>%1</numid> occurrences)",
4254 recurStr = i18ncp(
"Recurs Every N years on month-name [1st|2nd|...]",
4255 "Recurs yearly on %2 %3",
4256 "Recurs every %1 years on %2 %3",
4263 recurStr = i18nc(
"Recurs Every year on month-name [1st|2nd|...]",
4264 "Recurs yearly on %1 %2",
4267 dayList[ recur->
startDate().day() + 31 ]);
4269 recurStr = i18nc(
"Recurs Every year on month-name [1st|2nd|...]",
4270 "Recurs yearly on %1 %2",
4271 calSys->monthName(recur->
startDate().month(),
4273 dayList[ recur->
startDate().day() + 31 ]);
4279 case Recurrence::rYearlyDay:
4280 if (!recur->
yearDays().isEmpty()) {
4282 recurStr = i18ncp(
"Recurs every N years on day N until end-date",
4283 "Recurs every year on day <numid>%2</numid> until %3",
4284 "Recurs every <numid>%1</numid> years"
4285 " on day <numid>%2</numid> until %3",
4288 recurEnd(incidence));
4290 recurStr += i18nc(
"number of occurrences",
4291 " (<numid>%1</numid> occurrences)",
4295 recurStr = i18ncp(
"Recurs every N YEAR[S] on day N",
4296 "Recurs every year on day <numid>%2</numid>",
4297 "Recurs every <numid>%1</numid> years"
4298 " on day <numid>%2</numid>",
4303 case Recurrence::rYearlyPos:
4308 recurStr = i18ncp(
"Every N years on the [2nd|3rd|...] weekdayname "
4309 "of monthname until end-date",
4310 "Every year on the %2 %3 of %4 until %5",
4311 "Every <numid>%1</numid> years on the %2 %3 of %4"
4314 dayList[rule.pos() + 31],
4315 calSys->weekDayName(rule.day(), KCalendarSystem::LongDayName),
4317 recurEnd(incidence));
4319 recurStr += i18nc(
"number of occurrences",
4320 " (<numid>%1</numid> occurrences)",
4324 recurStr = i18ncp(
"Every N years on the [2nd|3rd|...] weekdayname "
4326 "Every year on the %2 %3 of %4",
4327 "Every <numid>%1</numid> years on the %2 %3 of %4",
4329 dayList[rule.pos() + 31],
4330 calSys->weekDayName(rule.day(), KCalendarSystem::LongDayName),
4338 if (recurStr.isEmpty()) {
4339 recurStr = i18n(
"Incidence recurs");
4344 DateTimeList::ConstIterator il;
4346 for (il = l.constBegin(); il != l.constEnd(); ++il) {
4348 case Recurrence::rMinutely:
4349 exStr << i18n(
"minute %1", (*il).time().minute());
4351 case Recurrence::rHourly:
4352 exStr << KGlobal::locale()->formatTime((*il).time());
4354 case Recurrence::rDaily:
4355 exStr << KGlobal::locale()->formatDate((*il).date(), KLocale::ShortDate);
4357 case Recurrence::rWeekly:
4358 exStr << calSys->weekDayName((*il).date(), KCalendarSystem::ShortDayName);
4360 case Recurrence::rMonthlyPos:
4361 exStr << KGlobal::locale()->formatDate((*il).date(), KLocale::ShortDate);
4363 case Recurrence::rMonthlyDay:
4364 exStr << KGlobal::locale()->formatDate((*il).date(), KLocale::ShortDate);
4366 case Recurrence::rYearlyMonth:
4367 exStr << calSys->monthName((*il).date(), KCalendarSystem::LongName);
4369 case Recurrence::rYearlyDay:
4370 exStr << KGlobal::locale()->formatDate((*il).date(), KLocale::ShortDate);
4372 case Recurrence::rYearlyPos:
4373 exStr << KGlobal::locale()->formatDate((*il).date(), KLocale::ShortDate);
4379 DateList::ConstIterator dl;
4380 for (dl = d.constBegin(); dl != d.constEnd(); ++dl) {
4382 case Recurrence::rDaily:
4383 exStr << KGlobal::locale()->formatDate((*dl), KLocale::ShortDate);
4385 case Recurrence::rWeekly:
4388 if (exStr.isEmpty()) {
4389 exStr << i18np(
"1 day",
"%1 days", recur->exDates().count());
4392 case Recurrence::rMonthlyPos:
4393 exStr << KGlobal::locale()->formatDate((*dl), KLocale::ShortDate);
4395 case Recurrence::rMonthlyDay:
4396 exStr << KGlobal::locale()->formatDate((*dl), KLocale::ShortDate);
4398 case Recurrence::rYearlyMonth:
4399 exStr << calSys->monthName((*dl), KCalendarSystem::LongName);
4401 case Recurrence::rYearlyDay:
4402 exStr << KGlobal::locale()->formatDate((*dl), KLocale::ShortDate);
4404 case Recurrence::rYearlyPos:
4405 exStr << KGlobal::locale()->formatDate((*dl), KLocale::ShortDate);
4410 if (!exStr.isEmpty()) {
4411 recurStr = i18n(
"%1 (excluding %2)", recurStr, exStr.join(
","));
4419 const KDateTime::Spec &spec)
4421 if (spec.isValid()) {
4424 if (spec.timeZone() != KSystemTimeZones::local()) {
4425 timeZone =
' ' + spec.timeZone().name();
4428 return KGlobal::locale()->formatTime(date.toTimeSpec(spec).time(), !shortfmt) + timeZone;
4430 return KGlobal::locale()->formatTime(date.time(), !shortfmt);
4436 const KDateTime::Spec &spec)
4438 if (spec.isValid()) {
4441 if (spec.timeZone() != KSystemTimeZones::local()) {
4442 timeZone =
' ' + spec.timeZone().name();
4446 KGlobal::locale()->formatDate(date.toTimeSpec(spec).date(),
4447 (shortfmt ? KLocale::ShortDate : KLocale::LongDate)) +
4451 KGlobal::locale()->formatDate(date.date(),
4452 (shortfmt ? KLocale::ShortDate : KLocale::LongDate));
4459 const KDateTime::Spec &spec)
4465 if (spec.isValid()) {
4467 if (spec.timeZone() != KSystemTimeZones::local()) {
4468 timeZone =
' ' + spec.timeZone().name();
4471 return KGlobal::locale()->formatDateTime(
4472 date.toTimeSpec(spec).dateTime(),
4473 (shortfmt ? KLocale::ShortDate : KLocale::LongDate)) + timeZone;
4475 return KGlobal::locale()->formatDateTime(
4477 (shortfmt ? KLocale::ShortDate : KLocale::LongDate));
4482 const Incidence::Ptr &incidence)
4485 Q_UNUSED(incidence);
4489 static QString secs2Duration(
int secs)
4492 int days = secs / 86400;
4494 tmp += i18np(
"1 day",
"%1 days", days);
4496 secs -= (days * 86400);
4498 int hours = secs / 3600;
4500 tmp += i18np(
"1 hour",
"%1 hours", hours);
4502 secs -= (hours * 3600);
4504 int mins = secs / 60;
4506 tmp += i18np(
"1 minute",
"%1 minutes", mins);
4514 if (incidence->type() == Incidence::TypeEvent) {
4516 if (event->hasEndDate()) {
4517 if (!event->allDay()) {
4518 tmp = secs2Duration(event->dtStart().secsTo(event->dtEnd()));
4520 tmp = i18np(
"1 day",
"%1 days",
4521 event->dtStart().date().daysTo(event->dtEnd().date()) + 1);
4524 tmp = i18n(
"forever");
4526 }
else if (incidence->type() == Incidence::TypeTodo) {
4528 if (todo->hasDueDate()) {
4529 if (todo->hasStartDate()) {
4530 if (!todo->allDay()) {
4531 tmp = secs2Duration(todo->dtStart().secsTo(todo->dtDue()));
4533 tmp = i18np(
"1 day",
"%1 days",
4534 todo->dtStart().date().daysTo(todo->dtDue().date()) + 1);
4552 Alarm::List::ConstIterator it;
4553 for (it = alarms.constBegin(); it != alarms.constEnd(); ++it) {
4556 QString remStr, atStr, offsetStr;
4557 if (alarm->hasTime()) {
4559 if (alarm->time().isValid()) {
4560 atStr = KGlobal::locale()->formatDateTime(alarm->time());
4562 }
else if (alarm->hasStartOffset()) {
4563 offset = alarm->startOffset().asSeconds();
4566 offsetStr = i18nc(
"N days/hours/minutes before the start datetime",
4567 "%1 before the start", secs2Duration(offset));
4568 }
else if (offset > 0) {
4569 offsetStr = i18nc(
"N days/hours/minutes after the start datetime",
4570 "%1 after the start", secs2Duration(offset));
4572 if (incidence->dtStart().isValid()) {
4573 atStr = KGlobal::locale()->formatDateTime(incidence->dtStart());
4576 }
else if (alarm->hasEndOffset()) {
4577 offset = alarm->endOffset().asSeconds();
4580 if (incidence->type() == Incidence::TypeTodo) {
4581 offsetStr = i18nc(
"N days/hours/minutes before the due datetime",
4582 "%1 before the to-do is due", secs2Duration(offset));
4584 offsetStr = i18nc(
"N days/hours/minutes before the end datetime",
4585 "%1 before the end", secs2Duration(offset));
4587 }
else if (offset > 0) {
4588 if (incidence->type() == Incidence::TypeTodo) {
4589 offsetStr = i18nc(
"N days/hours/minutes after the due datetime",
4590 "%1 after the to-do is due", secs2Duration(offset));
4592 offsetStr = i18nc(
"N days/hours/minutes after the end datetime",
4593 "%1 after the end", secs2Duration(offset));
4596 if (incidence->type() == Incidence::TypeTodo) {
4598 if (t->dtDue().isValid()) {
4599 atStr = KGlobal::locale()->formatDateTime(t->dtDue());
4603 if (e->dtEnd().isValid()) {
4604 atStr = KGlobal::locale()->formatDateTime(e->dtEnd());
4610 if (!atStr.isEmpty()) {
4611 remStr = i18nc(
"reminder occurs at datetime",
"at %1", atStr);
4617 if (alarm->repeatCount() > 0) {
4618 QString countStr = i18np(
"repeats once",
"repeats %1 times", alarm->repeatCount());
4619 QString intervalStr = i18nc(
"interval is N days/hours/minutes",
4621 secs2Duration(alarm->snoozeTime().asSeconds()));
4622 QString repeatStr = i18nc(
"(repeat string, interval string)",
4623 "(%1, %2)", countStr, intervalStr);
4624 remStr = remStr +
' ' + repeatStr;
4627 reminderStringList << remStr;