45 #include <kcalcore/visitor.h>
46 using namespace KCalCore;
48 #include <kpimutils/email.h>
50 #include <KCalendarSystem>
52 #include <KEMailSettings>
53 #include <KIconLoader>
56 #include <KSystemTimeZone>
58 #include <QtCore/QBitArray>
59 #include <QApplication>
61 #include <QTextDocument>
63 using namespace KCalUtils;
64 using namespace IncidenceFormatter;
71 static QString htmlAddLink(
const QString &ref,
const QString &text,
74 QString tmpStr(
"<a href=\"" + ref +
"\">" + text +
"</a>" );
81 static QString htmlAddMailtoLink(
const QString &email,
const QString &name )
85 if ( !email.isEmpty() ) {
86 Person person( name, email );
87 QString path = person.fullName().simplified();
88 if ( path.isEmpty() || path.startsWith(
'"' ) ) {
92 mailto.setProtocol(
"mailto" );
93 mailto.setPath( path );
94 const QString iconPath =
95 KIconLoader::global()->iconPath(
"mail-message-new", KIconLoader::Small );
96 str = htmlAddLink( mailto.url(),
"<img valign=\"top\" src=\"" + iconPath +
"\">" );
101 static QString htmlAddUidLink(
const QString &email,
const QString &name,
const QString &uid )
105 if ( !uid.isEmpty() ) {
107 if ( name.isEmpty() ) {
109 str += htmlAddLink(
"uid:" + uid, email );
111 str += htmlAddLink(
"uid:" + uid, name );
117 static QString htmlAddTag(
const QString &tag,
const QString &text )
119 int numLineBreaks = text.count(
"\n" );
120 QString str =
'<' + tag +
'>';
121 QString tmpText = text;
122 QString tmpStr = str;
123 if( numLineBreaks >= 0 ) {
124 if ( numLineBreaks > 0 ) {
127 for (
int i = 0; i <= numLineBreaks; ++i ) {
128 pos = tmpText.indexOf(
"\n" );
129 tmp = tmpText.left( pos );
130 tmpText = tmpText.right( tmpText.length() - pos - 1 );
131 tmpStr += tmp +
"<br>";
137 tmpStr +=
"</" + tag +
'>';
141 static QPair<QString, QString> searchNameAndUid(
const QString &email,
const QString &name,
147 QPair<QString, QString>s;
150 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
156 static QString searchName(
const QString &email,
const QString &name )
158 const QString printName = name.isEmpty() ? email : name;
167 KEMailSettings settings;
168 QStringList profiles = settings.profiles();
169 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
170 settings.setProfile( *it );
171 if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
179 static bool iamOrganizer( Incidence::Ptr incidence )
188 KEMailSettings settings;
189 QStringList profiles = settings.profiles();
190 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
191 settings.setProfile( *it );
192 if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer()->email() ) {
200 static bool senderIsOrganizer( Incidence::Ptr incidence,
const QString &sender )
204 if ( !incidence || sender.isEmpty() ) {
209 QString senderName, senderEmail;
210 if ( KPIMUtils::extractEmailAddressAndName( sender, senderEmail, senderName ) ) {
212 if ( incidence->organizer()->email() != senderEmail &&
213 incidence->organizer()->name() != senderName ) {
220 static bool attendeeIsOrganizer(
const Incidence::Ptr &incidence,
const Attendee::Ptr &attendee )
222 if ( incidence && attendee &&
223 ( incidence->organizer()->email() == attendee->email() ) ) {
230 static QString organizerName(
const Incidence::Ptr incidence,
const QString &defName )
233 if ( !defName.isEmpty() ) {
236 tName = i18n(
"Organizer Unknown" );
241 name = incidence->organizer()->name();
242 if ( name.isEmpty() ) {
243 name = incidence->organizer()->email();
246 if ( name.isEmpty() ) {
252 static QString firstAttendeeName(
const Incidence::Ptr &incidence,
const QString &defName )
255 if ( !defName.isEmpty() ) {
258 tName = i18n(
"Sender" );
264 if( attendees.count() > 0 ) {
266 name = attendee->name();
267 if ( name.isEmpty() ) {
268 name = attendee->email();
272 if ( name.isEmpty() ) {
283 iconPath = KIconLoader::global()->iconPath(
"dialog-ok-apply", KIconLoader::Small );
286 iconPath = KIconLoader::global()->iconPath(
"dialog-cancel", KIconLoader::Small );
289 iconPath = KIconLoader::global()->iconPath(
"help-about", KIconLoader::Small );
292 iconPath = KIconLoader::global()->iconPath(
"help-about", KIconLoader::Small );
295 iconPath = KIconLoader::global()->iconPath(
"dialog-ok", KIconLoader::Small );
298 iconPath = KIconLoader::global()->iconPath(
"mail-forward", KIconLoader::Small );
301 iconPath = KIconLoader::global()->iconPath(
"mail-mark-read", KIconLoader::Small );
315 static QString displayViewFormatPerson(
const QString &email,
const QString &name,
316 const QString &uid,
const QString &iconPath )
319 QPair<QString, QString> s = searchNameAndUid( email, name, uid );
320 const QString printName = s.first;
321 const QString printUid = s.second;
323 QString personString;
324 if ( !iconPath.isEmpty() ) {
325 personString +=
"<img valign=\"top\" src=\"" + iconPath +
"\">" +
" ";
329 if ( !printUid.isEmpty() ) {
330 personString += htmlAddUidLink( email, printName, printUid );
333 personString += ( printName.isEmpty() ? email : printName );
336 #ifndef KDEPIM_MOBILE_UI
338 if ( !email.isEmpty() ) {
339 personString +=
" " + htmlAddMailtoLink( email, printName );
346 static QString displayViewFormatPerson(
const QString &email,
const QString &name,
349 return displayViewFormatPerson( email, name, uid, rsvpStatusIconPath( status ) );
352 static bool incOrganizerOwnsCalendar(
const Calendar::Ptr &calendar,
353 const Incidence::Ptr &incidence )
358 Q_UNUSED( calendar );
359 return iamOrganizer( incidence );
362 static QString displayViewFormatAttendeeRoleList( Incidence::Ptr incidence,
Attendee::Role role,
366 Attendee::List::ConstIterator it;
369 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
371 if ( a->role() != role ) {
375 if ( attendeeIsOrganizer( incidence, a ) ) {
379 tmpStr += displayViewFormatPerson( a->email(), a->name(), a->uid(),
381 if ( !a->delegator().isEmpty() ) {
382 tmpStr += i18n(
" (delegated by %1)", a->delegator() );
384 if ( !a->delegate().isEmpty() ) {
385 tmpStr += i18n(
" (delegated to %1)", a->delegate() );
389 if ( tmpStr.endsWith( QLatin1String(
"<br>" ) ) ) {
395 static QString displayViewFormatAttendees(
Calendar::Ptr calendar, Incidence::Ptr incidence )
400 int attendeeCount = incidence->attendees().count();
401 if ( attendeeCount > 1 ||
402 ( attendeeCount == 1 &&
403 !attendeeIsOrganizer( incidence, incidence->attendees().first() ) ) ) {
405 QPair<QString, QString> s = searchNameAndUid( incidence->organizer()->email(),
406 incidence->organizer()->name(),
409 tmpStr +=
"<td><b>" + i18n(
"Organizer:" ) +
"</b></td>";
410 const QString iconPath =
411 KIconLoader::global()->iconPath(
"meeting-organizer", KIconLoader::Small );
412 tmpStr +=
"<td>" + displayViewFormatPerson( incidence->organizer()->email(),
413 s.first, s.second, iconPath ) +
420 bool showStatus = incOrganizerOwnsCalendar( calendar, incidence );
423 str = displayViewFormatAttendeeRoleList( incidence,
Attendee::Chair, showStatus );
424 if ( !str.isEmpty() ) {
426 tmpStr +=
"<td><b>" + i18n(
"Chair:" ) +
"</b></td>";
427 tmpStr +=
"<td>" + str +
"</td>";
433 if ( !str.isEmpty() ) {
435 tmpStr +=
"<td><b>" + i18n(
"Required Participants:" ) +
"</b></td>";
436 tmpStr +=
"<td>" + str +
"</td>";
442 if ( !str.isEmpty() ) {
444 tmpStr +=
"<td><b>" + i18n(
"Optional Participants:" ) +
"</b></td>";
445 tmpStr +=
"<td>" + str +
"</td>";
451 if ( !str.isEmpty() ) {
453 tmpStr +=
"<td><b>" + i18n(
"Observers:" ) +
"</b></td>";
454 tmpStr +=
"<td>" + str +
"</td>";
461 static QString displayViewFormatAttachments( Incidence::Ptr incidence )
465 Attachment::List::ConstIterator it;
467 for ( it = as.constBegin(); it != as.constEnd(); ++it ) {
469 if ( (*it)->isUri() ) {
471 if ( (*it)->uri().startsWith( QLatin1String(
"kmail:" ) ) ) {
472 name = i18n(
"Show mail" );
474 if ( (*it)->label().isEmpty() ) {
477 name = (*it)->label();
480 tmpStr += htmlAddLink( (*it)->uri(), name );
482 tmpStr += htmlAddLink( QString::fromLatin1(
"ATTACH:%1" ).
483 arg( QString::fromUtf8( (*it)->label().toUtf8().toBase64() ) ),
486 if ( count < as.count() ) {
493 static QString displayViewFormatCategories( Incidence::Ptr incidence )
496 return incidence->categories().join(
", " );
499 static QString displayViewFormatCreationDate( Incidence::Ptr incidence, KDateTime::Spec spec )
501 KDateTime kdt = incidence->created().toTimeSpec( spec );
502 return i18n(
"Creation date: %1",
dateTimeToString( incidence->created(),
false,
true, spec ) );
505 static QString displayViewFormatBirthday(
Event::Ptr event )
510 if ( event->customProperty(
"KABC",
"BIRTHDAY" ) !=
"YES" &&
511 event->customProperty(
"KABC",
"ANNIVERSARY" ) !=
"YES" ) {
515 QString uid_1 =
event->customProperty(
"KABC",
"UID-1" );
516 QString name_1 =
event->customProperty(
"KABC",
"NAME-1" );
517 QString email_1=
event->customProperty(
"KABC",
"EMAIL-1" );
519 QString tmpStr = displayViewFormatPerson( email_1, name_1, uid_1, QString() );
523 static QString displayViewFormatHeader( Incidence::Ptr incidence )
525 QString tmpStr =
"<table><tr>";
528 KIconLoader *iconLoader = KIconLoader::global();
532 if ( incidence->customProperty(
"KABC",
"BIRTHDAY" ) ==
"YES" ) {
533 iconPath = iconLoader->iconPath(
"view-calendar-birthday", KIconLoader::Small );
534 }
else if ( incidence->customProperty(
"KABC",
"ANNIVERSARY" ) ==
"YES" ) {
535 iconPath = iconLoader->iconPath(
"view-calendar-wedding-anniversary", KIconLoader::Small );
537 iconPath = iconLoader->iconPath( incidence->iconName(), KIconLoader::Small );
539 tmpStr +=
"<img valign=\"top\" src=\"" + iconPath +
"\">";
541 if ( incidence->hasEnabledAlarms() ) {
542 tmpStr +=
"<img valign=\"top\" src=\"" +
543 iconLoader->iconPath(
"preferences-desktop-notification-bell", KIconLoader::Small ) +
546 if ( incidence->recurs() ) {
547 tmpStr +=
"<img valign=\"top\" src=\"" +
548 iconLoader->iconPath(
"edit-redo", KIconLoader::Small ) +
551 if ( incidence->isReadOnly() ) {
552 tmpStr +=
"<img valign=\"top\" src=\"" +
553 iconLoader->iconPath(
"object-locked", KIconLoader::Small ) +
559 tmpStr +=
"<b><u>" + incidence->richSummary() +
"</u></b>";
562 tmpStr +=
"</tr></table>";
567 static QString displayViewFormatEvent(
const Calendar::Ptr calendar,
const QString &sourceName,
569 const QDate &date, KDateTime::Spec spec )
575 QString tmpStr = displayViewFormatHeader( event );
578 tmpStr +=
"<col width=\"25%\"/>";
579 tmpStr +=
"<col width=\"75%\"/>";
581 const QString calStr = calendar ?
resourceString( calendar, event ) : sourceName;
582 if ( !calStr.isEmpty() ) {
584 tmpStr +=
"<td><b>" + i18n(
"Calendar:" ) +
"</b></td>";
585 tmpStr +=
"<td>" + calStr +
"</td>";
589 if ( !event->location().isEmpty() ) {
591 tmpStr +=
"<td><b>" + i18n(
"Location:" ) +
"</b></td>";
592 tmpStr +=
"<td>" +
event->richLocation() +
"</td>";
596 KDateTime startDt =
event->dtStart();
597 KDateTime endDt =
event->dtEnd();
598 if ( event->recurs() ) {
599 if ( date.isValid() ) {
600 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
601 int diffDays = startDt.daysTo( kdt );
602 kdt = kdt.addSecs( -1 );
603 startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
604 if ( event->hasEndDate() ) {
605 endDt = endDt.addDays( diffDays );
606 if ( startDt > endDt ) {
607 startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
608 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
615 if ( event->allDay() ) {
616 if ( event->isMultiDay() ) {
617 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
619 i18nc(
"<beginTime> - <endTime>",
"%1 - %2",
624 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
626 i18nc(
"date as string",
"%1",
631 if ( event->isMultiDay() ) {
632 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
634 i18nc(
"<beginTime> - <endTime>",
"%1 - %2",
639 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
641 i18nc(
"date as string",
"%1",
645 tmpStr +=
"</tr><tr>";
646 tmpStr +=
"<td><b>" + i18n(
"Time:" ) +
"</b></td>";
647 if ( event->hasEndDate() && startDt != endDt ) {
649 i18nc(
"<beginTime> - <endTime>",
"%1 - %2",
663 if ( !durStr.isEmpty() ) {
665 tmpStr +=
"<td><b>" + i18n(
"Duration:" ) +
"</b></td>";
666 tmpStr +=
"<td>" + durStr +
"</td>";
670 if ( event->recurs() ) {
672 tmpStr +=
"<td><b>" + i18n(
"Recurrence:" ) +
"</b></td>";
679 const bool isBirthday =
event->customProperty(
"KABC",
"BIRTHDAY" ) ==
"YES";
680 const bool isAnniversary =
event->customProperty(
"KABC",
"ANNIVERSARY" ) ==
"YES";
682 if ( isBirthday || isAnniversary ) {
684 if ( isAnniversary ) {
685 tmpStr +=
"<td><b>" + i18n(
"Anniversary:" ) +
"</b></td>";
687 tmpStr +=
"<td><b>" + i18n(
"Birthday:" ) +
"</b></td>";
689 tmpStr +=
"<td>" + displayViewFormatBirthday( event ) +
"</td>";
691 tmpStr +=
"</table>";
695 if ( !event->description().isEmpty() ) {
697 tmpStr +=
"<td><b>" + i18n(
"Description:" ) +
"</b></td>";
698 tmpStr +=
"<td>" +
event->richDescription() +
"</td>";
704 int reminderCount =
event->alarms().count();
705 if ( reminderCount > 0 && event->hasEnabledAlarms() ) {
707 tmpStr +=
"<td><b>" +
708 i18np(
"Reminder:",
"Reminders:", reminderCount ) +
714 tmpStr += displayViewFormatAttendees( calendar, event );
716 int categoryCount =
event->categories().count();
717 if ( categoryCount > 0 ) {
720 tmpStr += i18np(
"Category:",
"Categories:", categoryCount ) +
722 tmpStr +=
"<td>" + displayViewFormatCategories( event ) +
"</td>";
726 int attachmentCount =
event->attachments().count();
727 if ( attachmentCount > 0 ) {
729 tmpStr +=
"<td><b>" +
730 i18np(
"Attachment:",
"Attachments:", attachmentCount ) +
732 tmpStr +=
"<td>" + displayViewFormatAttachments( event ) +
"</td>";
735 tmpStr +=
"</table>";
737 tmpStr +=
"<p><em>" + displayViewFormatCreationDate( event, spec ) +
"</em>";
742 static QString displayViewFormatTodo(
const Calendar::Ptr &calendar,
const QString &sourceName,
744 const QDate &date, KDateTime::Spec spec )
747 kDebug() <<
"IncidenceFormatter::displayViewFormatTodo was called without to-do, quitting";
751 QString tmpStr = displayViewFormatHeader( todo );
754 tmpStr +=
"<col width=\"25%\"/>";
755 tmpStr +=
"<col width=\"75%\"/>";
757 const QString calStr = calendar ?
resourceString( calendar, todo ) : sourceName;
758 if ( !calStr.isEmpty() ) {
760 tmpStr +=
"<td><b>" + i18n(
"Calendar:" ) +
"</b></td>";
761 tmpStr +=
"<td>" + calStr +
"</td>";
765 if ( !todo->location().isEmpty() ) {
767 tmpStr +=
"<td><b>" + i18n(
"Location:" ) +
"</b></td>";
768 tmpStr +=
"<td>" + todo->richLocation() +
"</td>";
772 const bool hastStartDate = todo->hasStartDate() && todo->dtStart().isValid();
773 const bool hasDueDate = todo->hasDueDate() && todo->dtDue().isValid();
775 if ( hastStartDate ) {
776 KDateTime startDt = todo->dtStart(
true );
777 if ( todo->recurs() ) {
778 if ( date.isValid() ) {
781 const int length = startDt.daysTo( todo->dtDue(
true ) );
783 startDt.setDate( date.addDays( -length ) );
785 kError() <<
"DTSTART is bigger than DTDUE, todo->uid() is " << todo->uid();
786 startDt.setDate( date );
789 kError() <<
"To-do is recurring but has no DTDUE set, todo->uid() is " << todo->uid();
790 startDt.setDate( date );
795 tmpStr +=
"<td><b>" +
796 i18nc(
"to-do start date/time",
"Start:" ) +
805 KDateTime dueDt = todo->dtDue();
806 if ( todo->recurs() ) {
807 if ( date.isValid() ) {
808 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
809 kdt = kdt.addSecs( -1 );
810 dueDt.setDate( todo->recurrence()->getNextDateTime( kdt ).date() );
814 tmpStr +=
"<td><b>" +
815 i18nc(
"to-do due date/time",
"Due:" ) +
824 if ( !durStr.isEmpty() ) {
826 tmpStr +=
"<td><b>" + i18n(
"Duration:" ) +
"</b></td>";
827 tmpStr +=
"<td>" + durStr +
"</td>";
831 if ( todo->recurs() ) {
833 tmpStr +=
"<td><b>" + i18n(
"Recurrence:" ) +
"</b></td>";
840 if ( !todo->description().isEmpty() ) {
842 tmpStr +=
"<td><b>" + i18n(
"Description:" ) +
"</b></td>";
843 tmpStr +=
"<td>" + todo->richDescription() +
"</td>";
849 int reminderCount = todo->alarms().count();
850 if ( reminderCount > 0 && todo->hasEnabledAlarms() ) {
852 tmpStr +=
"<td><b>" +
853 i18np(
"Reminder:",
"Reminders:", reminderCount ) +
859 tmpStr += displayViewFormatAttendees( calendar, todo );
861 int categoryCount = todo->categories().count();
862 if ( categoryCount > 0 ) {
864 tmpStr +=
"<td><b>" +
865 i18np(
"Category:",
"Categories:", categoryCount ) +
867 tmpStr +=
"<td>" + displayViewFormatCategories( todo ) +
"</td>";
871 if ( todo->priority() > 0 ) {
873 tmpStr +=
"<td><b>" + i18n(
"Priority:" ) +
"</b></td>";
875 tmpStr += QString::number( todo->priority() );
881 if ( todo->isCompleted() ) {
882 tmpStr +=
"<td><b>" + i18nc(
"Completed: date",
"Completed:" ) +
"</b></td>";
886 tmpStr +=
"<td><b>" + i18n(
"Percent Done:" ) +
"</b></td>";
888 tmpStr += i18n(
"%1%", todo->percentComplete() );
893 int attachmentCount = todo->attachments().count();
894 if ( attachmentCount > 0 ) {
896 tmpStr +=
"<td><b>" +
897 i18np(
"Attachment:",
"Attachments:", attachmentCount ) +
899 tmpStr +=
"<td>" + displayViewFormatAttachments( todo ) +
"</td>";
902 tmpStr +=
"</table>";
904 tmpStr +=
"<p><em>" + displayViewFormatCreationDate( todo, spec ) +
"</em>";
909 static QString displayViewFormatJournal(
const Calendar::Ptr &calendar,
const QString &sourceName,
916 QString tmpStr = displayViewFormatHeader( journal );
919 tmpStr +=
"<col width=\"25%\"/>";
920 tmpStr +=
"<col width=\"75%\"/>";
922 const QString calStr = calendar ?
resourceString( calendar, journal ) : sourceName;
923 if ( !calStr.isEmpty() ) {
925 tmpStr +=
"<td><b>" + i18n(
"Calendar:" ) +
"</b></td>";
926 tmpStr +=
"<td>" + calStr +
"</td>";
931 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
937 if ( !journal->description().isEmpty() ) {
939 tmpStr +=
"<td><b>" + i18n(
"Description:" ) +
"</b></td>";
940 tmpStr +=
"<td>" + journal->richDescription() +
"</td>";
944 int categoryCount = journal->categories().count();
945 if ( categoryCount > 0 ) {
947 tmpStr +=
"<td><b>" +
948 i18np(
"Category:",
"Categories:", categoryCount ) +
950 tmpStr +=
"<td>" + displayViewFormatCategories( journal ) +
"</td>";
954 tmpStr +=
"</table>";
956 tmpStr +=
"<p><em>" + displayViewFormatCreationDate( journal, spec ) +
"</em>";
961 static QString displayViewFormatFreeBusy(
const Calendar::Ptr &calendar,
const QString &sourceName,
964 Q_UNUSED( calendar );
965 Q_UNUSED( sourceName );
972 "h2", i18n(
"Free/Busy information for %1", fb->organizer()->fullName() ) ) );
974 tmpStr += htmlAddTag(
"h4",
975 i18n(
"Busy times in date range %1 - %2:",
981 htmlAddTag(
"b", i18nc(
"tag for busy periods list",
"Busy:" ) ) );
983 Period::List periods = fb->busyPeriods();
984 Period::List::iterator it;
985 for ( it = periods.begin(); it != periods.end(); ++it ) {
987 if ( per.hasDuration() ) {
988 int dur = per.duration().asSeconds();
991 cont += i18ncp(
"hours part of duration",
"1 hour ",
"%1 hours ", dur / 3600 );
995 cont += i18ncp(
"minutes part duration",
"1 minute ",
"%1 minutes ", dur / 60 );
999 cont += i18ncp(
"seconds part of duration",
"1 second",
"%1 seconds", dur );
1001 text += i18nc(
"startDate for duration",
"%1 for %2",
1006 if ( per.start().date() == per.end().date() ) {
1007 text += i18nc(
"date, fromTime - toTime ",
"%1, %2 - %3",
1012 text += i18nc(
"fromDateTime - toDateTime",
"%1 - %2",
1019 tmpStr += htmlAddTag(
"p", text );
1025 class KCalUtils::IncidenceFormatter::EventViewerVisitor :
public Visitor
1028 EventViewerVisitor()
1029 : mCalendar( 0 ), mSpec( KDateTime::Spec() ), mResult(
"" ) {}
1031 bool act(
const Calendar::Ptr &calendar, IncidenceBase::Ptr incidence,
const QDate &date,
1032 KDateTime::Spec spec=KDateTime::Spec() )
1034 mCalendar = calendar;
1035 mSourceName.clear();
1039 return incidence->accept( *
this, incidence );
1042 bool act(
const QString &sourceName, IncidenceBase::Ptr incidence,
const QDate &date,
1043 KDateTime::Spec spec=KDateTime::Spec() )
1045 mSourceName = sourceName;
1049 return incidence->accept( *
this, incidence );
1052 QString result()
const {
return mResult; }
1057 mResult = displayViewFormatEvent( mCalendar, mSourceName, event, mDate, mSpec );
1058 return !mResult.isEmpty();
1062 mResult = displayViewFormatTodo( mCalendar, mSourceName, todo, mDate, mSpec );
1063 return !mResult.isEmpty();
1067 mResult = displayViewFormatJournal( mCalendar, mSourceName, journal, mSpec );
1068 return !mResult.isEmpty();
1072 mResult = displayViewFormatFreeBusy( mCalendar, mSourceName, fb, mSpec );
1073 return !mResult.isEmpty();
1078 QString mSourceName;
1080 KDateTime::Spec mSpec;
1086 const IncidenceBase::Ptr &incidence,
1088 KDateTime::Spec spec )
1094 EventViewerVisitor v;
1095 if ( v.act( calendar, incidence, date, spec ) ) {
1103 const IncidenceBase::Ptr &incidence,
1105 KDateTime::Spec spec )
1111 EventViewerVisitor v;
1112 if ( v.act( sourceName, incidence, date, spec ) ) {
1123 static QString string2HTML(
const QString &str )
1125 return Qt::convertFromPlainText( str, Qt::WhiteSpaceNormal );
1128 static QString cleanHtml(
const QString &html )
1130 QRegExp rx(
"<body[^>]*>(.*)</body>", Qt::CaseInsensitive );
1132 QString body = rx.cap( 1 );
1134 return Qt::escape( body.remove( QRegExp(
"<[^>]*>" ) ).trimmed() );
1137 static QString invitationSummary(
const Incidence::Ptr &incidence,
bool noHtmlMode )
1139 QString summaryStr = i18n(
"Summary unspecified" );
1140 if ( !incidence->summary().isEmpty() ) {
1141 if ( !incidence->summaryIsRich() ) {
1142 summaryStr = Qt::escape( incidence->summary() );
1144 summaryStr = incidence->richSummary();
1146 summaryStr = cleanHtml( summaryStr );
1153 static QString invitationLocation(
const Incidence::Ptr &incidence,
bool noHtmlMode )
1155 QString locationStr = i18n(
"Location unspecified" );
1156 if ( !incidence->location().isEmpty() ) {
1157 if ( !incidence->locationIsRich() ) {
1158 locationStr = Qt::escape( incidence->location() );
1160 locationStr = incidence->richLocation();
1162 locationStr = cleanHtml( locationStr );
1169 static QString eventStartTimeStr(
const Event::Ptr &event )
1172 if ( !event->allDay() ) {
1173 tmp = i18nc(
"%1: Start Date, %2: Start Time",
"%1 %2",
1174 dateToString( event->dtStart(),
true, KSystemTimeZones::local() ),
1175 timeToString( event->dtStart(),
true, KSystemTimeZones::local() ) );
1177 tmp = i18nc(
"%1: Start Date",
"%1 (all day)",
1178 dateToString( event->dtStart(),
true, KSystemTimeZones::local() ) );
1183 static QString eventEndTimeStr(
const Event::Ptr &event )
1186 if ( event->hasEndDate() &&
event->dtEnd().isValid() ) {
1187 if ( !event->allDay() ) {
1188 tmp = i18nc(
"%1: End Date, %2: End Time",
"%1 %2",
1189 dateToString( event->dtEnd(),
true, KSystemTimeZones::local() ),
1190 timeToString( event->dtEnd(),
true, KSystemTimeZones::local() ) );
1192 tmp = i18nc(
"%1: End Date",
"%1 (all day)",
1193 dateToString( event->dtEnd(),
true, KSystemTimeZones::local() ) );
1199 static QString htmlInvitationDetailsBegin()
1201 QString dir = ( QApplication::isRightToLeft() ?
"rtl" :
"ltr" );
1202 return QString(
"<div dir=\"%1\">\n" ).arg( dir );
1205 static QString htmlInvitationDetailsEnd()
1210 static QString htmlInvitationDetailsTableBegin()
1212 return "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
1215 static QString htmlInvitationDetailsTableEnd()
1217 return "</table>\n";
1220 static QString diffColor()
1225 return QColor( Qt::red ).name();
1228 static QString noteColor()
1231 return qApp->palette().color( QPalette::Active, QPalette::Highlight ).name();
1234 static QString htmlRow(
const QString &title,
const QString &value )
1236 if ( !value.isEmpty() ) {
1237 return "<tr><td>" + title +
"</td><td>" + value +
"</td></tr>\n";
1243 static QString htmlRow(
const QString &title,
const QString &value,
const QString &oldvalue )
1246 if ( value.isEmpty() ) {
1251 if ( oldvalue.isEmpty() || value == oldvalue ) {
1252 return htmlRow( title, value );
1256 QString color = diffColor();
1257 QString newtitle =
"<font color=\"" + color +
"\">" + title +
"</font>";
1258 QString newvalue =
"<font color=\"" + color +
"\">" + value +
"</font>" +
1260 "(<strike>" + oldvalue +
"</strike>)";
1261 return htmlRow( newtitle, newvalue );
1265 static Attendee::Ptr findDelegatedFromMyAttendee(
const Incidence::Ptr &incidence )
1274 KEMailSettings settings;
1275 QStringList profiles = settings.profiles();
1276 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
1277 settings.setProfile( *it );
1279 QString delegatorName, delegatorEmail;
1281 Attendee::List::ConstIterator it2;
1282 for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) {
1284 KPIMUtils::extractEmailAddressAndName( a->delegator(), delegatorEmail, delegatorName );
1285 if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) {
1294 static Attendee::Ptr findMyAttendee(
const Incidence::Ptr &incidence )
1303 KEMailSettings settings;
1304 QStringList profiles = settings.profiles();
1305 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
1306 settings.setProfile( *it );
1309 Attendee::List::ConstIterator it2;
1310 for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) {
1312 if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
1321 static Attendee::Ptr findAttendee(
const Incidence::Ptr &incidence,
1322 const QString &email )
1332 Attendee::List::ConstIterator it;
1333 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
1335 if ( email == a->email() ) {
1343 static bool rsvpRequested(
const Incidence::Ptr &incidence )
1353 Attendee::List::ConstIterator it;
1354 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
1355 if ( it == attendees.constBegin() ) {
1356 rsvp = (*it)->RSVP();
1358 if ( (*it)->RSVP() != rsvp ) {
1367 static QString rsvpRequestedStr(
bool rsvpRequested,
const QString &role )
1369 if ( rsvpRequested ) {
1370 if ( role.isEmpty() ) {
1371 return i18n(
"Your response is requested" );
1373 return i18n(
"Your response as <b>%1</b> is requested", role );
1376 if ( role.isEmpty() ) {
1377 return i18n(
"No response is necessary" );
1379 return i18n(
"No response as <b>%1</b> is necessary", role );
1384 static QString myStatusStr( Incidence::Ptr incidence )
1390 ret = i18n(
"(<b>Note</b>: the Organizer preset your response to <b>%1</b>)",
1391 Stringify::attendeeStatus( a->status() ) );
1396 static QString invitationNote(
const QString &title,
const QString ¬e,
1397 const QString &tag,
const QString &color )
1400 if ( !note.isEmpty() ) {
1401 noteStr +=
"<table border=\"0\" style=\"margin-top:4px;\">";
1402 noteStr +=
"<tr><center><td>";
1403 if ( !color.isEmpty() ) {
1404 noteStr +=
"<font color=\"" + color +
"\">";
1406 if ( !title.isEmpty() ) {
1407 if ( !tag.isEmpty() ) {
1408 noteStr += htmlAddTag( tag, title );
1413 noteStr +=
" " + note;
1414 if ( !color.isEmpty() ) {
1415 noteStr +=
"</font>";
1417 noteStr +=
"</td></center></tr>";
1418 noteStr +=
"</table>";
1423 static QString invitationPerson(
const QString &email,
const QString &name,
const QString &uid,
1424 const QString &comment )
1426 QPair<QString, QString> s = searchNameAndUid( email, name, uid );
1427 const QString printName = s.first;
1428 const QString printUid = s.second;
1430 QString personString;
1432 if ( !printUid.isEmpty() ) {
1433 personString = htmlAddUidLink( email, printName, printUid );
1436 personString = ( printName.isEmpty() ? email : printName );
1438 if ( !comment.isEmpty() ) {
1439 personString = i18nc(
"name (comment)",
"%1 (%2)", personString, comment );
1441 personString +=
'\n';
1444 if ( !email.isEmpty() ) {
1445 personString +=
" " + htmlAddMailtoLink( email, printName );
1447 personString +=
'\n';
1449 return personString;
1452 static QString invitationDetailsIncidence(
const Incidence::Ptr &incidence,
bool noHtmlMode )
1460 QStringList comments;
1462 if ( incidence->comments().isEmpty() ) {
1463 if ( !incidence->description().isEmpty() ) {
1465 if ( !incidence->descriptionIsRich() &&
1466 !incidence->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) ) {
1467 comments << string2HTML( incidence->description() );
1469 if ( !incidence->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) ) {
1470 comments << incidence->richDescription();
1472 comments << incidence->description();
1475 comments[0] = cleanHtml( comments[0] );
1477 comments[0] = htmlAddTag(
"p", comments[0] );
1483 foreach (
const QString &c, incidence->comments() ) {
1484 if ( !c.isEmpty() ) {
1486 if ( !Qt::mightBeRichText( c ) ) {
1487 comments << string2HTML( c );
1490 comments << cleanHtml( cleanHtml(
"<body>" + c +
"</body>" ) );
1497 if ( !incidence->description().isEmpty() ) {
1499 if ( !incidence->descriptionIsRich() &&
1500 !incidence->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) ) {
1501 descr = string2HTML( incidence->description() );
1503 if ( !incidence->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) ) {
1504 descr = incidence->richDescription();
1506 descr = incidence->description();
1509 descr = cleanHtml( descr );
1511 descr = htmlAddTag(
"p", descr );
1516 if( !descr.isEmpty() ) {
1518 html +=
"<table border=\"0\" style=\"margin-top:4px;\">";
1519 html +=
"<tr><td><center>" +
1520 htmlAddTag(
"u", i18n(
"Description:" ) ) +
1521 "</center></td></tr>";
1522 html +=
"<tr><td>" + descr +
"</td></tr>";
1526 if ( !comments.isEmpty() ) {
1528 html +=
"<table border=\"0\" style=\"margin-top:4px;\">";
1529 html +=
"<tr><td><center>" +
1530 htmlAddTag(
"u", i18n(
"Comments:" ) ) +
1531 "</center></td></tr>";
1533 if ( comments.count() > 1 ) {
1535 for (
int i=0; i < comments.count(); ++i ) {
1536 html +=
"<li>" + comments[i] +
"</li>";
1540 html += comments[0];
1542 html +=
"</td></tr>";
1548 static QString invitationDetailsEvent(
const Event::Ptr &event,
bool noHtmlMode,
1549 KDateTime::Spec spec )
1556 QString html = htmlInvitationDetailsBegin();
1557 html += htmlInvitationDetailsTableBegin();
1560 html += htmlRow( i18n(
"What:" ), invitationSummary( event, noHtmlMode ) );
1561 html += htmlRow( i18n(
"Where:" ), invitationLocation( event, noHtmlMode ) );
1564 if ( event->dtStart().date() ==
event->dtEnd().date() ) {
1565 html += htmlRow( i18n(
"Date:" ),
dateToString( event->dtStart(),
false, spec ) );
1566 if ( !event->allDay() ) {
1567 html += htmlRow( i18n(
"Time:" ),
1573 html += htmlRow( i18nc(
"starting date",
"From:" ),
1575 if ( !event->allDay() ) {
1576 html += htmlRow( i18nc(
"starting time",
"At:" ),
1579 if ( event->hasEndDate() ) {
1580 html += htmlRow( i18nc(
"ending date",
"To:" ),
1582 if ( !event->allDay() ) {
1583 html += htmlRow( i18nc(
"ending time",
"At:" ),
1587 html += htmlRow( i18nc(
"ending date",
"To:" ), i18n(
"no end date specified" ) );
1595 if ( event->recurs() ) {
1599 html += htmlInvitationDetailsTableEnd();
1600 html += invitationDetailsIncidence( event, noHtmlMode );
1601 html += htmlInvitationDetailsEnd();
1608 KDateTime::Spec spec )
1611 return invitationDetailsEvent( event, noHtmlMode, spec );
1619 html += invitationNote( QString(),
1620 i18n(
"Please respond again to the original proposal." ),
1621 QString(), noteColor() );
1624 html += htmlInvitationDetailsBegin();
1625 html += htmlInvitationDetailsTableBegin();
1627 html += htmlRow( i18n(
"What:" ),
1628 invitationSummary( event, noHtmlMode ),
1629 invitationSummary( oldevent, noHtmlMode ) );
1631 html += htmlRow( i18n(
"Where:" ),
1632 invitationLocation( event, noHtmlMode ),
1633 invitationLocation( oldevent, noHtmlMode ) );
1636 if ( event->dtStart().date() ==
event->dtEnd().date() ) {
1637 html += htmlRow( i18n(
"Date:" ),
1640 QString spanStr, oldspanStr;
1641 if ( !event->allDay() ) {
1646 if ( !oldevent->allDay() ) {
1647 oldspanStr =
timeToString( oldevent->dtStart(), true ) +
1651 html += htmlRow( i18n(
"Time:" ), spanStr, oldspanStr );
1653 html += htmlRow( i18nc(
"Starting date of an event",
"From:" ),
1656 QString startStr, oldstartStr;
1657 if ( !event->allDay() ) {
1660 if ( !oldevent->allDay() ) {
1661 oldstartStr =
timeToString( oldevent->dtStart(), true );
1663 html += htmlRow( i18nc(
"Starting time of an event",
"At:" ), startStr, oldstartStr );
1664 if ( event->hasEndDate() ) {
1665 html += htmlRow( i18nc(
"Ending date of an event",
"To:" ),
1668 QString endStr, oldendStr;
1669 if ( !event->allDay() ) {
1672 if ( !oldevent->allDay() ) {
1675 html += htmlRow( i18nc(
"Starting time of an event",
"At:" ), endStr, oldendStr );
1677 QString endStr = i18n(
"no end date specified" );
1679 if ( !oldevent->hasEndDate() ) {
1680 oldendStr = i18n(
"no end date specified" );
1682 oldendStr =
dateTimeToString( oldevent->dtEnd(), oldevent->allDay(), false );
1684 html += htmlRow( i18nc(
"Ending date of an event",
"To:" ), endStr, oldendStr );
1690 QString recurStr, oldrecurStr;
1691 if ( event->recurs() || oldevent->recurs() ) {
1695 html += htmlRow( i18n(
"Recurrence:" ), recurStr, oldrecurStr );
1697 html += htmlInvitationDetailsTableEnd();
1698 html += invitationDetailsIncidence( event, noHtmlMode );
1699 html += htmlInvitationDetailsEnd();
1704 static QString invitationDetailsTodo(
const Todo::Ptr &todo,
bool noHtmlMode,
1705 KDateTime::Spec spec )
1712 QString html = htmlInvitationDetailsBegin();
1713 html += htmlInvitationDetailsTableBegin();
1716 html += htmlRow( i18n(
"What:" ), invitationSummary( todo, noHtmlMode ) );
1717 html += htmlRow( i18n(
"Where:" ), invitationLocation( todo, noHtmlMode ) );
1719 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
1720 html += htmlRow( i18n(
"Start Date:" ),
dateToString( todo->dtStart(),
false, spec ) );
1721 if ( !todo->allDay() ) {
1722 html += htmlRow( i18n(
"Start Time:" ),
timeToString( todo->dtStart(),
false, spec ) );
1725 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
1726 html += htmlRow( i18n(
"Due Date:" ),
dateToString( todo->dtDue(),
false, spec ) );
1727 if ( !todo->allDay() ) {
1728 html += htmlRow( i18n(
"Due Time:" ),
timeToString( todo->dtDue(),
false, spec ) );
1731 html += htmlRow( i18n(
"Due Date:" ), i18nc(
"Due Date: None",
"None" ) );
1738 if ( todo->percentComplete() > 0 ) {
1739 html += htmlRow( i18n(
"Percent Done:" ), i18n(
"%1%", todo->percentComplete() ) );
1743 if ( todo->recurs() ) {
1747 html += htmlInvitationDetailsTableEnd();
1748 html += invitationDetailsIncidence( todo, noHtmlMode );
1749 html += htmlInvitationDetailsEnd();
1754 static QString invitationDetailsTodo(
const Todo::Ptr &todo,
const Todo::Ptr &oldtodo,
1756 KDateTime::Spec spec )
1759 return invitationDetailsTodo( todo, noHtmlMode, spec );
1767 html += invitationNote( QString(),
1768 i18n(
"Please respond again to the original proposal." ),
1769 QString(), noteColor() );
1772 html += htmlInvitationDetailsBegin();
1773 html += htmlInvitationDetailsTableBegin();
1775 html += htmlRow( i18n(
"What:" ),
1776 invitationSummary( todo, noHtmlMode ),
1777 invitationSummary( todo, noHtmlMode ) );
1779 html += htmlRow( i18n(
"Where:" ),
1780 invitationLocation( todo, noHtmlMode ),
1781 invitationLocation( oldtodo, noHtmlMode ) );
1783 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
1784 html += htmlRow( i18n(
"Start Date:" ),
1787 QString startTimeStr, oldstartTimeStr;
1788 if ( !todo->allDay() || !oldtodo->allDay() ) {
1789 startTimeStr = todo->allDay() ?
1790 i18n(
"All day" ) :
timeToString( todo->dtStart(), false );
1791 oldstartTimeStr = oldtodo->allDay() ?
1792 i18n(
"All day" ) :
timeToString( oldtodo->dtStart(), false );
1794 html += htmlRow( i18n(
"Start Time:" ), startTimeStr, oldstartTimeStr );
1796 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
1797 html += htmlRow( i18n(
"Due Date:" ),
1800 QString endTimeStr, oldendTimeStr;
1801 if ( !todo->allDay() || !oldtodo->allDay() ) {
1802 endTimeStr = todo->allDay() ?
1803 i18n(
"All day" ) :
timeToString( todo->dtDue(), false );
1804 oldendTimeStr = oldtodo->allDay() ?
1805 i18n(
"All day" ) :
timeToString( oldtodo->dtDue(), false );
1807 html += htmlRow( i18n(
"Due Time:" ), endTimeStr, oldendTimeStr );
1809 QString dueStr = i18nc(
"Due Date: None",
"None" );
1811 if ( !oldtodo->hasDueDate() || !oldtodo->dtDue().isValid() ) {
1812 olddueStr = i18nc(
"Due Date: None",
"None" );
1814 olddueStr =
dateTimeToString( oldtodo->dtDue(), oldtodo->allDay(), false );
1816 html += htmlRow( i18n(
"Due Date:" ), dueStr, olddueStr );
1821 QString completionStr, oldcompletionStr;
1822 if ( todo->percentComplete() > 0 || oldtodo->percentComplete() > 0 ) {
1823 completionStr = i18n(
"%1%", todo->percentComplete() );
1824 oldcompletionStr = i18n(
"%1%", oldtodo->percentComplete() );
1826 html += htmlRow( i18n(
"Percent Done:" ), completionStr, oldcompletionStr );
1828 QString recurStr, oldrecurStr;
1829 if ( todo->recurs() || oldtodo->recurs() ) {
1833 html += htmlRow( i18n(
"Recurrence:" ), recurStr, oldrecurStr );
1835 html += htmlInvitationDetailsTableEnd();
1836 html += invitationDetailsIncidence( todo, noHtmlMode );
1838 html += htmlInvitationDetailsEnd();
1843 static QString invitationDetailsJournal(
const Journal::Ptr &journal,
bool noHtmlMode,
1844 KDateTime::Spec spec )
1850 QString html = htmlInvitationDetailsBegin();
1851 html += htmlInvitationDetailsTableBegin();
1853 html += htmlRow( i18n(
"Summary:" ), invitationSummary( journal, noHtmlMode ) );
1854 html += htmlRow( i18n(
"Date:" ),
dateToString( journal->dtStart(),
false, spec ) );
1856 html += htmlInvitationDetailsTableEnd();
1857 html += invitationDetailsIncidence( journal, noHtmlMode );
1858 html += htmlInvitationDetailsEnd();
1863 static QString invitationDetailsJournal(
const Journal::Ptr &journal,
1865 bool noHtmlMode, KDateTime::Spec spec )
1867 if ( !oldjournal ) {
1868 return invitationDetailsJournal( journal, noHtmlMode, spec );
1871 QString html = htmlInvitationDetailsBegin();
1872 html += htmlInvitationDetailsTableBegin();
1874 html += htmlRow( i18n(
"What:" ),
1875 invitationSummary( journal, noHtmlMode ),
1876 invitationSummary( oldjournal, noHtmlMode ) );
1878 html += htmlRow( i18n(
"Date:" ),
1882 html += htmlInvitationDetailsTableEnd();
1883 html += invitationDetailsIncidence( journal, noHtmlMode );
1884 html += htmlInvitationDetailsEnd();
1889 static QString invitationDetailsFreeBusy(
const FreeBusy::Ptr &fb,
bool noHtmlMode,
1890 KDateTime::Spec spec )
1892 Q_UNUSED( noHtmlMode );
1898 QString html = htmlInvitationDetailsTableBegin();
1900 html += htmlRow( i18n(
"Person:" ), fb->organizer()->fullName() );
1901 html += htmlRow( i18n(
"Start date:" ),
dateToString( fb->dtStart(),
true, spec ) );
1902 html += htmlRow( i18n(
"End date:" ),
dateToString( fb->dtEnd(),
true, spec ) );
1904 html +=
"<tr><td colspan=2><hr></td></tr>\n";
1905 html +=
"<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
1907 Period::List periods = fb->busyPeriods();
1908 Period::List::iterator it;
1909 for ( it = periods.begin(); it != periods.end(); ++it ) {
1911 if ( per.hasDuration() ) {
1912 int dur = per.duration().asSeconds();
1914 if ( dur >= 3600 ) {
1915 cont += i18ncp(
"hours part of duration",
"1 hour ",
"%1 hours ", dur / 3600 );
1919 cont += i18ncp(
"minutes part of duration",
"1 minute",
"%1 minutes ", dur / 60 );
1923 cont += i18ncp(
"seconds part of duration",
"1 second",
"%1 seconds", dur );
1925 html += htmlRow( QString(),
1926 i18nc(
"startDate for duration",
"%1 for %2",
1928 per.start().dateTime(), KLocale::LongDate ),
1932 if ( per.start().date() == per.end().date() ) {
1933 cont = i18nc(
"date, fromTime - toTime ",
"%1, %2 - %3",
1934 KGlobal::locale()->
formatDate( per.start().date() ),
1935 KGlobal::locale()->formatTime( per.start().time() ),
1936 KGlobal::locale()->formatTime( per.end().time() ) );
1938 cont = i18nc(
"fromDateTime - toDateTime",
"%1 - %2",
1940 per.start().dateTime(), KLocale::LongDate ),
1941 KGlobal::locale()->formatDateTime(
1942 per.end().dateTime(), KLocale::LongDate ) );
1945 html += htmlRow( QString(), cont );
1949 html += htmlInvitationDetailsTableEnd();
1954 bool noHtmlMode, KDateTime::Spec spec )
1957 return invitationDetailsFreeBusy( fb, noHtmlMode, spec );
1960 static bool replyMeansCounter(
const Incidence::Ptr &incidence )
1962 Q_UNUSED( incidence );
1980 static QString invitationHeaderEvent(
const Event::Ptr &event,
1981 const Incidence::Ptr &existingIncidence,
1984 if ( !msg || !event ) {
1988 switch ( msg->method() ) {
1990 return i18n(
"This invitation has been published" );
1992 if ( existingIncidence && event->revision() > 0 ) {
1993 QString orgStr = organizerName( event, sender );
1994 if ( senderIsOrganizer( event, sender ) ) {
1995 return i18n(
"This invitation has been updated by the organizer %1", orgStr );
1997 return i18n(
"This invitation has been updated by %1 as a representative of %2",
2001 if ( iamOrganizer( event ) ) {
2002 return i18n(
"I created this invitation" );
2004 QString orgStr = organizerName( event, sender );
2005 if ( senderIsOrganizer( event, sender ) ) {
2006 return i18n(
"You received an invitation from %1", orgStr );
2008 return i18n(
"You received an invitation from %1 as a representative of %2",
2013 return i18n(
"This invitation was refreshed" );
2015 if ( iamOrganizer( event ) ) {
2016 return i18n(
"This invitation has been canceled" );
2018 return i18n(
"The organizer has revoked the invitation" );
2021 return i18n(
"Addition to the invitation" );
2024 if ( replyMeansCounter( event ) ) {
2025 return i18n(
"%1 makes this counter proposal", firstAttendeeName( event, sender ) );
2029 if( attendees.count() == 0 ) {
2030 kDebug() <<
"No attendees in the iCal reply!";
2033 if ( attendees.count() != 1 ) {
2034 kDebug() <<
"Warning: attendeecount in the reply should be 1"
2035 <<
"but is" << attendees.count();
2037 QString attendeeName = firstAttendeeName( event, sender );
2039 QString delegatorName, dummy;
2041 KPIMUtils::extractEmailAddressAndName( attendee->delegator(), dummy, delegatorName );
2042 if ( delegatorName.isEmpty() ) {
2043 delegatorName = attendee->delegator();
2046 switch( attendee->status() ) {
2048 return i18n(
"%1 indicates this invitation still needs some action", attendeeName );
2050 if ( event->revision() > 0 ) {
2051 if ( !sender.isEmpty() ) {
2052 return i18n(
"This invitation has been updated by attendee %1", sender );
2054 return i18n(
"This invitation has been updated by an attendee" );
2057 if ( delegatorName.isEmpty() ) {
2058 return i18n(
"%1 accepts this invitation", attendeeName );
2060 return i18n(
"%1 accepts this invitation on behalf of %2",
2061 attendeeName, delegatorName );
2065 if ( delegatorName.isEmpty() ) {
2066 return i18n(
"%1 tentatively accepts this invitation", attendeeName );
2068 return i18n(
"%1 tentatively accepts this invitation on behalf of %2",
2069 attendeeName, delegatorName );
2072 if ( delegatorName.isEmpty() ) {
2073 return i18n(
"%1 declines this invitation", attendeeName );
2075 return i18n(
"%1 declines this invitation on behalf of %2",
2076 attendeeName, delegatorName );
2080 QString delegate, dummy;
2081 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
2082 if ( delegate.isEmpty() ) {
2083 delegate = attendee->delegate();
2085 if ( !delegate.isEmpty() ) {
2086 return i18n(
"%1 has delegated this invitation to %2", attendeeName, delegate );
2088 return i18n(
"%1 has delegated this invitation", attendeeName );
2092 return i18n(
"This invitation is now completed" );
2094 return i18n(
"%1 is still processing the invitation", attendeeName );
2096 return i18n(
"Unknown response to this invitation" );
2101 return i18n(
"%1 makes this counter proposal",
2102 firstAttendeeName( event, i18n(
"Sender" ) ) );
2106 QString orgStr = organizerName( event, sender );
2107 if ( senderIsOrganizer( event, sender ) ) {
2108 return i18n(
"%1 declines your counter proposal", orgStr );
2110 return i18n(
"%1 declines your counter proposal on behalf of %2", sender, orgStr );
2115 return i18n(
"Error: Event iTIP message with unknown method" );
2117 kError() <<
"encountered an iTIP method that we do not support";
2121 static QString invitationHeaderTodo(
const Todo::Ptr &todo,
2122 const Incidence::Ptr &existingIncidence,
2125 if ( !msg || !todo ) {
2129 switch ( msg->method() ) {
2131 return i18n(
"This to-do has been published" );
2133 if ( existingIncidence && todo->revision() > 0 ) {
2134 QString orgStr = organizerName( todo, sender );
2135 if ( senderIsOrganizer( todo, sender ) ) {
2136 return i18n(
"This to-do has been updated by the organizer %1", orgStr );
2138 return i18n(
"This to-do has been updated by %1 as a representative of %2",
2142 if ( iamOrganizer( todo ) ) {
2143 return i18n(
"I created this to-do" );
2145 QString orgStr = organizerName( todo, sender );
2146 if ( senderIsOrganizer( todo, sender ) ) {
2147 return i18n(
"You have been assigned this to-do by %1", orgStr );
2149 return i18n(
"You have been assigned this to-do by %1 as a representative of %2",
2155 return i18n(
"This to-do was refreshed" );
2157 if ( iamOrganizer( todo ) ) {
2158 return i18n(
"This to-do was canceled" );
2160 return i18n(
"The organizer has revoked this to-do" );
2163 return i18n(
"Addition to the to-do" );
2166 if ( replyMeansCounter( todo ) ) {
2167 return i18n(
"%1 makes this counter proposal", firstAttendeeName( todo, sender ) );
2171 if ( attendees.count() == 0 ) {
2172 kDebug() <<
"No attendees in the iCal reply!";
2175 if ( attendees.count() != 1 ) {
2176 kDebug() <<
"Warning: attendeecount in the reply should be 1"
2177 <<
"but is" << attendees.count();
2179 QString attendeeName = firstAttendeeName( todo, sender );
2181 QString delegatorName, dummy;
2183 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegatorName );
2184 if ( delegatorName.isEmpty() ) {
2185 delegatorName = attendee->delegator();
2188 switch( attendee->status() ) {
2190 return i18n(
"%1 indicates this to-do assignment still needs some action",
2193 if ( todo->revision() > 0 ) {
2194 if ( !sender.isEmpty() ) {
2195 if ( todo->isCompleted() ) {
2196 return i18n(
"This to-do has been completed by assignee %1", sender );
2198 return i18n(
"This to-do has been updated by assignee %1", sender );
2201 if ( todo->isCompleted() ) {
2202 return i18n(
"This to-do has been completed by an assignee" );
2204 return i18n(
"This to-do has been updated by an assignee" );
2208 if ( delegatorName.isEmpty() ) {
2209 return i18n(
"%1 accepts this to-do", attendeeName );
2211 return i18n(
"%1 accepts this to-do on behalf of %2",
2212 attendeeName, delegatorName );
2216 if ( delegatorName.isEmpty() ) {
2217 return i18n(
"%1 tentatively accepts this to-do", attendeeName );
2219 return i18n(
"%1 tentatively accepts this to-do on behalf of %2",
2220 attendeeName, delegatorName );
2223 if ( delegatorName.isEmpty() ) {
2224 return i18n(
"%1 declines this to-do", attendeeName );
2226 return i18n(
"%1 declines this to-do on behalf of %2",
2227 attendeeName, delegatorName );
2231 QString delegate, dummy;
2232 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
2233 if ( delegate.isEmpty() ) {
2234 delegate = attendee->delegate();
2236 if ( !delegate.isEmpty() ) {
2237 return i18n(
"%1 has delegated this to-do to %2", attendeeName, delegate );
2239 return i18n(
"%1 has delegated this to-do", attendeeName );
2243 return i18n(
"The request for this to-do is now completed" );
2245 return i18n(
"%1 is still processing the to-do", attendeeName );
2247 return i18n(
"Unknown response to this to-do" );
2252 return i18n(
"%1 makes this counter proposal", firstAttendeeName( todo, sender ) );
2256 QString orgStr = organizerName( todo, sender );
2257 if ( senderIsOrganizer( todo, sender ) ) {
2258 return i18n(
"%1 declines the counter proposal", orgStr );
2260 return i18n(
"%1 declines the counter proposal on behalf of %2", sender, orgStr );
2265 return i18n(
"Error: To-do iTIP message with unknown method" );
2267 kError() <<
"encountered an iTIP method that we do not support";
2271 static QString invitationHeaderJournal(
const Journal::Ptr &journal,
2274 if ( !msg || !journal ) {
2278 switch ( msg->method() ) {
2280 return i18n(
"This journal has been published" );
2282 return i18n(
"You have been assigned this journal" );
2284 return i18n(
"This journal was refreshed" );
2286 return i18n(
"This journal was canceled" );
2288 return i18n(
"Addition to the journal" );
2291 if ( replyMeansCounter( journal ) ) {
2292 return i18n(
"Sender makes this counter proposal" );
2296 if ( attendees.count() == 0 ) {
2297 kDebug() <<
"No attendees in the iCal reply!";
2300 if( attendees.count() != 1 ) {
2301 kDebug() <<
"Warning: attendeecount in the reply should be 1 "
2302 <<
"but is " << attendees.count();
2306 switch( attendee->status() ) {
2308 return i18n(
"Sender indicates this journal assignment still needs some action" );
2310 return i18n(
"Sender accepts this journal" );
2312 return i18n(
"Sender tentatively accepts this journal" );
2314 return i18n(
"Sender declines this journal" );
2316 return i18n(
"Sender has delegated this request for the journal" );
2318 return i18n(
"The request for this journal is now completed" );
2320 return i18n(
"Sender is still processing the invitation" );
2322 return i18n(
"Unknown response to this journal" );
2327 return i18n(
"Sender makes this counter proposal" );
2329 return i18n(
"Sender declines the counter proposal" );
2331 return i18n(
"Error: Journal iTIP message with unknown method" );
2333 kError() <<
"encountered an iTIP method that we do not support";
2337 static QString invitationHeaderFreeBusy(
const FreeBusy::Ptr &fb,
2340 if ( !msg || !fb ) {
2344 switch ( msg->method() ) {
2346 return i18n(
"This free/busy list has been published" );
2348 return i18n(
"The free/busy list has been requested" );
2350 return i18n(
"This free/busy list was refreshed" );
2352 return i18n(
"This free/busy list was canceled" );
2354 return i18n(
"Addition to the free/busy list" );
2356 return i18n(
"Reply to the free/busy list" );
2358 return i18n(
"Sender makes this counter proposal" );
2360 return i18n(
"Sender declines the counter proposal" );
2362 return i18n(
"Error: Free/Busy iTIP message with unknown method" );
2364 kError() <<
"encountered an iTIP method that we do not support";
2369 static QString invitationAttendeeList(
const Incidence::Ptr &incidence )
2375 if ( incidence->type() == Incidence::TypeTodo ) {
2376 tmpStr += i18n(
"Assignees" );
2378 tmpStr += i18n(
"Invitation List" );
2383 if ( !attendees.isEmpty() ) {
2384 QStringList comments;
2385 Attendee::List::ConstIterator it;
2386 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
2388 if ( !iamAttendee( a ) ) {
2391 tmpStr +=
"<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\">";
2396 if ( attendeeIsOrganizer( incidence, a ) ) {
2397 comments << i18n(
"organizer" );
2399 if ( !a->delegator().isEmpty() ) {
2400 comments << i18n(
" (delegated by %1)", a->delegator() );
2402 if ( !a->delegate().isEmpty() ) {
2403 comments << i18n(
" (delegated to %1)", a->delegate() );
2405 tmpStr += invitationPerson( a->email(), a->name(), QString(), comments.join(
"," ) );
2412 tmpStr +=
"</table>";
2420 static QString invitationRsvpList(
const Incidence::Ptr &incidence,
const Attendee::Ptr &sender )
2426 if ( incidence->type() == Incidence::TypeTodo ) {
2427 tmpStr += i18n(
"Assignees" );
2429 tmpStr += i18n(
"Invitation List" );
2434 if ( !attendees.isEmpty() ) {
2435 QStringList comments;
2436 Attendee::List::ConstIterator it;
2437 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
2439 if ( !attendeeIsOrganizer( incidence, a ) ) {
2440 QString statusStr = Stringify::attendeeStatus( a->status () );
2441 if ( sender && ( a->email() == sender->email() ) ) {
2444 if ( a->status() != sender->status() ) {
2445 statusStr = i18n(
"%1 (<i>unrecorded</i>)",
2446 Stringify::attendeeStatus( sender->status() ) );
2452 tmpStr +=
"<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\">";
2457 if ( iamAttendee( a ) ) {
2458 comments << i18n(
"myself" );
2460 if ( !a->delegator().isEmpty() ) {
2461 comments << i18n(
" (delegated by %1)", a->delegator() );
2463 if ( !a->delegate().isEmpty() ) {
2464 comments << i18n(
" (delegated to %1)", a->delegate() );
2466 tmpStr += invitationPerson( a->email(), a->name(), QString(), comments.join(
"," ) );
2468 tmpStr +=
"<td>" + statusStr +
"</td>";
2474 tmpStr +=
"</table>";
2476 tmpStr +=
"<i> " + i18nc(
"no attendees",
"None" ) +
"</i>";
2482 static QString invitationAttachments( InvitationFormatterHelper *helper,
2483 const Incidence::Ptr &incidence )
2490 if ( incidence->type() == Incidence::TypeFreeBusy ) {
2496 if ( !attachments.isEmpty() ) {
2497 tmpStr += i18n(
"Attached Documents:" ) +
"<ol>";
2499 Attachment::List::ConstIterator it;
2500 for ( it = attachments.constBegin(); it != attachments.constEnd(); ++it ) {
2504 KMimeType::Ptr
mimeType = KMimeType::mimeType( a->mimeType() );
2505 const QString iconStr = ( mimeType ?
2506 mimeType->iconName( a->uri() ) :
2507 QString(
"application-octet-stream" ) );
2508 const QString iconPath = KIconLoader::global()->iconPath( iconStr, KIconLoader::Small );
2509 if ( !iconPath.isEmpty() ) {
2510 tmpStr +=
"<img valign=\"top\" src=\"" + iconPath +
"\">";
2512 tmpStr += helper->makeLink(
"ATTACH:" + a->label().toUtf8().toBase64(), a->label() );
2522 class KCalUtils::IncidenceFormatter::ScheduleMessageVisitor :
public Visitor
2525 ScheduleMessageVisitor() : mMessage( 0 ) { mResult =
""; }
2526 bool act(
const IncidenceBase::Ptr &incidence,
2527 const Incidence::Ptr &existingIncidence,
2530 mExistingIncidence = existingIncidence;
2533 return incidence->accept( *
this, incidence );
2535 QString result()
const {
return mResult; }
2539 Incidence::Ptr mExistingIncidence;
2544 class KCalUtils::IncidenceFormatter::InvitationHeaderVisitor :
2545 public IncidenceFormatter::ScheduleMessageVisitor
2550 mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender );
2551 return !mResult.isEmpty();
2555 mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender );
2556 return !mResult.isEmpty();
2560 mResult = invitationHeaderJournal( journal, mMessage );
2561 return !mResult.isEmpty();
2565 mResult = invitationHeaderFreeBusy( fb, mMessage );
2566 return !mResult.isEmpty();
2570 class KCalUtils::IncidenceFormatter::InvitationBodyVisitor
2571 :
public IncidenceFormatter::ScheduleMessageVisitor
2574 InvitationBodyVisitor(
bool noHtmlMode, KDateTime::Spec spec )
2575 : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ), mSpec( spec ) {}
2581 mResult = invitationDetailsEvent( event, oldevent, mMessage, mNoHtmlMode, mSpec );
2582 return !mResult.isEmpty();
2587 mResult = invitationDetailsTodo( todo, oldtodo, mMessage, mNoHtmlMode, mSpec );
2588 return !mResult.isEmpty();
2593 mResult = invitationDetailsJournal( journal, oldjournal, mNoHtmlMode, mSpec );
2594 return !mResult.isEmpty();
2598 mResult = invitationDetailsFreeBusy( fb,
FreeBusy::Ptr(), mNoHtmlMode, mSpec );
2599 return !mResult.isEmpty();
2604 KDateTime::Spec mSpec;
2608 InvitationFormatterHelper::InvitationFormatterHelper()
2613 InvitationFormatterHelper::~InvitationFormatterHelper()
2617 QString InvitationFormatterHelper::generateLinkURL(
const QString &
id )
2623 class IncidenceFormatter::IncidenceCompareVisitor :
public Visitor
2626 IncidenceCompareVisitor() {}
2627 bool act(
const IncidenceBase::Ptr &incidence,
2628 const Incidence::Ptr &existingIncidence )
2630 if ( !existingIncidence ) {
2633 Incidence::Ptr inc = incidence.staticCast<
Incidence>();
2634 if ( !inc || !existingIncidence ||
2635 inc->revision() <= existingIncidence->revision() ) {
2638 mExistingIncidence = existingIncidence;
2639 return incidence->
accept( *
this, incidence );
2642 QString result()
const
2644 if ( mChanges.isEmpty() ) {
2647 QString html =
"<div align=\"left\"><ul><li>";
2648 html += mChanges.join(
"</li><li>" );
2649 html +=
"</li><ul></div>";
2656 compareEvents( event, mExistingIncidence.dynamicCast<
Event>() );
2657 compareIncidences( event, mExistingIncidence );
2658 return !mChanges.isEmpty();
2662 compareTodos( todo, mExistingIncidence.dynamicCast<
Todo>() );
2663 compareIncidences( todo, mExistingIncidence );
2664 return !mChanges.isEmpty();
2668 compareIncidences( journal, mExistingIncidence );
2669 return !mChanges.isEmpty();
2674 return !mChanges.isEmpty();
2678 void compareEvents(
const Event::Ptr &newEvent,
2681 if ( !oldEvent || !newEvent ) {
2684 if ( oldEvent->dtStart() != newEvent->dtStart() ||
2685 oldEvent->allDay() != newEvent->allDay() ) {
2686 mChanges += i18n(
"The invitation starting time has been changed from %1 to %2",
2687 eventStartTimeStr( oldEvent ), eventStartTimeStr( newEvent ) );
2689 if ( oldEvent->dtEnd() != newEvent->dtEnd() ||
2690 oldEvent->allDay() != newEvent->allDay() ) {
2691 mChanges += i18n(
"The invitation ending time has been changed from %1 to %2",
2692 eventEndTimeStr( oldEvent ), eventEndTimeStr( newEvent ) );
2696 void compareTodos(
const Todo::Ptr &newTodo,
2699 if ( !oldTodo || !newTodo ) {
2703 if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) {
2704 mChanges += i18n(
"The to-do has been completed" );
2706 if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) {
2707 mChanges += i18n(
"The to-do is no longer completed" );
2709 if ( oldTodo->percentComplete() != newTodo->percentComplete() ) {
2710 const QString oldPer = i18n(
"%1%", oldTodo->percentComplete() );
2711 const QString newPer = i18n(
"%1%", newTodo->percentComplete() );
2712 mChanges += i18n(
"The task completed percentage has changed from %1 to %2",
2716 if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) {
2717 mChanges += i18n(
"A to-do starting time has been added" );
2719 if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) {
2720 mChanges += i18n(
"The to-do starting time has been removed" );
2722 if ( oldTodo->hasStartDate() && newTodo->hasStartDate() &&
2723 oldTodo->dtStart() != newTodo->dtStart() ) {
2724 mChanges += i18n(
"The to-do starting time has been changed from %1 to %2",
2729 if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) {
2730 mChanges += i18n(
"A to-do due time has been added" );
2732 if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) {
2733 mChanges += i18n(
"The to-do due time has been removed" );
2735 if ( oldTodo->hasDueDate() && newTodo->hasDueDate() &&
2736 oldTodo->dtDue() != newTodo->dtDue() ) {
2737 mChanges += i18n(
"The to-do due time has been changed from %1 to %2",
2743 void compareIncidences(
const Incidence::Ptr &newInc,
2744 const Incidence::Ptr &oldInc )
2746 if ( !oldInc || !newInc ) {
2750 if ( oldInc->summary() != newInc->summary() ) {
2751 mChanges += i18n(
"The summary has been changed to: \"%1\"",
2752 newInc->richSummary() );
2755 if ( oldInc->location() != newInc->location() ) {
2756 mChanges += i18n(
"The location has been changed to: \"%1\"",
2757 newInc->richLocation() );
2760 if ( oldInc->description() != newInc->description() ) {
2761 mChanges += i18n(
"The description has been changed to: \"%1\"",
2762 newInc->richDescription() );
2767 for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
2768 it != newAttendees.constEnd(); ++it ) {
2769 Attendee::Ptr oldAtt = oldInc->attendeeByMail( (*it)->email() );
2771 mChanges += i18n(
"Attendee %1 has been added", (*it)->fullName() );
2773 if ( oldAtt->status() != (*it)->status() ) {
2774 mChanges += i18n(
"The status of attendee %1 has been changed to: %2",
2775 (*it)->fullName(), Stringify::attendeeStatus( (*it)->status() ) );
2780 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
2781 it != oldAttendees.constEnd(); ++it ) {
2782 if ( !attendeeIsOrganizer( oldInc, (*it) ) ) {
2783 Attendee::Ptr newAtt = newInc->attendeeByMail( (*it)->email() );
2785 mChanges += i18n(
"Attendee %1 has been removed", (*it)->fullName() );
2792 Incidence::Ptr mExistingIncidence;
2793 QStringList mChanges;
2797 QString InvitationFormatterHelper::makeLink(
const QString &
id,
const QString &text )
2799 if ( !
id.startsWith( QLatin1String(
"ATTACH:" ) ) ) {
2800 QString res = QString(
"<a href=\"%1\"><b>%2</b></a>" ).
2801 arg( generateLinkURL(
id ), text );
2805 QString res = QString(
"<a href=\"%1\">%2</a>" ).
2806 arg( generateLinkURL(
id ), text );
2813 static bool incidenceOwnedByMe(
const Calendar::Ptr &calendar,
2814 const Incidence::Ptr &incidence )
2816 Q_UNUSED( calendar );
2817 Q_UNUSED( incidence );
2822 static QString tdOpen =
"<td style=\"border-width:2px;border-style:outset\">";
2823 static QString tdClose =
"</td>";
2825 static QString responseButtons(
const Incidence::Ptr &inc,
2826 bool rsvpReq,
bool rsvpRec,
2827 InvitationFormatterHelper *helper )
2834 if ( !rsvpReq && ( inc && inc->revision() == 0 ) ) {
2837 html += helper->makeLink(
"record", i18n(
"[Record]" ) );
2842 html += helper->makeLink(
"delete", i18n(
"[Move to Trash]" ) );
2849 html += helper->makeLink(
"accept", i18nc(
"accept invitation",
"Accept" ) );
2854 html += helper->makeLink(
"accept_conditionally",
2855 i18nc(
"Accept invitation conditionally",
"Accept cond." ) );
2860 html += helper->makeLink(
"counter",
2861 i18nc(
"invitation counter proposal",
"Counter proposal" ) );
2866 html += helper->makeLink(
"decline",
2867 i18nc(
"decline invitation",
"Decline" ) );
2871 if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
2874 html += helper->makeLink(
"delegate",
2875 i18nc(
"delegate inviation to another",
"Delegate" ) );
2880 html += helper->makeLink(
"forward",
2881 i18nc(
"forward request to another",
"Forward" ) );
2885 if ( inc && inc->type() == Incidence::TypeEvent ) {
2887 html += helper->makeLink(
"check_calendar",
2888 i18nc(
"look for scheduling conflicts",
"Check my calendar" ) );
2895 static QString counterButtons(
const Incidence::Ptr &incidence,
2896 InvitationFormatterHelper *helper )
2905 html += helper->makeLink(
"accept_counter", i18n(
"[Accept]" ) );
2910 html += helper->makeLink(
"decline_counter", i18n(
"[Decline]" ) );
2914 if ( incidence && incidence->type() == Incidence::TypeEvent ) {
2916 html += helper->makeLink(
"check_calendar", i18n(
"[Check my calendar] " ) );
2927 static QString formatICalInvitationHelper( QString invitation,
2929 InvitationFormatterHelper *helper,
2931 KDateTime::Spec spec,
2932 const QString &sender,
2933 bool outlookCompareStyle )
2935 if ( invitation.isEmpty() ) {
2945 kDebug() <<
"Failed to parse the scheduling message";
2951 IncidenceBase::Ptr incBase = msg->event();
2953 incBase->shiftTimes( mCalendar->timeSpec(), KDateTime::Spec::LocalZone() );
2956 Incidence::Ptr existingIncidence;
2957 if ( incBase && helper->calendar() ) {
2958 existingIncidence = helper->calendar()->incidence( incBase->uid() );
2960 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
2961 existingIncidence.clear();
2963 if ( !existingIncidence ) {
2964 const Incidence::List list = helper->calendar()->incidences();
2965 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
2966 if ( (*it)->schedulingID() == incBase->uid() &&
2967 incidenceOwnedByMe( helper->calendar(), *it ) ) {
2968 existingIncidence = *it;
2975 Incidence::Ptr inc = incBase.staticCast<
Incidence>();
2979 int incRevision = 0;
2980 if ( inc && inc->type() != Incidence::TypeFreeBusy ) {
2981 incRevision = inc->revision();
2986 html +=
"<div align=\"center\" style=\"border:solid 1px;\">";
2988 IncidenceFormatter::InvitationHeaderVisitor headerVisitor;
2990 if ( !headerVisitor.act( inc, existingIncidence, msg, sender ) ) {
2993 html += htmlAddTag(
"h3", headerVisitor.result() );
2995 if ( outlookCompareStyle ||
2998 IncidenceFormatter::InvitationBodyVisitor bodyVisitor( noHtmlMode, spec );
3002 if ( inc && existingIncidence &&
3003 incRevision < existingIncidence->revision() ) {
3004 bodyOk = bodyVisitor.act( existingIncidence, inc, msg, sender );
3006 bodyOk = bodyVisitor.act( inc, existingIncidence, msg, sender );
3009 bodyOk = bodyVisitor.act( inc, Incidence::Ptr(), msg, sender );
3012 html += bodyVisitor.result();
3018 InvitationBodyVisitor bodyVisitor( noHtmlMode, spec );
3019 if ( !bodyVisitor.act( inc, Incidence::Ptr(), msg, sender ) ) {
3022 html += bodyVisitor.result();
3025 IncidenceFormatter::IncidenceCompareVisitor compareVisitor;
3026 if ( compareVisitor.act( inc, existingIncidence ) ) {
3027 html +=
"<p align=\"left\">";
3028 if ( senderIsOrganizer( inc, sender ) ) {
3029 html += i18n(
"The following changes have been made by the organizer:" );
3030 }
else if ( !sender.isEmpty() ) {
3031 html += i18n(
"The following changes have been made by %1:", sender );
3033 html += i18n(
"The following changes have been made:" );
3036 html += compareVisitor.result();
3040 IncidenceCompareVisitor compareVisitor;
3041 if ( compareVisitor.act( inc, existingIncidence ) ) {
3042 html +=
"<p align=\"left\">";
3043 if ( !sender.isEmpty() ) {
3044 html += i18n(
"The following changes have been made by %1:", sender );
3046 html += i18n(
"The following changes have been made by an attendee:" );
3049 html += compareVisitor.result();
3055 bool myInc = iamOrganizer( inc );
3058 bool rsvpRec =
false;
3061 Incidence::Ptr rsvpIncidence = existingIncidence;
3062 if ( !rsvpIncidence && inc && incRevision > 0 ) {
3063 rsvpIncidence = inc;
3065 if ( rsvpIncidence ) {
3066 ea = findMyAttendee( rsvpIncidence );
3069 ( ea->status() == Attendee::Accepted ||
3070 ea->status() == Attendee::Declined ||
3071 ea->status() == Attendee::Tentative ) ) {
3078 bool isDelegated =
false;
3081 if ( !inc->attendees().isEmpty() ) {
3082 a = inc->attendees().first();
3086 isDelegated = ( a->status() == Attendee::Delegated );
3087 role = Stringify::attendeeRole( a->role() );
3091 bool rsvpReq = rsvpRequested( inc );
3092 if ( !myInc && a ) {
3095 if ( rsvpRec && inc ) {
3096 if ( incRevision == 0 ) {
3097 html += i18n(
"Your <b>%1</b> response has been recorded",
3098 Stringify::attendeeStatus( ea->status() ) );
3100 html += i18n(
"Your status for this invitation is <b>%1</b>",
3101 Stringify::attendeeStatus( ea->status() ) );
3105 html += i18n(
"This invitation was canceled" );
3106 }
else if ( msg->method() ==
iTIPAdd ) {
3107 html += i18n(
"This invitation was accepted" );
3110 html += rsvpRequestedStr( rsvpReq, role );
3112 if ( !isDelegated ) {
3113 html += rsvpRequestedStr( rsvpReq, role );
3115 html += i18n(
"Awaiting delegation response" );
3123 if ( inc && incRevision == 0 ) {
3124 QString statStr = myStatusStr( inc );
3125 if ( !statStr.isEmpty() ) {
3137 html +=
"<table border=\"0\" align=\"center\" cellspacing=\"4\"><tr>";
3139 switch ( msg->method() ) {
3145 if ( inc && incRevision > 0 && ( existingIncidence || !helper->calendar() ) ) {
3146 if ( inc->type() == Incidence::TypeTodo ) {
3147 html += helper->makeLink(
"reply", i18n(
"[Record invitation in my to-do list]" ) );
3149 html += helper->makeLink(
"reply", i18n(
"[Record invitation in my calendar]" ) );
3153 if ( !myInc && a ) {
3154 html += responseButtons( inc, rsvpReq, rsvpRec, helper );
3163 if ( inc->type() == Incidence::TypeTodo ) {
3164 html += helper->makeLink(
"cancel",
3165 i18n(
"Remove invitation from my to-do list" ) );
3167 html += helper->makeLink(
"cancel",
3168 i18n(
"Remove invitation from my calendar" ) );
3181 if ( replyMeansCounter( inc ) ) {
3182 html +=
"<tr>" + counterButtons( inc, helper ) +
"</tr>";
3191 a = findDelegatedFromMyAttendee( inc );
3193 if ( a->status() != Attendee::Accepted ||
3194 a->status() != Attendee::Tentative ) {
3195 html += responseButtons( inc, rsvpReq, rsvpRec, helper );
3201 if ( !inc->attendees().isEmpty() ) {
3202 a = inc->attendees().first();
3204 if ( a && helper->calendar() ) {
3205 ea = findAttendee( existingIncidence, a->email() );
3208 if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
3210 html += htmlAddTag(
"i", i18n(
"The <b>%1</b> response has been recorded",
3211 Stringify::attendeeStatus( ea->status() ) ) );
3215 if ( inc->type() == Incidence::TypeTodo ) {
3216 html += helper->makeLink(
"reply", i18n(
"[Record response in my to-do list]" ) );
3218 html += helper->makeLink(
"reply", i18n(
"[Record response in my calendar]" ) );
3227 html += counterButtons( inc, helper );
3231 html += responseButtons( inc, rsvpReq, rsvpRec, helper );
3239 html +=
"</tr></table>";
3243 html += invitationRsvpList( existingIncidence, a );
3245 html += invitationAttendeeList( inc );
3252 html += invitationAttachments( helper, inc );
3260 InvitationFormatterHelper *helper,
3261 bool outlookCompareStyle )
3263 return formatICalInvitationHelper( invitation, calendar, helper,
false,
3264 KSystemTimeZones::local(), QString(),
3265 outlookCompareStyle );
3270 InvitationFormatterHelper *helper,
3271 const QString &sender,
3272 bool outlookCompareStyle )
3274 return formatICalInvitationHelper( invitation, calendar, helper,
true,
3275 KSystemTimeZones::local(), sender,
3276 outlookCompareStyle );
3284 class KCalUtils::IncidenceFormatter::ToolTipVisitor :
public Visitor
3288 : mRichText( true ), mSpec( KDateTime::Spec() ), mResult(
"" ) {}
3291 const IncidenceBase::Ptr &incidence,
3292 const QDate &date=QDate(),
bool richText=
true,
3293 KDateTime::Spec spec=KDateTime::Spec() )
3295 mCalendar = calendar;
3298 mRichText = richText;
3301 return incidence ? incidence->
accept( *
this, incidence ) :
false;
3304 bool act(
const QString &location,
const IncidenceBase::Ptr &incidence,
3305 const QDate &date=QDate(),
bool richText=
true,
3306 KDateTime::Spec spec=KDateTime::Spec() )
3308 mLocation = location;
3310 mRichText = richText;
3313 return incidence ? incidence->
accept( *
this, incidence ) :
false;
3316 QString result()
const {
return mResult; }
3324 QString dateRangeText(
const Event::Ptr &event,
const QDate &date );
3325 QString dateRangeText(
const Todo::Ptr &todo,
const QDate &date );
3329 QString generateToolTip(
const Incidence::Ptr &incidence, QString dtRangeText );
3336 KDateTime::Spec mSpec;
3340 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const Event::Ptr &event,
3347 KDateTime startDt =
event->dtStart();
3348 KDateTime endDt =
event->dtEnd();
3349 if ( event->recurs() ) {
3350 if ( date.isValid() ) {
3351 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
3352 int diffDays = startDt.daysTo( kdt );
3353 kdt = kdt.addSecs( -1 );
3354 startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
3355 if ( event->hasEndDate() ) {
3356 endDt = endDt.addDays( diffDays );
3357 if ( startDt > endDt ) {
3358 startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
3359 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
3365 if ( event->isMultiDay() ) {
3367 ret +=
"<br>" + i18nc(
"Event start",
"<i>From:</i> %1", tmp );
3370 ret +=
"<br>" + i18nc(
"Event end",
"<i>To:</i> %1", tmp );
3375 i18n(
"<i>Date:</i> %1",
dateToString( startDt,
false, mSpec ) );
3376 if ( !event->allDay() ) {
3377 const QString dtStartTime =
timeToString( startDt,
true, mSpec );
3378 const QString dtEndTime =
timeToString( endDt,
true, mSpec );
3379 if ( dtStartTime == dtEndTime ) {
3382 i18nc(
"time for event",
"<i>Time:</i> %1",
3386 i18nc(
"time range for event",
3387 "<i>Time:</i> %1 - %2",
3388 dtStartTime, dtEndTime );
3393 return ret.replace(
' ',
" " );
3396 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const Todo::Ptr &todo,
3401 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
3402 KDateTime startDt = todo->dtStart();
3403 if ( todo->recurs() ) {
3404 if ( date.isValid() ) {
3405 startDt.setDate( date );
3409 i18n(
"<i>Start:</i> %1",
dateToString( startDt,
false, mSpec ) );
3412 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
3413 KDateTime dueDt = todo->dtDue();
3414 if ( todo->recurs() ) {
3415 if ( date.isValid() ) {
3416 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
3417 kdt = kdt.addSecs( -1 );
3418 dueDt.setDate( todo->recurrence()->getNextDateTime( kdt ).date() );
3422 i18n(
"<i>Due:</i> %1",
3428 if ( todo->priority() > 0 ) {
3430 ret +=
"<i>" + i18n(
"Priority:" ) +
"</i>" +
" ";
3431 ret += QString::number( todo->priority() );
3435 if ( todo->isCompleted() ) {
3436 ret +=
"<i>" + i18nc(
"Completed: date",
"Completed:" ) +
"</i>" +
" ";
3439 ret +=
"<i>" + i18n(
"Percent Done:" ) +
"</i>" +
" ";
3440 ret += i18n(
"%1%", todo->percentComplete() );
3443 return ret.replace(
' ',
" " );
3446 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const Journal::Ptr &journal )
3450 if ( journal->dtStart().isValid() ) {
3452 i18n(
"<i>Date:</i> %1",
dateToString( journal->dtStart(),
false, mSpec ) );
3454 return ret.replace(
' ',
" " );
3457 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const FreeBusy::Ptr &fb )
3462 i18n(
"<i>Period start:</i> %1",
3465 i18n(
"<i>Period start:</i> %1",
3467 return ret.replace(
' ',
" " );
3472 mResult = generateToolTip( event, dateRangeText( event, mDate ) );
3473 return !mResult.isEmpty();
3478 mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
3479 return !mResult.isEmpty();
3484 mResult = generateToolTip( journal, dateRangeText( journal ) );
3485 return !mResult.isEmpty();
3491 mResult =
"<qt><b>" +
3492 i18n(
"Free/Busy information for %1", fb->organizer()->fullName() ) +
3494 mResult += dateRangeText( fb );
3496 return !mResult.isEmpty();
3499 static QString tooltipPerson(
const QString &email,
const QString &name,
Attendee::PartStat status )
3502 const QString printName = searchName( email, name );
3505 const QString iconPath = rsvpStatusIconPath( status );
3508 QString personString;
3509 if ( !iconPath.isEmpty() ) {
3510 personString +=
"<img valign=\"top\" src=\"" + iconPath +
"\">" +
" ";
3512 if ( status != Attendee::None ) {
3513 personString += i18nc(
"attendee name (attendee status)",
"%1 (%2)",
3514 printName.isEmpty() ? email : printName,
3515 Stringify::attendeeStatus( status ) );
3517 personString += i18n(
"%1", printName.isEmpty() ? email : printName );
3519 return personString;
3522 static QString tooltipFormatOrganizer(
const QString &email,
const QString &name )
3525 const QString printName = searchName( email, name );
3528 const QString iconPath =
3529 KIconLoader::global()->iconPath(
"meeting-organizer", KIconLoader::Small );
3532 QString personString;
3533 personString +=
"<img valign=\"top\" src=\"" + iconPath +
"\">" +
" ";
3534 personString += ( printName.isEmpty() ? email : printName );
3535 return personString;
3538 static QString tooltipFormatAttendeeRoleList(
const Incidence::Ptr &incidence,
3542 const QString etc = i18nc(
"elipsis",
"..." );
3546 Attendee::List::ConstIterator it;
3549 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
3551 if ( a->role() != role ) {
3555 if ( attendeeIsOrganizer( incidence, a ) ) {
3559 if ( i == maxNumAtts ) {
3560 tmpStr +=
" " + etc;
3563 tmpStr +=
" " + tooltipPerson( a->email(), a->name(),
3564 showStatus ? a->status() : Attendee::None );
3565 if ( !a->delegator().isEmpty() ) {
3566 tmpStr += i18n(
" (delegated by %1)", a->delegator() );
3568 if ( !a->delegate().isEmpty() ) {
3569 tmpStr += i18n(
" (delegated to %1)", a->delegate() );
3574 if ( tmpStr.endsWith( QLatin1String(
"<br>" ) ) ) {
3580 static QString tooltipFormatAttendees(
const Calendar::Ptr &calendar,
3581 const Incidence::Ptr &incidence )
3583 QString tmpStr, str;
3586 int attendeeCount = incidence->attendees().count();
3587 if ( attendeeCount > 1 ||
3588 ( attendeeCount == 1 &&
3589 !attendeeIsOrganizer( incidence, incidence->attendees().first() ) ) ) {
3590 tmpStr +=
"<i>" + i18n(
"Organizer:" ) +
"</i>" +
"<br>";
3591 tmpStr +=
" " + tooltipFormatOrganizer( incidence->organizer()->email(),
3592 incidence->organizer()->name() );
3597 const bool showStatus = attendeeCount > 0 && incOrganizerOwnsCalendar( calendar, incidence );
3600 str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair, showStatus );
3601 if ( !str.isEmpty() ) {
3602 tmpStr +=
"<br><i>" + i18n(
"Chair:" ) +
"</i>" +
"<br>";
3607 str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant, showStatus );
3608 if ( !str.isEmpty() ) {
3609 tmpStr +=
"<br><i>" + i18n(
"Required Participants:" ) +
"</i>" +
"<br>";
3614 str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant, showStatus );
3615 if ( !str.isEmpty() ) {
3616 tmpStr +=
"<br><i>" + i18n(
"Optional Participants:" ) +
"</i>" +
"<br>";
3621 str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant, showStatus );
3622 if ( !str.isEmpty() ) {
3623 tmpStr +=
"<br><i>" + i18n(
"Observers:" ) +
"</i>" +
"<br>";
3630 QString IncidenceFormatter::ToolTipVisitor::generateToolTip(
const Incidence::Ptr &incidence,
3631 QString dtRangeText )
3633 int maxDescLen = 120;
3640 QString tmp =
"<qt>";
3643 tmp +=
"<b>" + incidence->richSummary() +
"</b>";
3646 QString calStr = mLocation;
3650 if ( !calStr.isEmpty() ) {
3651 tmp +=
"<i>" + i18n(
"Calendar:" ) +
"</i>" +
" ";
3657 if ( !incidence->location().isEmpty() ) {
3659 tmp +=
"<i>" + i18n(
"Location:" ) +
"</i>" +
" ";
3660 tmp += incidence->richLocation();
3664 if ( !durStr.isEmpty() ) {
3666 tmp +=
"<i>" + i18n(
"Duration:" ) +
"</i>" +
" ";
3670 if ( incidence->recurs() ) {
3672 tmp +=
"<i>" + i18n(
"Recurrence:" ) +
"</i>" +
" ";
3676 if ( !incidence->description().isEmpty() ) {
3677 QString desc( incidence->description() );
3678 if ( !incidence->descriptionIsRich() ) {
3679 if ( desc.length() > maxDescLen ) {
3680 desc = desc.left( maxDescLen ) + i18nc(
"elipsis",
"..." );
3682 desc = Qt::escape( desc ).replace(
'\n',
"<br>" );
3687 tmp +=
"<i>" + i18n(
"Description:" ) +
"</i>" +
"<br>";
3692 int reminderCount = incidence->alarms().count();
3693 if ( reminderCount > 0 && incidence->hasEnabledAlarms() ) {
3695 tmp +=
"<i>" + i18np(
"Reminder:",
"Reminders:", reminderCount ) +
"</i>" +
" ";
3700 tmp += tooltipFormatAttendees( mCalendar, incidence );
3702 int categoryCount = incidence->categories().count();
3703 if ( categoryCount > 0 ) {
3705 tmp +=
"<i>" + i18np(
"Category:",
"Categories:", categoryCount ) +
"</i>" +
" ";
3706 tmp += incidence->categories().join(
", " );
3715 const IncidenceBase::Ptr &incidence,
3718 KDateTime::Spec spec )
3721 if ( v.act( sourceName, incidence, date, richText, spec ) ) {
3733 static QString mailBodyIncidence(
const Incidence::Ptr &incidence )
3736 if ( !incidence->summary().isEmpty() ) {
3737 body += i18n(
"Summary: %1\n", incidence->richSummary() );
3739 if ( !incidence->organizer()->isEmpty() ) {
3740 body += i18n(
"Organizer: %1\n", incidence->organizer()->fullName() );
3742 if ( !incidence->location().isEmpty() ) {
3743 body += i18n(
"Location: %1\n", incidence->richLocation() );
3750 class KCalUtils::IncidenceFormatter::MailBodyVisitor :
public Visitor
3754 : mSpec( KDateTime::Spec() ), mResult(
"" ) {}
3756 bool act( IncidenceBase::Ptr incidence, KDateTime::Spec spec=KDateTime::Spec() )
3760 return incidence ? incidence->accept( *
this, incidence ) :
false;
3762 QString result()
const
3773 mResult = i18n(
"This is a Free Busy Object" );
3774 return !mResult.isEmpty();
3777 KDateTime::Spec mSpec;
3783 QString recurrence[]= {
3784 i18nc(
"no recurrence",
"None" ),
3785 i18nc(
"event recurs by minutes",
"Minutely" ),
3786 i18nc(
"event recurs by hours",
"Hourly" ),
3787 i18nc(
"event recurs by days",
"Daily" ),
3788 i18nc(
"event recurs by weeks",
"Weekly" ),
3789 i18nc(
"event recurs same position (e.g. first monday) each month",
"Monthly Same Position" ),
3790 i18nc(
"event recurs same day each month",
"Monthly Same Day" ),
3791 i18nc(
"event recurs same month each year",
"Yearly Same Month" ),
3792 i18nc(
"event recurs same day each year",
"Yearly Same Day" ),
3793 i18nc(
"event recurs same position (e.g. first monday) each year",
"Yearly Same Position" )
3796 mResult = mailBodyIncidence( event );
3797 mResult += i18n(
"Start Date: %1\n",
dateToString( event->dtStart(),
true, mSpec ) );
3798 if ( !event->allDay() ) {
3799 mResult += i18n(
"Start Time: %1\n",
timeToString( event->dtStart(),
true, mSpec ) );
3801 if ( event->dtStart() !=
event->dtEnd() ) {
3802 mResult += i18n(
"End Date: %1\n",
dateToString( event->dtEnd(),
true, mSpec ) );
3804 if ( !event->allDay() ) {
3805 mResult += i18n(
"End Time: %1\n",
timeToString( event->dtEnd(),
true, mSpec ) );
3807 if ( event->recurs() ) {
3810 mResult += i18n(
"Recurs: %1\n", recurrence[ recur->
recurrenceType() ] );
3811 mResult += i18n(
"Frequency: %1\n", event->recurrence()->frequency() );
3814 mResult += i18np(
"Repeats once",
"Repeats %1 times", recur->
duration() );
3820 if ( event->allDay() ) {
3821 endstr = KGlobal::locale()->formatDate( recur->
endDate() );
3823 endstr = KGlobal::locale()->formatDateTime( recur->
endDateTime().dateTime() );
3825 mResult += i18n(
"Repeat until: %1\n", endstr );
3827 mResult += i18n(
"Repeats forever\n" );
3832 QString details =
event->richDescription();
3833 if ( !details.isEmpty() ) {
3834 mResult += i18n(
"Details:\n%1\n", details );
3836 return !mResult.isEmpty();
3841 mResult = mailBodyIncidence( todo );
3843 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
3844 mResult += i18n(
"Start Date: %1\n",
dateToString( todo->dtStart(
false ),
true, mSpec ) );
3845 if ( !todo->allDay() ) {
3846 mResult += i18n(
"Start Time: %1\n",
timeToString( todo->dtStart(
false ),
true, mSpec ) );
3849 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
3850 mResult += i18n(
"Due Date: %1\n",
dateToString( todo->dtDue(),
true, mSpec ) );
3851 if ( !todo->allDay() ) {
3852 mResult += i18n(
"Due Time: %1\n",
timeToString( todo->dtDue(),
true, mSpec ) );
3855 QString details = todo->richDescription();
3856 if ( !details.isEmpty() ) {
3857 mResult += i18n(
"Details:\n%1\n", details );
3859 return !mResult.isEmpty();
3864 mResult = mailBodyIncidence( journal );
3865 mResult += i18n(
"Date: %1\n",
dateToString( journal->dtStart(),
true, mSpec ) );
3866 if ( !journal->allDay() ) {
3867 mResult += i18n(
"Time: %1\n",
timeToString( journal->dtStart(),
true, mSpec ) );
3869 if ( !journal->description().isEmpty() ) {
3870 mResult += i18n(
"Text of the journal:\n%1\n", journal->richDescription() );
3872 return !mResult.isEmpty();
3877 KDateTime::Spec spec )
3884 if ( v.act( incidence, spec ) ) {
3891 static QString recurEnd(
const Incidence::Ptr &incidence )
3894 if ( incidence->allDay() ) {
3895 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
3897 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
3909 if ( !incidence->recurs() ) {
3910 return i18n(
"No recurrence" );
3912 static QStringList dayList;
3913 if ( dayList.isEmpty() ) {
3914 dayList.append( i18n(
"31st Last" ) );
3915 dayList.append( i18n(
"30th Last" ) );
3916 dayList.append( i18n(
"29th Last" ) );
3917 dayList.append( i18n(
"28th Last" ) );
3918 dayList.append( i18n(
"27th Last" ) );
3919 dayList.append( i18n(
"26th Last" ) );
3920 dayList.append( i18n(
"25th Last" ) );
3921 dayList.append( i18n(
"24th Last" ) );
3922 dayList.append( i18n(
"23rd Last" ) );
3923 dayList.append( i18n(
"22nd Last" ) );
3924 dayList.append( i18n(
"21st Last" ) );
3925 dayList.append( i18n(
"20th Last" ) );
3926 dayList.append( i18n(
"19th Last" ) );
3927 dayList.append( i18n(
"18th Last" ) );
3928 dayList.append( i18n(
"17th Last" ) );
3929 dayList.append( i18n(
"16th Last" ) );
3930 dayList.append( i18n(
"15th Last" ) );
3931 dayList.append( i18n(
"14th Last" ) );
3932 dayList.append( i18n(
"13th Last" ) );
3933 dayList.append( i18n(
"12th Last" ) );
3934 dayList.append( i18n(
"11th Last" ) );
3935 dayList.append( i18n(
"10th Last" ) );
3936 dayList.append( i18n(
"9th Last" ) );
3937 dayList.append( i18n(
"8th Last" ) );
3938 dayList.append( i18n(
"7th Last" ) );
3939 dayList.append( i18n(
"6th Last" ) );
3940 dayList.append( i18n(
"5th Last" ) );
3941 dayList.append( i18n(
"4th Last" ) );
3942 dayList.append( i18n(
"3rd Last" ) );
3943 dayList.append( i18n(
"2nd Last" ) );
3944 dayList.append( i18nc(
"last day of the month",
"Last" ) );
3945 dayList.append( i18nc(
"unknown day of the month",
"unknown" ) );
3946 dayList.append( i18n(
"1st" ) );
3947 dayList.append( i18n(
"2nd" ) );
3948 dayList.append( i18n(
"3rd" ) );
3949 dayList.append( i18n(
"4th" ) );
3950 dayList.append( i18n(
"5th" ) );
3951 dayList.append( i18n(
"6th" ) );
3952 dayList.append( i18n(
"7th" ) );
3953 dayList.append( i18n(
"8th" ) );
3954 dayList.append( i18n(
"9th" ) );
3955 dayList.append( i18n(
"10th" ) );
3956 dayList.append( i18n(
"11th" ) );
3957 dayList.append( i18n(
"12th" ) );
3958 dayList.append( i18n(
"13th" ) );
3959 dayList.append( i18n(
"14th" ) );
3960 dayList.append( i18n(
"15th" ) );
3961 dayList.append( i18n(
"16th" ) );
3962 dayList.append( i18n(
"17th" ) );
3963 dayList.append( i18n(
"18th" ) );
3964 dayList.append( i18n(
"19th" ) );
3965 dayList.append( i18n(
"20th" ) );
3966 dayList.append( i18n(
"21st" ) );
3967 dayList.append( i18n(
"22nd" ) );
3968 dayList.append( i18n(
"23rd" ) );
3969 dayList.append( i18n(
"24th" ) );
3970 dayList.append( i18n(
"25th" ) );
3971 dayList.append( i18n(
"26th" ) );
3972 dayList.append( i18n(
"27th" ) );
3973 dayList.append( i18n(
"28th" ) );
3974 dayList.append( i18n(
"29th" ) );
3975 dayList.append( i18n(
"30th" ) );
3976 dayList.append( i18n(
"31st" ) );
3979 const int weekStart = KGlobal::locale()->weekStartDay();
3981 const KCalendarSystem *calSys = KGlobal::locale()->calendar();
3985 QString txt, recurStr;
3986 static QString noRecurrence = i18n(
"No recurrence" );
3988 case Recurrence::rNone:
3989 return noRecurrence;
3991 case Recurrence::rMinutely:
3993 recurStr = i18np(
"Recurs every minute until %2",
3994 "Recurs every %1 minutes until %2",
3995 recur->
frequency(), recurEnd( incidence ) );
3997 recurStr += i18nc(
"number of occurrences",
3998 " (<numid>%1</numid> occurrences)",
4002 recurStr = i18np(
"Recurs every minute",
4003 "Recurs every %1 minutes", recur->
frequency() );
4007 case Recurrence::rHourly:
4009 recurStr = i18np(
"Recurs hourly until %2",
4010 "Recurs every %1 hours until %2",
4011 recur->
frequency(), recurEnd( incidence ) );
4013 recurStr += i18nc(
"number of occurrences",
4014 " (<numid>%1</numid> occurrences)",
4018 recurStr = i18np(
"Recurs hourly",
"Recurs every %1 hours", recur->
frequency() );
4022 case Recurrence::rDaily:
4024 recurStr = i18np(
"Recurs daily until %2",
4025 "Recurs every %1 days until %2",
4026 recur->
frequency(), recurEnd( incidence ) );
4028 recurStr += i18nc(
"number of occurrences",
4029 " (<numid>%1</numid> occurrences)",
4033 recurStr = i18np(
"Recurs daily",
"Recurs every %1 days", recur->
frequency() );
4037 case Recurrence::rWeekly:
4039 bool addSpace =
false;
4040 for (
int i = 0; i < 7; ++i ) {
4041 if ( recur->
days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
4043 dayNames.append( i18nc(
"separator for list of days",
", " ) );
4045 dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1,
4046 KCalendarSystem::ShortDayName ) );
4050 if ( dayNames.isEmpty() ) {
4051 dayNames = i18nc(
"Recurs weekly on no days",
"no days" );
4054 recurStr = i18ncp(
"Recurs weekly on [list of days] until end-date",
4055 "Recurs weekly on %2 until %3",
4056 "Recurs every <numid>%1</numid> weeks on %2 until %3",
4057 recur->
frequency(), dayNames, recurEnd( incidence ) );
4059 recurStr += i18nc(
"number of occurrences",
4060 " (<numid>%1</numid> occurrences)",
4064 recurStr = i18ncp(
"Recurs weekly on [list of days]",
4065 "Recurs weekly on %2",
4066 "Recurs every <numid>%1</numid> weeks on %2",
4071 case Recurrence::rMonthlyPos:
4076 recurStr = i18ncp(
"Recurs every N months on the [2nd|3rd|...]"
4077 " weekdayname until end-date",
4078 "Recurs every month on the %2 %3 until %4",
4079 "Recurs every <numid>%1</numid> months on the %2 %3 until %4",
4081 dayList[rule.pos() + 31],
4082 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
4083 recurEnd( incidence ) );
4085 recurStr += i18nc(
"number of occurrences",
4086 " (<numid>%1</numid> occurrences)",
4090 recurStr = i18ncp(
"Recurs every N months on the [2nd|3rd|...] weekdayname",
4091 "Recurs every month on the %2 %3",
4092 "Recurs every %1 months on the %2 %3",
4094 dayList[rule.pos() + 31],
4095 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ) );
4100 case Recurrence::rMonthlyDay:
4105 recurStr = i18ncp(
"Recurs monthly on the [1st|2nd|...] day until end-date",
4106 "Recurs monthly on the %2 day until %3",
4107 "Recurs every %1 months on the %2 day until %3",
4110 recurEnd( incidence ) );
4112 recurStr += i18nc(
"number of occurrences",
4113 " (<numid>%1</numid> occurrences)",
4117 recurStr = i18ncp(
"Recurs monthly on the [1st|2nd|...] day",
4118 "Recurs monthly on the %2 day",
4119 "Recurs every <numid>%1</numid> month on the %2 day",
4121 dayList[days + 31] );
4126 case Recurrence::rYearlyMonth:
4130 recurStr = i18ncp(
"Recurs Every N years on month-name [1st|2nd|...]"
4132 "Recurs yearly on %2 %3 until %4",
4133 "Recurs every %1 years on %2 %3 until %4",
4137 recurEnd( incidence ) );
4139 recurStr += i18nc(
"number of occurrences",
4140 " (<numid>%1</numid> occurrences)",
4146 recurStr = i18ncp(
"Recurs Every N years on month-name [1st|2nd|...]",
4147 "Recurs yearly on %2 %3",
4148 "Recurs every %1 years on %2 %3",
4152 dayList[ recur->
yearDates()[0] + 31 ] );
4155 recurStr = i18nc(
"Recurs Every year on month-name [1st|2nd|...]",
4156 "Recurs yearly on %1 %2",
4159 dayList[ recur->
startDate().day() + 31 ] );
4161 recurStr = i18nc(
"Recurs Every year on month-name [1st|2nd|...]",
4162 "Recurs yearly on %1 %2",
4163 calSys->monthName( recur->
startDate().month(),
4165 dayList[ recur->
startDate().day() + 31 ] );
4171 case Recurrence::rYearlyDay:
4172 if ( !recur->
yearDays().isEmpty() ) {
4174 recurStr = i18ncp(
"Recurs every N years on day N until end-date",
4175 "Recurs every year on day <numid>%2</numid> until %3",
4176 "Recurs every <numid>%1</numid> years"
4177 " on day <numid>%2</numid> until %3",
4180 recurEnd( incidence ) );
4182 recurStr += i18nc(
"number of occurrences",
4183 " (<numid>%1</numid> occurrences)",
4187 recurStr = i18ncp(
"Recurs every N YEAR[S] on day N",
4188 "Recurs every year on day <numid>%2</numid>",
4189 "Recurs every <numid>%1</numid> years"
4190 " on day <numid>%2</numid>",
4195 case Recurrence::rYearlyPos:
4200 recurStr = i18ncp(
"Every N years on the [2nd|3rd|...] weekdayname "
4201 "of monthname until end-date",
4202 "Every year on the %2 %3 of %4 until %5",
4203 "Every <numid>%1</numid> years on the %2 %3 of %4"
4206 dayList[rule.pos() + 31],
4207 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
4209 recurEnd( incidence ) );
4211 recurStr += i18nc(
"number of occurrences",
4212 " (<numid>%1</numid> occurrences)",
4216 recurStr = i18ncp(
"Every N years on the [2nd|3rd|...] weekdayname "
4218 "Every year on the %2 %3 of %4",
4219 "Every <numid>%1</numid> years on the %2 %3 of %4",
4221 dayList[rule.pos() + 31],
4222 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
4230 if ( recurStr.isEmpty() ) {
4231 recurStr = i18n(
"Incidence recurs" );
4236 DateTimeList::ConstIterator il;
4238 for ( il = l.constBegin(); il != l.constEnd(); ++il ) {
4240 case Recurrence::rMinutely:
4241 exStr << i18n(
"minute %1", (*il).time().minute() );
4243 case Recurrence::rHourly:
4244 exStr << KGlobal::locale()->formatTime( (*il).time() );
4246 case Recurrence::rDaily:
4247 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4249 case Recurrence::rWeekly:
4250 exStr << calSys->weekDayName( (*il).date(), KCalendarSystem::ShortDayName );
4252 case Recurrence::rMonthlyPos:
4253 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4255 case Recurrence::rMonthlyDay:
4256 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4258 case Recurrence::rYearlyMonth:
4259 exStr << calSys->monthName( (*il).date(), KCalendarSystem::LongName );
4261 case Recurrence::rYearlyDay:
4262 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4264 case Recurrence::rYearlyPos:
4265 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4271 DateList::ConstIterator dl;
4272 for ( dl = d.constBegin(); dl != d.constEnd(); ++dl ) {
4274 case Recurrence::rDaily:
4275 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4277 case Recurrence::rWeekly:
4280 if ( exStr.isEmpty() ) {
4281 exStr << i18np(
"1 day",
"%1 days", recur->exDates().count() );
4284 case Recurrence::rMonthlyPos:
4285 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4287 case Recurrence::rMonthlyDay:
4288 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4290 case Recurrence::rYearlyMonth:
4291 exStr << calSys->monthName( (*dl), KCalendarSystem::LongName );
4293 case Recurrence::rYearlyDay:
4294 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4296 case Recurrence::rYearlyPos:
4297 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4302 if ( !exStr.isEmpty() ) {
4303 recurStr = i18n(
"%1 (excluding %2)", recurStr, exStr.join(
"," ) );
4311 const KDateTime::Spec &spec )
4313 if ( spec.isValid() ) {
4316 if ( spec.timeZone() != KSystemTimeZones::local() ) {
4317 timeZone =
' ' + spec.timeZone().name();
4320 return KGlobal::locale()->formatTime( date.toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
4322 return KGlobal::locale()->formatTime( date.time(), !shortfmt );
4328 const KDateTime::Spec &spec )
4330 if ( spec.isValid() ) {
4333 if ( spec.timeZone() != KSystemTimeZones::local() ) {
4334 timeZone =
' ' + spec.timeZone().name();
4338 KGlobal::locale()->formatDate( date.toTimeSpec( spec ).date(),
4339 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) +
4343 KGlobal::locale()->formatDate( date.date(),
4344 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
4351 const KDateTime::Spec &spec )
4357 if ( spec.isValid() ) {
4359 if ( spec.timeZone() != KSystemTimeZones::local() ) {
4360 timeZone =
' ' + spec.timeZone().name();
4363 return KGlobal::locale()->formatDateTime(
4364 date.toTimeSpec( spec ).dateTime(),
4365 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
4367 return KGlobal::locale()->formatDateTime(
4369 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
4374 const Incidence::Ptr &incidence )
4376 Q_UNUSED( calendar );
4377 Q_UNUSED( incidence );
4381 static QString secs2Duration(
int secs )
4384 int days = secs / 86400;
4386 tmp += i18np(
"1 day",
"%1 days", days );
4388 secs -= ( days * 86400 );
4390 int hours = secs / 3600;
4392 tmp += i18np(
"1 hour",
"%1 hours", hours );
4394 secs -= ( hours * 3600 );
4396 int mins = secs / 60;
4398 tmp += i18np(
"1 minute",
"%1 minutes", mins );
4406 if ( incidence->type() == Incidence::TypeEvent ) {
4408 if ( event->hasEndDate() ) {
4409 if ( !event->allDay() ) {
4410 tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) );
4412 tmp = i18np(
"1 day",
"%1 days",
4413 event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 );
4416 tmp = i18n(
"forever" );
4418 }
else if ( incidence->type() == Incidence::TypeTodo ) {
4420 if ( todo->hasDueDate() ) {
4421 if ( todo->hasStartDate() ) {
4422 if ( !todo->allDay() ) {
4423 tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) );
4425 tmp = i18np(
"1 day",
"%1 days",
4426 todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 );
4438 Q_UNUSED( shortfmt );
4444 Alarm::List::ConstIterator it;
4445 for ( it = alarms.constBegin(); it != alarms.constEnd(); ++it ) {
4448 QString remStr, atStr, offsetStr;
4449 if ( alarm->hasTime() ) {
4451 if ( alarm->time().isValid() ) {
4452 atStr = KGlobal::locale()->formatDateTime( alarm->time() );
4454 }
else if ( alarm->hasStartOffset() ) {
4455 offset = alarm->startOffset().asSeconds();
4458 offsetStr = i18nc(
"N days/hours/minutes before the start datetime",
4459 "%1 before the start", secs2Duration( offset ) );
4460 }
else if ( offset > 0 ) {
4461 offsetStr = i18nc(
"N days/hours/minutes after the start datetime",
4462 "%1 after the start", secs2Duration( offset ) );
4464 if ( incidence->dtStart().isValid() ) {
4465 atStr = KGlobal::locale()->formatDateTime( incidence->dtStart() );
4468 }
else if ( alarm->hasEndOffset() ) {
4469 offset = alarm->endOffset().asSeconds();
4472 if ( incidence->type() == Incidence::TypeTodo ) {
4473 offsetStr = i18nc(
"N days/hours/minutes before the due datetime",
4474 "%1 before the to-do is due", secs2Duration( offset ) );
4476 offsetStr = i18nc(
"N days/hours/minutes before the end datetime",
4477 "%1 before the end", secs2Duration( offset ) );
4479 }
else if ( offset > 0 ) {
4480 if ( incidence->type() == Incidence::TypeTodo ) {
4481 offsetStr = i18nc(
"N days/hours/minutes after the due datetime",
4482 "%1 after the to-do is due", secs2Duration( offset ) );
4484 offsetStr = i18nc(
"N days/hours/minutes after the end datetime",
4485 "%1 after the end", secs2Duration( offset ) );
4488 if ( incidence->type() == Incidence::TypeTodo ) {
4490 if ( t->dtDue().isValid() ) {
4491 atStr = KGlobal::locale()->formatDateTime( t->dtDue() );
4495 if ( e->dtEnd().isValid() ) {
4496 atStr = KGlobal::locale()->formatDateTime( e->dtEnd() );
4501 if ( offset == 0 ) {
4502 if ( !atStr.isEmpty() ) {
4503 remStr = i18nc(
"reminder occurs at datetime",
"at %1", atStr );
4509 if ( alarm->repeatCount() > 0 ) {
4510 QString countStr = i18np(
"repeats once",
"repeats %1 times", alarm->repeatCount() );
4511 QString intervalStr = i18nc(
"interval is N days/hours/minutes",
4513 secs2Duration( alarm->snoozeTime().asSeconds() ) );
4514 QString repeatStr = i18nc(
"(repeat string, interval string)",
4515 "(%1, %2)", countStr, intervalStr );
4516 remStr = remStr +
' ' + repeatStr;
4519 reminderStringList << remStr;