45 #include <kcalcore/visitor.h>
46 using namespace KCalCore;
48 #include <kpimutils/email.h>
49 #include <kpimutils/linklocator.h>
51 #include <KCalendarSystem>
53 #include <KEMailSettings>
54 #include <KIconLoader>
55 #include <KLocalizedString>
58 #include <KSystemTimeZone>
60 #include <QtCore/QBitArray>
61 #include <QApplication>
63 #include <QTextDocument>
65 using namespace KCalUtils;
66 using namespace IncidenceFormatter;
73 static QString string2HTML(
const QString &str )
77 return KPIMUtils::LinkLocator::convertToHtml( str );
80 static QString htmlAddLink(
const QString &ref,
const QString &text,
83 QString tmpStr(
"<a href=\"" + ref +
"\">" + text +
"</a>" );
90 static QString htmlAddMailtoLink(
const QString &email,
const QString &name )
94 if ( !email.isEmpty() ) {
95 Person person( name, email );
96 QString path = person.fullName().simplified();
97 if ( path.isEmpty() || path.startsWith(
'"' ) ) {
101 mailto.setProtocol(
"mailto" );
102 mailto.setPath( path );
103 const QString iconPath =
104 KIconLoader::global()->iconPath(
"mail-message-new", KIconLoader::Small );
105 str = htmlAddLink( mailto.url(),
"<img valign=\"top\" src=\"" + iconPath +
"\">" );
110 static QString htmlAddUidLink(
const QString &email,
const QString &name,
const QString &uid )
114 if ( !uid.isEmpty() ) {
116 if ( name.isEmpty() ) {
118 str += htmlAddLink(
"uid:" + uid, email );
120 str += htmlAddLink(
"uid:" + uid, name );
126 static QString htmlAddTag(
const QString &tag,
const QString &text )
128 int numLineBreaks = text.count(
"\n" );
129 QString str =
'<' + tag +
'>';
130 QString tmpText = text;
131 QString tmpStr = str;
132 if( numLineBreaks >= 0 ) {
133 if ( numLineBreaks > 0 ) {
136 for (
int i = 0; i <= numLineBreaks; ++i ) {
137 pos = tmpText.indexOf(
"\n" );
138 tmp = tmpText.left( pos );
139 tmpText = tmpText.right( tmpText.length() - pos - 1 );
140 tmpStr += tmp +
"<br>";
146 tmpStr +=
"</" + tag +
'>';
150 static QPair<QString, QString> searchNameAndUid(
const QString &email,
const QString &name,
156 QPair<QString, QString>s;
159 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
165 static QString searchName(
const QString &email,
const QString &name )
167 const QString printName = name.isEmpty() ? email : name;
176 KEMailSettings settings;
177 QStringList profiles = settings.profiles();
178 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
179 settings.setProfile( *it );
180 if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
188 static bool iamOrganizer( Incidence::Ptr incidence )
197 KEMailSettings settings;
198 QStringList profiles = settings.profiles();
199 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
200 settings.setProfile( *it );
201 if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer()->email() ) {
209 static bool senderIsOrganizer( Incidence::Ptr incidence,
const QString &sender )
213 if ( !incidence || sender.isEmpty() ) {
218 QString senderName, senderEmail;
219 if ( KPIMUtils::extractEmailAddressAndName( sender, senderEmail, senderName ) ) {
221 if ( incidence->organizer()->email() != senderEmail &&
222 incidence->organizer()->name() != senderName ) {
229 static bool attendeeIsOrganizer(
const Incidence::Ptr &incidence,
const Attendee::Ptr &attendee )
231 if ( incidence && attendee &&
232 ( incidence->organizer()->email() == attendee->email() ) ) {
239 static QString organizerName(
const Incidence::Ptr incidence,
const QString &defName )
242 if ( !defName.isEmpty() ) {
245 tName = i18n(
"Organizer Unknown" );
250 name = incidence->organizer()->name();
251 if ( name.isEmpty() ) {
252 name = incidence->organizer()->email();
255 if ( name.isEmpty() ) {
261 static QString firstAttendeeName(
const Incidence::Ptr &incidence,
const QString &defName )
264 if ( !defName.isEmpty() ) {
267 tName = i18n(
"Sender" );
273 if( attendees.count() > 0 ) {
275 name = attendee->name();
276 if ( name.isEmpty() ) {
277 name = attendee->email();
281 if ( name.isEmpty() ) {
292 iconPath = KIconLoader::global()->iconPath(
"dialog-ok-apply", KIconLoader::Small );
295 iconPath = KIconLoader::global()->iconPath(
"dialog-cancel", KIconLoader::Small );
298 iconPath = KIconLoader::global()->iconPath(
"help-about", KIconLoader::Small );
301 iconPath = KIconLoader::global()->iconPath(
"help-about", KIconLoader::Small );
304 iconPath = KIconLoader::global()->iconPath(
"dialog-ok", KIconLoader::Small );
307 iconPath = KIconLoader::global()->iconPath(
"mail-forward", KIconLoader::Small );
310 iconPath = KIconLoader::global()->iconPath(
"mail-mark-read", KIconLoader::Small );
324 static QString displayViewFormatPerson(
const QString &email,
const QString &name,
325 const QString &uid,
const QString &iconPath )
328 QPair<QString, QString> s = searchNameAndUid( email, name, uid );
329 const QString printName = s.first;
330 const QString printUid = s.second;
332 QString personString;
333 if ( !iconPath.isEmpty() ) {
334 personString +=
"<img valign=\"top\" src=\"" + iconPath +
"\">" +
" ";
338 if ( !printUid.isEmpty() ) {
339 personString += htmlAddUidLink( email, printName, printUid );
342 personString += ( printName.isEmpty() ? email : printName );
345 #ifndef KDEPIM_MOBILE_UI
347 if ( !email.isEmpty() ) {
348 personString +=
" " + htmlAddMailtoLink( email, printName );
355 static QString displayViewFormatPerson(
const QString &email,
const QString &name,
358 return displayViewFormatPerson( email, name, uid, rsvpStatusIconPath( status ) );
361 static bool incOrganizerOwnsCalendar(
const Calendar::Ptr &calendar,
362 const Incidence::Ptr &incidence )
367 Q_UNUSED( calendar );
368 return iamOrganizer( incidence );
371 static QString displayViewFormatAttendeeRoleList( Incidence::Ptr incidence,
Attendee::Role role,
375 Attendee::List::ConstIterator it;
378 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
380 if ( a->role() != role ) {
384 if ( attendeeIsOrganizer( incidence, a ) ) {
388 tmpStr += displayViewFormatPerson( a->email(), a->name(), a->uid(),
389 showStatus ? a->status() : Attendee::None );
390 if ( !a->delegator().isEmpty() ) {
391 tmpStr += i18n(
" (delegated by %1)", a->delegator() );
393 if ( !a->delegate().isEmpty() ) {
394 tmpStr += i18n(
" (delegated to %1)", a->delegate() );
398 if ( tmpStr.endsWith( QLatin1String(
"<br>" ) ) ) {
404 static QString displayViewFormatAttendees(
Calendar::Ptr calendar, Incidence::Ptr incidence )
409 int attendeeCount = incidence->attendees().count();
410 if ( attendeeCount > 1 ||
411 ( attendeeCount == 1 &&
412 !attendeeIsOrganizer( incidence, incidence->attendees().first() ) ) ) {
414 QPair<QString, QString> s = searchNameAndUid( incidence->organizer()->email(),
415 incidence->organizer()->name(),
418 tmpStr +=
"<td><b>" + i18n(
"Organizer:" ) +
"</b></td>";
419 const QString iconPath =
420 KIconLoader::global()->iconPath(
"meeting-organizer", KIconLoader::Small );
421 tmpStr +=
"<td>" + displayViewFormatPerson( incidence->organizer()->email(),
422 s.first, s.second, iconPath ) +
429 bool showStatus = incOrganizerOwnsCalendar( calendar, incidence );
432 str = displayViewFormatAttendeeRoleList( incidence,
Attendee::Chair, showStatus );
433 if ( !str.isEmpty() ) {
435 tmpStr +=
"<td><b>" + i18n(
"Chair:" ) +
"</b></td>";
436 tmpStr +=
"<td>" + str +
"</td>";
442 if ( !str.isEmpty() ) {
444 tmpStr +=
"<td><b>" + i18n(
"Required Participants:" ) +
"</b></td>";
445 tmpStr +=
"<td>" + str +
"</td>";
451 if ( !str.isEmpty() ) {
453 tmpStr +=
"<td><b>" + i18n(
"Optional Participants:" ) +
"</b></td>";
454 tmpStr +=
"<td>" + str +
"</td>";
460 if ( !str.isEmpty() ) {
462 tmpStr +=
"<td><b>" + i18n(
"Observers:" ) +
"</b></td>";
463 tmpStr +=
"<td>" + str +
"</td>";
470 static QString displayViewFormatAttachments( Incidence::Ptr incidence )
474 Attachment::List::ConstIterator it;
476 for ( it = as.constBegin(); it != as.constEnd(); ++it ) {
478 if ( (*it)->isUri() ) {
480 if ( (*it)->uri().startsWith( QLatin1String(
"kmail:" ) ) ) {
481 name = i18n(
"Show mail" );
483 if ( (*it)->label().isEmpty() ) {
486 name = (*it)->label();
489 tmpStr += htmlAddLink( (*it)->uri(), name );
491 tmpStr += htmlAddLink( QString::fromLatin1(
"ATTACH:%1" ).
492 arg( QString::fromUtf8( (*it)->label().toUtf8().toBase64() ) ),
495 if ( count < as.count() ) {
502 static QString displayViewFormatCategories( Incidence::Ptr incidence )
505 return incidence->categories().join(
", " );
508 static QString displayViewFormatCreationDate( Incidence::Ptr incidence, KDateTime::Spec spec )
510 KDateTime kdt = incidence->created().toTimeSpec( spec );
511 return i18n(
"Creation date: %1",
dateTimeToString( incidence->created(),
false,
true, spec ) );
514 static QString displayViewFormatBirthday(
Event::Ptr event )
519 if ( event->customProperty(
"KABC",
"BIRTHDAY" ) !=
"YES" &&
520 event->customProperty(
"KABC",
"ANNIVERSARY" ) !=
"YES" ) {
524 QString uid_1 =
event->customProperty(
"KABC",
"UID-1" );
525 QString name_1 =
event->customProperty(
"KABC",
"NAME-1" );
526 QString email_1=
event->customProperty(
"KABC",
"EMAIL-1" );
528 QString tmpStr = displayViewFormatPerson( email_1, name_1, uid_1, QString() );
532 static QString displayViewFormatHeader( Incidence::Ptr incidence )
534 QString tmpStr =
"<table><tr>";
537 KIconLoader *iconLoader = KIconLoader::global();
541 if ( incidence->customProperty(
"KABC",
"BIRTHDAY" ) ==
"YES" ) {
542 iconPath = iconLoader->iconPath(
"view-calendar-birthday", KIconLoader::Small );
543 }
else if ( incidence->customProperty(
"KABC",
"ANNIVERSARY" ) ==
"YES" ) {
544 iconPath = iconLoader->iconPath(
"view-calendar-wedding-anniversary", KIconLoader::Small );
546 iconPath = iconLoader->iconPath( incidence->iconName(), KIconLoader::Small );
548 tmpStr +=
"<img valign=\"top\" src=\"" + iconPath +
"\">";
550 if ( incidence->hasEnabledAlarms() ) {
551 tmpStr +=
"<img valign=\"top\" src=\"" +
552 iconLoader->iconPath(
"preferences-desktop-notification-bell", KIconLoader::Small ) +
555 if ( incidence->recurs() ) {
556 tmpStr +=
"<img valign=\"top\" src=\"" +
557 iconLoader->iconPath(
"edit-redo", KIconLoader::Small ) +
560 if ( incidence->isReadOnly() ) {
561 tmpStr +=
"<img valign=\"top\" src=\"" +
562 iconLoader->iconPath(
"object-locked", KIconLoader::Small ) +
568 tmpStr +=
"<b><u>" + incidence->richSummary() +
"</u></b>";
571 tmpStr +=
"</tr></table>";
576 static QString displayViewFormatEvent(
const Calendar::Ptr calendar,
const QString &sourceName,
578 const QDate &date, KDateTime::Spec spec )
584 QString tmpStr = displayViewFormatHeader( event );
587 tmpStr +=
"<col width=\"25%\"/>";
588 tmpStr +=
"<col width=\"75%\"/>";
590 const QString calStr = calendar ?
resourceString( calendar, event ) : sourceName;
591 if ( !calStr.isEmpty() ) {
593 tmpStr +=
"<td><b>" + i18n(
"Calendar:" ) +
"</b></td>";
594 tmpStr +=
"<td>" + calStr +
"</td>";
598 if ( !event->location().isEmpty() ) {
600 tmpStr +=
"<td><b>" + i18n(
"Location:" ) +
"</b></td>";
601 tmpStr +=
"<td>" +
event->richLocation() +
"</td>";
605 KDateTime startDt =
event->dtStart();
606 KDateTime endDt =
event->dtEnd();
607 if ( event->recurs() ) {
608 if ( date.isValid() ) {
609 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
610 int diffDays = startDt.daysTo( kdt );
611 kdt = kdt.addSecs( -1 );
612 startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
613 if ( event->hasEndDate() ) {
614 endDt = endDt.addDays( diffDays );
615 if ( startDt > endDt ) {
616 startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
617 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
624 if ( event->allDay() ) {
625 if ( event->isMultiDay() ) {
626 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
628 i18nc(
"<beginTime> - <endTime>",
"%1 - %2",
633 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
635 i18nc(
"date as string",
"%1",
640 if ( event->isMultiDay() ) {
641 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
643 i18nc(
"<beginTime> - <endTime>",
"%1 - %2",
648 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
650 i18nc(
"date as string",
"%1",
654 tmpStr +=
"</tr><tr>";
655 tmpStr +=
"<td><b>" + i18n(
"Time:" ) +
"</b></td>";
656 if ( event->hasEndDate() && startDt != endDt ) {
658 i18nc(
"<beginTime> - <endTime>",
"%1 - %2",
672 if ( !durStr.isEmpty() ) {
674 tmpStr +=
"<td><b>" + i18n(
"Duration:" ) +
"</b></td>";
675 tmpStr +=
"<td>" + durStr +
"</td>";
679 if ( event->recurs() ||
event->hasRecurrenceId() ) {
681 tmpStr +=
"<td><b>" + i18n(
"Recurrence:" ) +
"</b></td>";
684 if ( event->hasRecurrenceId() ) {
685 str = i18n(
"Exception" );
690 tmpStr +=
"<td>" + str +
695 const bool isBirthday =
event->customProperty(
"KABC",
"BIRTHDAY" ) ==
"YES";
696 const bool isAnniversary =
event->customProperty(
"KABC",
"ANNIVERSARY" ) ==
"YES";
698 if ( isBirthday || isAnniversary ) {
700 if ( isAnniversary ) {
701 tmpStr +=
"<td><b>" + i18n(
"Anniversary:" ) +
"</b></td>";
703 tmpStr +=
"<td><b>" + i18n(
"Birthday:" ) +
"</b></td>";
705 tmpStr +=
"<td>" + displayViewFormatBirthday( event ) +
"</td>";
707 tmpStr +=
"</table>";
711 if ( !event->description().isEmpty() ) {
713 if ( !event->descriptionIsRich() &&
714 !
event->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) )
716 descStr = string2HTML( event->description() );
718 if ( !event->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) ) {
719 descStr =
event->richDescription();
721 descStr =
event->description();
725 tmpStr +=
"<td><b>" + i18n(
"Description:" ) +
"</b></td>";
726 tmpStr +=
"<td>" + descStr +
"</td>";
732 int reminderCount =
event->alarms().count();
733 if ( reminderCount > 0 && event->hasEnabledAlarms() ) {
735 tmpStr +=
"<td><b>" +
736 i18np(
"Reminder:",
"Reminders:", reminderCount ) +
742 tmpStr += displayViewFormatAttendees( calendar, event );
744 int categoryCount =
event->categories().count();
745 if ( categoryCount > 0 ) {
748 tmpStr += i18np(
"Category:",
"Categories:", categoryCount ) +
750 tmpStr +=
"<td>" + displayViewFormatCategories( event ) +
"</td>";
754 int attachmentCount =
event->attachments().count();
755 if ( attachmentCount > 0 ) {
757 tmpStr +=
"<td><b>" +
758 i18np(
"Attachment:",
"Attachments:", attachmentCount ) +
760 tmpStr +=
"<td>" + displayViewFormatAttachments( event ) +
"</td>";
763 tmpStr +=
"</table>";
765 tmpStr +=
"<p><em>" + displayViewFormatCreationDate( event, spec ) +
"</em>";
770 static QString displayViewFormatTodo(
const Calendar::Ptr &calendar,
const QString &sourceName,
772 const QDate &date, KDateTime::Spec spec )
775 kDebug() <<
"IncidenceFormatter::displayViewFormatTodo was called without to-do, quitting";
779 QString tmpStr = displayViewFormatHeader( todo );
782 tmpStr +=
"<col width=\"25%\"/>";
783 tmpStr +=
"<col width=\"75%\"/>";
785 const QString calStr = calendar ?
resourceString( calendar, todo ) : sourceName;
786 if ( !calStr.isEmpty() ) {
788 tmpStr +=
"<td><b>" + i18n(
"Calendar:" ) +
"</b></td>";
789 tmpStr +=
"<td>" + calStr +
"</td>";
793 if ( !todo->location().isEmpty() ) {
795 tmpStr +=
"<td><b>" + i18n(
"Location:" ) +
"</b></td>";
796 tmpStr +=
"<td>" + todo->richLocation() +
"</td>";
800 const bool hastStartDate = todo->hasStartDate() && todo->dtStart().isValid();
801 const bool hasDueDate = todo->hasDueDate() && todo->dtDue().isValid();
803 if ( hastStartDate ) {
804 KDateTime startDt = todo->dtStart(
true );
805 if ( todo->recurs() ) {
806 if ( date.isValid() ) {
809 const int length = startDt.daysTo( todo->dtDue(
true ) );
811 startDt.setDate( date.addDays( -length ) );
813 kError() <<
"DTSTART is bigger than DTDUE, todo->uid() is " << todo->uid();
814 startDt.setDate( date );
817 kError() <<
"To-do is recurring but has no DTDUE set, todo->uid() is " << todo->uid();
818 startDt.setDate( date );
823 tmpStr +=
"<td><b>" +
824 i18nc(
"to-do start date/time",
"Start:" ) +
833 KDateTime dueDt = todo->dtDue();
834 if ( todo->recurs() ) {
835 if ( date.isValid() ) {
836 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
837 kdt = kdt.addSecs( -1 );
838 dueDt.setDate( todo->recurrence()->getNextDateTime( kdt ).date() );
842 tmpStr +=
"<td><b>" +
843 i18nc(
"to-do due date/time",
"Due:" ) +
852 if ( !durStr.isEmpty() ) {
854 tmpStr +=
"<td><b>" + i18n(
"Duration:" ) +
"</b></td>";
855 tmpStr +=
"<td>" + durStr +
"</td>";
859 if ( todo->recurs() || todo->hasRecurrenceId() ) {
861 tmpStr +=
"<td><b>" + i18n(
"Recurrence:" ) +
"</b></td>";
863 if ( todo->hasRecurrenceId() ) {
864 str = i18n(
"Exception" );
874 if ( !todo->description().isEmpty() ) {
876 tmpStr +=
"<td><b>" + i18n(
"Description:" ) +
"</b></td>";
877 tmpStr +=
"<td>" + todo->richDescription() +
"</td>";
883 int reminderCount = todo->alarms().count();
884 if ( reminderCount > 0 && todo->hasEnabledAlarms() ) {
886 tmpStr +=
"<td><b>" +
887 i18np(
"Reminder:",
"Reminders:", reminderCount ) +
893 tmpStr += displayViewFormatAttendees( calendar, todo );
895 int categoryCount = todo->categories().count();
896 if ( categoryCount > 0 ) {
898 tmpStr +=
"<td><b>" +
899 i18np(
"Category:",
"Categories:", categoryCount ) +
901 tmpStr +=
"<td>" + displayViewFormatCategories( todo ) +
"</td>";
905 if ( todo->priority() > 0 ) {
907 tmpStr +=
"<td><b>" + i18n(
"Priority:" ) +
"</b></td>";
909 tmpStr += QString::number( todo->priority() );
915 if ( todo->isCompleted() ) {
916 tmpStr +=
"<td><b>" + i18nc(
"Completed: date",
"Completed:" ) +
"</b></td>";
920 tmpStr +=
"<td><b>" + i18n(
"Percent Done:" ) +
"</b></td>";
922 tmpStr += i18n(
"%1%", todo->percentComplete() );
927 int attachmentCount = todo->attachments().count();
928 if ( attachmentCount > 0 ) {
930 tmpStr +=
"<td><b>" +
931 i18np(
"Attachment:",
"Attachments:", attachmentCount ) +
933 tmpStr +=
"<td>" + displayViewFormatAttachments( todo ) +
"</td>";
936 tmpStr +=
"</table>";
938 tmpStr +=
"<p><em>" + displayViewFormatCreationDate( todo, spec ) +
"</em>";
943 static QString displayViewFormatJournal(
const Calendar::Ptr &calendar,
const QString &sourceName,
950 QString tmpStr = displayViewFormatHeader( journal );
953 tmpStr +=
"<col width=\"25%\"/>";
954 tmpStr +=
"<col width=\"75%\"/>";
956 const QString calStr = calendar ?
resourceString( calendar, journal ) : sourceName;
957 if ( !calStr.isEmpty() ) {
959 tmpStr +=
"<td><b>" + i18n(
"Calendar:" ) +
"</b></td>";
960 tmpStr +=
"<td>" + calStr +
"</td>";
965 tmpStr +=
"<td><b>" + i18n(
"Date:" ) +
"</b></td>";
971 if ( !journal->description().isEmpty() ) {
973 tmpStr +=
"<td><b>" + i18n(
"Description:" ) +
"</b></td>";
974 tmpStr +=
"<td>" + journal->richDescription() +
"</td>";
978 int categoryCount = journal->categories().count();
979 if ( categoryCount > 0 ) {
981 tmpStr +=
"<td><b>" +
982 i18np(
"Category:",
"Categories:", categoryCount ) +
984 tmpStr +=
"<td>" + displayViewFormatCategories( journal ) +
"</td>";
988 tmpStr +=
"</table>";
990 tmpStr +=
"<p><em>" + displayViewFormatCreationDate( journal, spec ) +
"</em>";
995 static QString displayViewFormatFreeBusy(
const Calendar::Ptr &calendar,
const QString &sourceName,
998 Q_UNUSED( calendar );
999 Q_UNUSED( sourceName );
1006 "h2", i18n(
"Free/Busy information for %1", fb->organizer()->fullName() ) ) );
1008 tmpStr += htmlAddTag(
"h4",
1009 i18n(
"Busy times in date range %1 - %2:",
1015 htmlAddTag(
"b", i18nc(
"tag for busy periods list",
"Busy:" ) ) );
1017 Period::List periods = fb->busyPeriods();
1018 Period::List::iterator it;
1019 for ( it = periods.begin(); it != periods.end(); ++it ) {
1021 if ( per.hasDuration() ) {
1022 int dur = per.duration().asSeconds();
1024 if ( dur >= 3600 ) {
1025 cont += i18ncp(
"hours part of duration",
"1 hour ",
"%1 hours ", dur / 3600 );
1029 cont += i18ncp(
"minutes part duration",
"1 minute ",
"%1 minutes ", dur / 60 );
1033 cont += i18ncp(
"seconds part of duration",
"1 second",
"%1 seconds", dur );
1035 text += i18nc(
"startDate for duration",
"%1 for %2",
1040 if ( per.start().date() == per.end().date() ) {
1041 text += i18nc(
"date, fromTime - toTime ",
"%1, %2 - %3",
1046 text += i18nc(
"fromDateTime - toDateTime",
"%1 - %2",
1053 tmpStr += htmlAddTag(
"p", text );
1059 class KCalUtils::IncidenceFormatter::EventViewerVisitor :
public Visitor
1062 EventViewerVisitor()
1063 : mCalendar( 0 ), mSpec( KDateTime::Spec() ), mResult(
"" ) {}
1065 bool act(
const Calendar::Ptr &calendar, IncidenceBase::Ptr incidence,
const QDate &date,
1066 KDateTime::Spec spec=KDateTime::Spec() )
1068 mCalendar = calendar;
1069 mSourceName.clear();
1073 return incidence->accept( *
this, incidence );
1076 bool act(
const QString &sourceName, IncidenceBase::Ptr incidence,
const QDate &date,
1077 KDateTime::Spec spec=KDateTime::Spec() )
1079 mSourceName = sourceName;
1083 return incidence->accept( *
this, incidence );
1086 QString result()
const {
return mResult; }
1091 mResult = displayViewFormatEvent( mCalendar, mSourceName, event, mDate, mSpec );
1092 return !mResult.isEmpty();
1096 mResult = displayViewFormatTodo( mCalendar, mSourceName, todo, mDate, mSpec );
1097 return !mResult.isEmpty();
1101 mResult = displayViewFormatJournal( mCalendar, mSourceName, journal, mSpec );
1102 return !mResult.isEmpty();
1106 mResult = displayViewFormatFreeBusy( mCalendar, mSourceName, fb, mSpec );
1107 return !mResult.isEmpty();
1112 QString mSourceName;
1114 KDateTime::Spec mSpec;
1120 const IncidenceBase::Ptr &incidence,
1122 KDateTime::Spec spec )
1128 EventViewerVisitor v;
1129 if ( v.act( calendar, incidence, date, spec ) ) {
1137 const IncidenceBase::Ptr &incidence,
1139 KDateTime::Spec spec )
1145 EventViewerVisitor v;
1146 if ( v.act( sourceName, incidence, date, spec ) ) {
1157 static QString cleanHtml(
const QString &html )
1159 QRegExp rx(
"<body[^>]*>(.*)</body>", Qt::CaseInsensitive );
1161 QString body = rx.cap( 1 );
1163 return Qt::escape( body.remove( QRegExp(
"<[^>]*>" ) ).trimmed() );
1166 static QString invitationSummary(
const Incidence::Ptr &incidence,
bool noHtmlMode )
1168 QString summaryStr = i18n(
"Summary unspecified" );
1169 if ( !incidence->summary().isEmpty() ) {
1170 if ( !incidence->summaryIsRich() ) {
1171 summaryStr = Qt::escape( incidence->summary() );
1173 summaryStr = incidence->richSummary();
1175 summaryStr = cleanHtml( summaryStr );
1182 static QString invitationLocation(
const Incidence::Ptr &incidence,
bool noHtmlMode )
1184 QString locationStr = i18n(
"Location unspecified" );
1185 if ( !incidence->location().isEmpty() ) {
1186 if ( !incidence->locationIsRich() ) {
1187 locationStr = Qt::escape( incidence->location() );
1189 locationStr = incidence->richLocation();
1191 locationStr = cleanHtml( locationStr );
1198 static QString eventStartTimeStr(
const Event::Ptr &event )
1201 if ( !event->allDay() ) {
1202 tmp = i18nc(
"%1: Start Date, %2: Start Time",
"%1 %2",
1203 dateToString( event->dtStart(),
true, KSystemTimeZones::local() ),
1204 timeToString( event->dtStart(),
true, KSystemTimeZones::local() ) );
1206 tmp = i18nc(
"%1: Start Date",
"%1 (all day)",
1207 dateToString( event->dtStart(),
true, KSystemTimeZones::local() ) );
1212 static QString eventEndTimeStr(
const Event::Ptr &event )
1215 if ( event->hasEndDate() &&
event->dtEnd().isValid() ) {
1216 if ( !event->allDay() ) {
1217 tmp = i18nc(
"%1: End Date, %2: End Time",
"%1 %2",
1218 dateToString( event->dtEnd(),
true, KSystemTimeZones::local() ),
1219 timeToString( event->dtEnd(),
true, KSystemTimeZones::local() ) );
1221 tmp = i18nc(
"%1: End Date",
"%1 (all day)",
1222 dateToString( event->dtEnd(),
true, KSystemTimeZones::local() ) );
1228 static QString htmlInvitationDetailsBegin()
1230 QString dir = ( QApplication::isRightToLeft() ?
"rtl" :
"ltr" );
1231 return QString(
"<div dir=\"%1\">\n" ).arg( dir );
1234 static QString htmlInvitationDetailsEnd()
1239 static QString htmlInvitationDetailsTableBegin()
1242 return "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
1246 static QString htmlInvitationDetailsTableEnd()
1248 return "</table>\n";
1251 static QString diffColor()
1256 return QColor( Qt::red ).name();
1259 static QString noteColor()
1262 return qApp->palette().color( QPalette::Active, QPalette::Highlight ).name();
1265 static QString htmlRow(
const QString &title,
const QString &value )
1267 if ( !value.isEmpty() ) {
1268 return "<tr><td>" + title +
"</td><td>" + value +
"</td></tr>\n";
1274 static QString htmlRow(
const QString &title,
const QString &value,
const QString &oldvalue )
1277 if ( value.isEmpty() ) {
1282 if ( oldvalue.isEmpty() || value == oldvalue ) {
1283 return htmlRow( title, value );
1287 QString color = diffColor();
1288 QString newtitle =
"<font color=\"" + color +
"\">" + title +
"</font>";
1289 QString newvalue =
"<font color=\"" + color +
"\">" + value +
"</font>" +
1291 "(<strike>" + oldvalue +
"</strike>)";
1292 return htmlRow( newtitle, newvalue );
1296 static Attendee::Ptr findDelegatedFromMyAttendee(
const Incidence::Ptr &incidence )
1305 KEMailSettings settings;
1306 QStringList profiles = settings.profiles();
1307 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
1308 settings.setProfile( *it );
1310 QString delegatorName, delegatorEmail;
1312 Attendee::List::ConstIterator it2;
1313 for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) {
1315 KPIMUtils::extractEmailAddressAndName( a->delegator(), delegatorEmail, delegatorName );
1316 if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) {
1325 static Attendee::Ptr findMyAttendee(
const Incidence::Ptr &incidence )
1334 KEMailSettings settings;
1335 QStringList profiles = settings.profiles();
1336 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
1337 settings.setProfile( *it );
1340 Attendee::List::ConstIterator it2;
1341 for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) {
1343 if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
1352 static Attendee::Ptr findAttendee(
const Incidence::Ptr &incidence,
1353 const QString &email )
1363 Attendee::List::ConstIterator it;
1364 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
1366 if ( email == a->email() ) {
1374 static bool rsvpRequested(
const Incidence::Ptr &incidence )
1384 Attendee::List::ConstIterator it;
1385 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
1386 if ( it == attendees.constBegin() ) {
1387 rsvp = (*it)->RSVP();
1389 if ( (*it)->RSVP() != rsvp ) {
1398 static QString rsvpRequestedStr(
bool rsvpRequested,
const QString &role )
1400 if ( rsvpRequested ) {
1401 if ( role.isEmpty() ) {
1402 return i18n(
"Your response is requested" );
1404 return i18n(
"Your response as <b>%1</b> is requested", role );
1407 if ( role.isEmpty() ) {
1408 return i18n(
"No response is necessary" );
1410 return i18n(
"No response as <b>%1</b> is necessary", role );
1415 static QString myStatusStr( Incidence::Ptr incidence )
1421 ret = i18n(
"(<b>Note</b>: the Organizer preset your response to <b>%1</b>)",
1422 Stringify::attendeeStatus( a->status() ) );
1427 static QString invitationNote(
const QString &title,
const QString ¬e,
1428 const QString &tag,
const QString &color )
1431 if ( !note.isEmpty() ) {
1432 noteStr +=
"<table border=\"0\" style=\"margin-top:4px;\">";
1433 noteStr +=
"<tr><center><td>";
1434 if ( !color.isEmpty() ) {
1435 noteStr +=
"<font color=\"" + color +
"\">";
1437 if ( !title.isEmpty() ) {
1438 if ( !tag.isEmpty() ) {
1439 noteStr += htmlAddTag( tag, title );
1444 noteStr +=
" " + note;
1445 if ( !color.isEmpty() ) {
1446 noteStr +=
"</font>";
1448 noteStr +=
"</td></center></tr>";
1449 noteStr +=
"</table>";
1454 static QString invitationPerson(
const QString &email,
const QString &name,
const QString &uid,
1455 const QString &comment )
1457 QPair<QString, QString> s = searchNameAndUid( email, name, uid );
1458 const QString printName = s.first;
1459 const QString printUid = s.second;
1461 QString personString;
1463 if ( !printUid.isEmpty() ) {
1464 personString = htmlAddUidLink( email, printName, printUid );
1467 personString = ( printName.isEmpty() ? email : printName );
1469 if ( !comment.isEmpty() ) {
1470 personString = i18nc(
"name (comment)",
"%1 (%2)", personString, comment );
1472 personString +=
'\n';
1475 if ( !email.isEmpty() ) {
1476 personString +=
" " + htmlAddMailtoLink( email, printName );
1478 personString +=
'\n';
1480 return personString;
1483 static QString invitationDetailsIncidence(
const Incidence::Ptr &incidence,
bool noHtmlMode )
1491 QStringList comments;
1493 if ( incidence->comments().isEmpty() ) {
1494 if ( !incidence->description().isEmpty() ) {
1496 if ( !incidence->descriptionIsRich() &&
1497 !incidence->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) ) {
1498 comments << string2HTML( incidence->description() );
1500 if ( !incidence->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) ) {
1501 comments << incidence->richDescription();
1503 comments << incidence->description();
1506 comments[0] = cleanHtml( comments[0] );
1508 comments[0] = htmlAddTag(
"p", comments[0] );
1514 foreach (
const QString &c, incidence->comments() ) {
1515 if ( !c.isEmpty() ) {
1517 if ( !Qt::mightBeRichText( c ) ) {
1518 comments << string2HTML( c );
1521 comments << cleanHtml( cleanHtml(
"<body>" + c +
"</body>" ) );
1528 if ( !incidence->description().isEmpty() ) {
1530 if ( !incidence->descriptionIsRich() &&
1531 !incidence->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) ) {
1532 descr = string2HTML( incidence->description() );
1534 if ( !incidence->description().startsWith( QLatin1String(
"<!DOCTYPE HTML" ) ) ) {
1535 descr = incidence->richDescription();
1537 descr = incidence->description();
1540 descr = cleanHtml( descr );
1542 descr = htmlAddTag(
"p", descr );
1547 if( !descr.isEmpty() ) {
1549 html +=
"<table border=\"0\" style=\"margin-top:4px;\">";
1550 html +=
"<tr><td><center>" +
1551 htmlAddTag(
"u", i18n(
"Description:" ) ) +
1552 "</center></td></tr>";
1553 html +=
"<tr><td>" + descr +
"</td></tr>";
1557 if ( !comments.isEmpty() ) {
1559 html +=
"<table border=\"0\" style=\"margin-top:4px;\">";
1560 html +=
"<tr><td><center>" +
1561 htmlAddTag(
"u", i18n(
"Comments:" ) ) +
1562 "</center></td></tr>";
1564 if ( comments.count() > 1 ) {
1566 for (
int i=0; i < comments.count(); ++i ) {
1567 html +=
"<li>" + comments[i] +
"</li>";
1571 html += comments[0];
1573 html +=
"</td></tr>";
1579 static QString invitationDetailsEvent(
const Event::Ptr &event,
bool noHtmlMode,
1580 KDateTime::Spec spec )
1587 QString html = htmlInvitationDetailsBegin();
1588 html += htmlInvitationDetailsTableBegin();
1591 html += htmlRow( i18n(
"What:" ), invitationSummary( event, noHtmlMode ) );
1592 html += htmlRow( i18n(
"Where:" ), invitationLocation( event, noHtmlMode ) );
1595 if ( event->dtStart().date() ==
event->dtEnd().date() ) {
1596 html += htmlRow( i18n(
"Date:" ),
dateToString( event->dtStart(),
false, spec ) );
1597 if ( !event->allDay() ) {
1598 html += htmlRow( i18n(
"Time:" ),
1604 html += htmlRow( i18nc(
"starting date",
"From:" ),
1606 if ( !event->allDay() ) {
1607 html += htmlRow( i18nc(
"starting time",
"At:" ),
1610 if ( event->hasEndDate() ) {
1611 html += htmlRow( i18nc(
"ending date",
"To:" ),
1613 if ( !event->allDay() ) {
1614 html += htmlRow( i18nc(
"ending time",
"At:" ),
1618 html += htmlRow( i18nc(
"ending date",
"To:" ), i18n(
"no end date specified" ) );
1626 if ( event->recurs() ) {
1630 html += htmlInvitationDetailsTableEnd();
1631 html += invitationDetailsIncidence( event, noHtmlMode );
1632 html += htmlInvitationDetailsEnd();
1639 KDateTime::Spec spec )
1642 return invitationDetailsEvent( event, noHtmlMode, spec );
1650 html += invitationNote( QString(),
1651 i18n(
"Please respond again to the original proposal." ),
1652 QString(), noteColor() );
1655 html += htmlInvitationDetailsBegin();
1656 html += htmlInvitationDetailsTableBegin();
1658 html += htmlRow( i18n(
"What:" ),
1659 invitationSummary( event, noHtmlMode ),
1660 invitationSummary( oldevent, noHtmlMode ) );
1662 html += htmlRow( i18n(
"Where:" ),
1663 invitationLocation( event, noHtmlMode ),
1664 invitationLocation( oldevent, noHtmlMode ) );
1667 if ( event->dtStart().date() ==
event->dtEnd().date() ) {
1668 html += htmlRow( i18n(
"Date:" ),
1671 QString spanStr, oldspanStr;
1672 if ( !event->allDay() ) {
1673 spanStr =
timeToString( event->dtStart(),
true, spec ) +
1677 if ( !oldevent->allDay() ) {
1678 oldspanStr =
timeToString( oldevent->dtStart(),
true, spec ) +
1682 html += htmlRow( i18n(
"Time:" ), spanStr, oldspanStr );
1684 html += htmlRow( i18nc(
"Starting date of an event",
"From:" ),
1687 QString startStr, oldstartStr;
1688 if ( !event->allDay() ) {
1689 startStr =
timeToString( event->dtStart(),
true, spec );
1691 if ( !oldevent->allDay() ) {
1692 oldstartStr =
timeToString( oldevent->dtStart(),
true, spec );
1694 html += htmlRow( i18nc(
"Starting time of an event",
"At:" ), startStr, oldstartStr );
1695 if ( event->hasEndDate() ) {
1696 html += htmlRow( i18nc(
"Ending date of an event",
"To:" ),
1699 QString endStr, oldendStr;
1700 if ( !event->allDay() ) {
1703 if ( !oldevent->allDay() ) {
1704 oldendStr =
timeToString( oldevent->dtEnd(),
true, spec );
1706 html += htmlRow( i18nc(
"Starting time of an event",
"At:" ), endStr, oldendStr );
1708 QString endStr = i18n(
"no end date specified" );
1710 if ( !oldevent->hasEndDate() ) {
1711 oldendStr = i18n(
"no end date specified" );
1713 oldendStr =
dateTimeToString( oldevent->dtEnd(), oldevent->allDay(), false );
1715 html += htmlRow( i18nc(
"Ending date of an event",
"To:" ), endStr, oldendStr );
1721 QString recurStr, oldrecurStr;
1722 if ( event->recurs() || oldevent->recurs() ) {
1726 html += htmlRow( i18n(
"Recurrence:" ), recurStr, oldrecurStr );
1728 html += htmlInvitationDetailsTableEnd();
1729 html += invitationDetailsIncidence( event, noHtmlMode );
1730 html += htmlInvitationDetailsEnd();
1735 static QString invitationDetailsTodo(
const Todo::Ptr &todo,
bool noHtmlMode,
1736 KDateTime::Spec spec )
1743 QString html = htmlInvitationDetailsBegin();
1744 html += htmlInvitationDetailsTableBegin();
1747 html += htmlRow( i18n(
"What:" ), invitationSummary( todo, noHtmlMode ) );
1748 html += htmlRow( i18n(
"Where:" ), invitationLocation( todo, noHtmlMode ) );
1750 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
1751 html += htmlRow( i18n(
"Start Date:" ),
dateToString( todo->dtStart(),
false, spec ) );
1752 if ( !todo->allDay() ) {
1753 html += htmlRow( i18n(
"Start Time:" ),
timeToString( todo->dtStart(),
false, spec ) );
1756 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
1757 html += htmlRow( i18n(
"Due Date:" ),
dateToString( todo->dtDue(),
false, spec ) );
1758 if ( !todo->allDay() ) {
1759 html += htmlRow( i18n(
"Due Time:" ),
timeToString( todo->dtDue(),
false, spec ) );
1762 html += htmlRow( i18n(
"Due Date:" ), i18nc(
"Due Date: None",
"None" ) );
1769 if ( todo->percentComplete() > 0 ) {
1770 html += htmlRow( i18n(
"Percent Done:" ), i18n(
"%1%", todo->percentComplete() ) );
1774 if ( todo->recurs() ) {
1778 html += htmlInvitationDetailsTableEnd();
1779 html += invitationDetailsIncidence( todo, noHtmlMode );
1780 html += htmlInvitationDetailsEnd();
1785 static QString invitationDetailsTodo(
const Todo::Ptr &todo,
const Todo::Ptr &oldtodo,
1787 KDateTime::Spec spec )
1790 return invitationDetailsTodo( todo, noHtmlMode, spec );
1798 html += invitationNote( QString(),
1799 i18n(
"Please respond again to the original proposal." ),
1800 QString(), noteColor() );
1803 html += htmlInvitationDetailsBegin();
1804 html += htmlInvitationDetailsTableBegin();
1806 html += htmlRow( i18n(
"What:" ),
1807 invitationSummary( todo, noHtmlMode ),
1808 invitationSummary( todo, noHtmlMode ) );
1810 html += htmlRow( i18n(
"Where:" ),
1811 invitationLocation( todo, noHtmlMode ),
1812 invitationLocation( oldtodo, noHtmlMode ) );
1814 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
1815 html += htmlRow( i18n(
"Start Date:" ),
1818 QString startTimeStr, oldstartTimeStr;
1819 if ( !todo->allDay() || !oldtodo->allDay() ) {
1820 startTimeStr = todo->allDay() ?
1821 i18n(
"All day" ) :
timeToString( todo->dtStart(), false, spec );
1822 oldstartTimeStr = oldtodo->allDay() ?
1823 i18n(
"All day" ) :
timeToString( oldtodo->dtStart(), false, spec );
1825 html += htmlRow( i18n(
"Start Time:" ), startTimeStr, oldstartTimeStr );
1827 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
1828 html += htmlRow( i18n(
"Due Date:" ),
1831 QString endTimeStr, oldendTimeStr;
1832 if ( !todo->allDay() || !oldtodo->allDay() ) {
1833 endTimeStr = todo->allDay() ?
1834 i18n(
"All day" ) :
timeToString( todo->dtDue(), false, spec );
1835 oldendTimeStr = oldtodo->allDay() ?
1836 i18n(
"All day" ) :
timeToString( oldtodo->dtDue(), false, spec );
1838 html += htmlRow( i18n(
"Due Time:" ), endTimeStr, oldendTimeStr );
1840 QString dueStr = i18nc(
"Due Date: None",
"None" );
1842 if ( !oldtodo->hasDueDate() || !oldtodo->dtDue().isValid() ) {
1843 olddueStr = i18nc(
"Due Date: None",
"None" );
1845 olddueStr =
dateTimeToString( oldtodo->dtDue(), oldtodo->allDay(), false );
1847 html += htmlRow( i18n(
"Due Date:" ), dueStr, olddueStr );
1852 QString completionStr, oldcompletionStr;
1853 if ( todo->percentComplete() > 0 || oldtodo->percentComplete() > 0 ) {
1854 completionStr = i18n(
"%1%", todo->percentComplete() );
1855 oldcompletionStr = i18n(
"%1%", oldtodo->percentComplete() );
1857 html += htmlRow( i18n(
"Percent Done:" ), completionStr, oldcompletionStr );
1859 QString recurStr, oldrecurStr;
1860 if ( todo->recurs() || oldtodo->recurs() ) {
1864 html += htmlRow( i18n(
"Recurrence:" ), recurStr, oldrecurStr );
1866 html += htmlInvitationDetailsTableEnd();
1867 html += invitationDetailsIncidence( todo, noHtmlMode );
1869 html += htmlInvitationDetailsEnd();
1874 static QString invitationDetailsJournal(
const Journal::Ptr &journal,
bool noHtmlMode,
1875 KDateTime::Spec spec )
1881 QString html = htmlInvitationDetailsBegin();
1882 html += htmlInvitationDetailsTableBegin();
1884 html += htmlRow( i18n(
"Summary:" ), invitationSummary( journal, noHtmlMode ) );
1885 html += htmlRow( i18n(
"Date:" ),
dateToString( journal->dtStart(),
false, spec ) );
1887 html += htmlInvitationDetailsTableEnd();
1888 html += invitationDetailsIncidence( journal, noHtmlMode );
1889 html += htmlInvitationDetailsEnd();
1894 static QString invitationDetailsJournal(
const Journal::Ptr &journal,
1896 bool noHtmlMode, KDateTime::Spec spec )
1898 if ( !oldjournal ) {
1899 return invitationDetailsJournal( journal, noHtmlMode, spec );
1902 QString html = htmlInvitationDetailsBegin();
1903 html += htmlInvitationDetailsTableBegin();
1905 html += htmlRow( i18n(
"What:" ),
1906 invitationSummary( journal, noHtmlMode ),
1907 invitationSummary( oldjournal, noHtmlMode ) );
1909 html += htmlRow( i18n(
"Date:" ),
1913 html += htmlInvitationDetailsTableEnd();
1914 html += invitationDetailsIncidence( journal, noHtmlMode );
1915 html += htmlInvitationDetailsEnd();
1920 static QString invitationDetailsFreeBusy(
const FreeBusy::Ptr &fb,
bool noHtmlMode,
1921 KDateTime::Spec spec )
1923 Q_UNUSED( noHtmlMode );
1929 QString html = htmlInvitationDetailsTableBegin();
1931 html += htmlRow( i18n(
"Person:" ), fb->organizer()->fullName() );
1932 html += htmlRow( i18n(
"Start date:" ),
dateToString( fb->dtStart(),
true, spec ) );
1933 html += htmlRow( i18n(
"End date:" ),
dateToString( fb->dtEnd(),
true, spec ) );
1935 html +=
"<tr><td colspan=2><hr></td></tr>\n";
1936 html +=
"<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
1938 Period::List periods = fb->busyPeriods();
1939 Period::List::iterator it;
1940 for ( it = periods.begin(); it != periods.end(); ++it ) {
1942 if ( per.hasDuration() ) {
1943 int dur = per.duration().asSeconds();
1945 if ( dur >= 3600 ) {
1946 cont += i18ncp(
"hours part of duration",
"1 hour ",
"%1 hours ", dur / 3600 );
1950 cont += i18ncp(
"minutes part of duration",
"1 minute",
"%1 minutes ", dur / 60 );
1954 cont += i18ncp(
"seconds part of duration",
"1 second",
"%1 seconds", dur );
1956 html += htmlRow( QString(),
1957 i18nc(
"startDate for duration",
"%1 for %2",
1959 per.start().dateTime(), KLocale::LongDate ),
1963 if ( per.start().date() == per.end().date() ) {
1964 cont = i18nc(
"date, fromTime - toTime ",
"%1, %2 - %3",
1965 KGlobal::locale()->
formatDate( per.start().date() ),
1966 KGlobal::locale()->formatTime( per.start().time() ),
1967 KGlobal::locale()->formatTime( per.end().time() ) );
1969 cont = i18nc(
"fromDateTime - toDateTime",
"%1 - %2",
1971 per.start().dateTime(), KLocale::LongDate ),
1972 KGlobal::locale()->formatDateTime(
1973 per.end().dateTime(), KLocale::LongDate ) );
1976 html += htmlRow( QString(), cont );
1980 html += htmlInvitationDetailsTableEnd();
1985 bool noHtmlMode, KDateTime::Spec spec )
1988 return invitationDetailsFreeBusy( fb, noHtmlMode, spec );
1991 static bool replyMeansCounter(
const Incidence::Ptr &incidence )
1993 Q_UNUSED( incidence );
2011 static QString invitationHeaderEvent(
const Event::Ptr &event,
2012 const Incidence::Ptr &existingIncidence,
2015 if ( !msg || !event ) {
2019 switch ( msg->method() ) {
2021 return i18n(
"This invitation has been published" );
2023 if ( existingIncidence && event->revision() > 0 ) {
2024 QString orgStr = organizerName( event, sender );
2025 if ( senderIsOrganizer( event, sender ) ) {
2026 return i18n(
"This invitation has been updated by the organizer %1", orgStr );
2028 return i18n(
"This invitation has been updated by %1 as a representative of %2",
2032 if ( iamOrganizer( event ) ) {
2033 return i18n(
"I created this invitation" );
2035 QString orgStr = organizerName( event, sender );
2036 if ( senderIsOrganizer( event, sender ) ) {
2037 return i18n(
"You received an invitation from %1", orgStr );
2039 return i18n(
"You received an invitation from %1 as a representative of %2",
2044 return i18n(
"This invitation was refreshed" );
2046 if ( iamOrganizer( event ) ) {
2047 return i18n(
"This invitation has been canceled" );
2049 return i18n(
"The organizer has revoked the invitation" );
2052 return i18n(
"Addition to the invitation" );
2055 if ( replyMeansCounter( event ) ) {
2056 return i18n(
"%1 makes this counter proposal", firstAttendeeName( event, sender ) );
2060 if( attendees.count() == 0 ) {
2061 kDebug() <<
"No attendees in the iCal reply!";
2064 if ( attendees.count() != 1 ) {
2065 kDebug() <<
"Warning: attendeecount in the reply should be 1"
2066 <<
"but is" << attendees.count();
2068 QString attendeeName = firstAttendeeName( event, sender );
2070 QString delegatorName, dummy;
2072 KPIMUtils::extractEmailAddressAndName( attendee->delegator(), dummy, delegatorName );
2073 if ( delegatorName.isEmpty() ) {
2074 delegatorName = attendee->delegator();
2077 switch( attendee->status() ) {
2079 return i18n(
"%1 indicates this invitation still needs some action", attendeeName );
2081 if ( event->revision() > 0 ) {
2082 if ( !sender.isEmpty() ) {
2083 return i18n(
"This invitation has been updated by attendee %1", sender );
2085 return i18n(
"This invitation has been updated by an attendee" );
2088 if ( delegatorName.isEmpty() ) {
2089 return i18n(
"%1 accepts this invitation", attendeeName );
2091 return i18n(
"%1 accepts this invitation on behalf of %2",
2092 attendeeName, delegatorName );
2096 if ( delegatorName.isEmpty() ) {
2097 return i18n(
"%1 tentatively accepts this invitation", attendeeName );
2099 return i18n(
"%1 tentatively accepts this invitation on behalf of %2",
2100 attendeeName, delegatorName );
2103 if ( delegatorName.isEmpty() ) {
2104 return i18n(
"%1 declines this invitation", attendeeName );
2106 return i18n(
"%1 declines this invitation on behalf of %2",
2107 attendeeName, delegatorName );
2111 QString delegate, dummy;
2112 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
2113 if ( delegate.isEmpty() ) {
2114 delegate = attendee->delegate();
2116 if ( !delegate.isEmpty() ) {
2117 return i18n(
"%1 has delegated this invitation to %2", attendeeName, delegate );
2119 return i18n(
"%1 has delegated this invitation", attendeeName );
2123 return i18n(
"This invitation is now completed" );
2125 return i18n(
"%1 is still processing the invitation", attendeeName );
2126 case Attendee::None:
2127 return i18n(
"Unknown response to this invitation" );
2132 return i18n(
"%1 makes this counter proposal",
2133 firstAttendeeName( event, i18n(
"Sender" ) ) );
2137 QString orgStr = organizerName( event, sender );
2138 if ( senderIsOrganizer( event, sender ) ) {
2139 return i18n(
"%1 declines your counter proposal", orgStr );
2141 return i18n(
"%1 declines your counter proposal on behalf of %2", sender, orgStr );
2146 return i18n(
"Error: Event iTIP message with unknown method" );
2148 kError() <<
"encountered an iTIP method that we do not support";
2152 static QString invitationHeaderTodo(
const Todo::Ptr &todo,
2153 const Incidence::Ptr &existingIncidence,
2156 if ( !msg || !todo ) {
2160 switch ( msg->method() ) {
2162 return i18n(
"This to-do has been published" );
2164 if ( existingIncidence && todo->revision() > 0 ) {
2165 QString orgStr = organizerName( todo, sender );
2166 if ( senderIsOrganizer( todo, sender ) ) {
2167 return i18n(
"This to-do has been updated by the organizer %1", orgStr );
2169 return i18n(
"This to-do has been updated by %1 as a representative of %2",
2173 if ( iamOrganizer( todo ) ) {
2174 return i18n(
"I created this to-do" );
2176 QString orgStr = organizerName( todo, sender );
2177 if ( senderIsOrganizer( todo, sender ) ) {
2178 return i18n(
"You have been assigned this to-do by %1", orgStr );
2180 return i18n(
"You have been assigned this to-do by %1 as a representative of %2",
2186 return i18n(
"This to-do was refreshed" );
2188 if ( iamOrganizer( todo ) ) {
2189 return i18n(
"This to-do was canceled" );
2191 return i18n(
"The organizer has revoked this to-do" );
2194 return i18n(
"Addition to the to-do" );
2197 if ( replyMeansCounter( todo ) ) {
2198 return i18n(
"%1 makes this counter proposal", firstAttendeeName( todo, sender ) );
2202 if ( attendees.count() == 0 ) {
2203 kDebug() <<
"No attendees in the iCal reply!";
2206 if ( attendees.count() != 1 ) {
2207 kDebug() <<
"Warning: attendeecount in the reply should be 1"
2208 <<
"but is" << attendees.count();
2210 QString attendeeName = firstAttendeeName( todo, sender );
2212 QString delegatorName, dummy;
2214 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegatorName );
2215 if ( delegatorName.isEmpty() ) {
2216 delegatorName = attendee->delegator();
2219 switch( attendee->status() ) {
2221 return i18n(
"%1 indicates this to-do assignment still needs some action",
2224 if ( todo->revision() > 0 ) {
2225 if ( !sender.isEmpty() ) {
2226 if ( todo->isCompleted() ) {
2227 return i18n(
"This to-do has been completed by assignee %1", sender );
2229 return i18n(
"This to-do has been updated by assignee %1", sender );
2232 if ( todo->isCompleted() ) {
2233 return i18n(
"This to-do has been completed by an assignee" );
2235 return i18n(
"This to-do has been updated by an assignee" );
2239 if ( delegatorName.isEmpty() ) {
2240 return i18n(
"%1 accepts this to-do", attendeeName );
2242 return i18n(
"%1 accepts this to-do on behalf of %2",
2243 attendeeName, delegatorName );
2247 if ( delegatorName.isEmpty() ) {
2248 return i18n(
"%1 tentatively accepts this to-do", attendeeName );
2250 return i18n(
"%1 tentatively accepts this to-do on behalf of %2",
2251 attendeeName, delegatorName );
2254 if ( delegatorName.isEmpty() ) {
2255 return i18n(
"%1 declines this to-do", attendeeName );
2257 return i18n(
"%1 declines this to-do on behalf of %2",
2258 attendeeName, delegatorName );
2262 QString delegate, dummy;
2263 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
2264 if ( delegate.isEmpty() ) {
2265 delegate = attendee->delegate();
2267 if ( !delegate.isEmpty() ) {
2268 return i18n(
"%1 has delegated this to-do to %2", attendeeName, delegate );
2270 return i18n(
"%1 has delegated this to-do", attendeeName );
2274 return i18n(
"The request for this to-do is now completed" );
2276 return i18n(
"%1 is still processing the to-do", attendeeName );
2277 case Attendee::None:
2278 return i18n(
"Unknown response to this to-do" );
2283 return i18n(
"%1 makes this counter proposal", firstAttendeeName( todo, sender ) );
2287 QString orgStr = organizerName( todo, sender );
2288 if ( senderIsOrganizer( todo, sender ) ) {
2289 return i18n(
"%1 declines the counter proposal", orgStr );
2291 return i18n(
"%1 declines the counter proposal on behalf of %2", sender, orgStr );
2296 return i18n(
"Error: To-do iTIP message with unknown method" );
2298 kError() <<
"encountered an iTIP method that we do not support";
2302 static QString invitationHeaderJournal(
const Journal::Ptr &journal,
2305 if ( !msg || !journal ) {
2309 switch ( msg->method() ) {
2311 return i18n(
"This journal has been published" );
2313 return i18n(
"You have been assigned this journal" );
2315 return i18n(
"This journal was refreshed" );
2317 return i18n(
"This journal was canceled" );
2319 return i18n(
"Addition to the journal" );
2322 if ( replyMeansCounter( journal ) ) {
2323 return i18n(
"Sender makes this counter proposal" );
2327 if ( attendees.count() == 0 ) {
2328 kDebug() <<
"No attendees in the iCal reply!";
2331 if( attendees.count() != 1 ) {
2332 kDebug() <<
"Warning: attendeecount in the reply should be 1 "
2333 <<
"but is " << attendees.count();
2337 switch( attendee->status() ) {
2339 return i18n(
"Sender indicates this journal assignment still needs some action" );
2341 return i18n(
"Sender accepts this journal" );
2343 return i18n(
"Sender tentatively accepts this journal" );
2345 return i18n(
"Sender declines this journal" );
2347 return i18n(
"Sender has delegated this request for the journal" );
2349 return i18n(
"The request for this journal is now completed" );
2351 return i18n(
"Sender is still processing the invitation" );
2352 case Attendee::None:
2353 return i18n(
"Unknown response to this journal" );
2358 return i18n(
"Sender makes this counter proposal" );
2360 return i18n(
"Sender declines the counter proposal" );
2362 return i18n(
"Error: Journal iTIP message with unknown method" );
2364 kError() <<
"encountered an iTIP method that we do not support";
2368 static QString invitationHeaderFreeBusy(
const FreeBusy::Ptr &fb,
2371 if ( !msg || !fb ) {
2375 switch ( msg->method() ) {
2377 return i18n(
"This free/busy list has been published" );
2379 return i18n(
"The free/busy list has been requested" );
2381 return i18n(
"This free/busy list was refreshed" );
2383 return i18n(
"This free/busy list was canceled" );
2385 return i18n(
"Addition to the free/busy list" );
2387 return i18n(
"Reply to the free/busy list" );
2389 return i18n(
"Sender makes this counter proposal" );
2391 return i18n(
"Sender declines the counter proposal" );
2393 return i18n(
"Error: Free/Busy iTIP message with unknown method" );
2395 kError() <<
"encountered an iTIP method that we do not support";
2400 static QString invitationAttendeeList(
const Incidence::Ptr &incidence )
2406 if ( incidence->type() == Incidence::TypeTodo ) {
2407 tmpStr += i18n(
"Assignees" );
2409 tmpStr += i18n(
"Invitation List" );
2414 if ( !attendees.isEmpty() ) {
2415 QStringList comments;
2416 Attendee::List::ConstIterator it;
2417 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
2419 if ( !iamAttendee( a ) ) {
2422 tmpStr +=
"<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\">";
2427 if ( attendeeIsOrganizer( incidence, a ) ) {
2428 comments << i18n(
"organizer" );
2430 if ( !a->delegator().isEmpty() ) {
2431 comments << i18n(
" (delegated by %1)", a->delegator() );
2433 if ( !a->delegate().isEmpty() ) {
2434 comments << i18n(
" (delegated to %1)", a->delegate() );
2436 tmpStr += invitationPerson( a->email(), a->name(), QString(), comments.join(
"," ) );
2443 tmpStr +=
"</table>";
2451 static QString invitationRsvpList(
const Incidence::Ptr &incidence,
const Attendee::Ptr &sender )
2457 if ( incidence->type() == Incidence::TypeTodo ) {
2458 tmpStr += i18n(
"Assignees" );
2460 tmpStr += i18n(
"Invitation List" );
2465 if ( !attendees.isEmpty() ) {
2466 QStringList comments;
2467 Attendee::List::ConstIterator it;
2468 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
2470 if ( !attendeeIsOrganizer( incidence, a ) ) {
2471 QString statusStr = Stringify::attendeeStatus( a->status () );
2472 if ( sender && ( a->email() == sender->email() ) ) {
2475 if ( a->status() != sender->status() ) {
2476 statusStr = i18n(
"%1 (<i>unrecorded</i>)",
2477 Stringify::attendeeStatus( sender->status() ) );
2483 tmpStr +=
"<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\">";
2488 if ( iamAttendee( a ) ) {
2489 comments << i18n(
"myself" );
2491 if ( !a->delegator().isEmpty() ) {
2492 comments << i18n(
" (delegated by %1)", a->delegator() );
2494 if ( !a->delegate().isEmpty() ) {
2495 comments << i18n(
" (delegated to %1)", a->delegate() );
2497 tmpStr += invitationPerson( a->email(), a->name(), QString(), comments.join(
"," ) );
2499 tmpStr +=
"<td>" + statusStr +
"</td>";
2505 tmpStr +=
"</table>";
2507 tmpStr +=
"<i> " + i18nc(
"no attendees",
"None" ) +
"</i>";
2513 static QString invitationAttachments( InvitationFormatterHelper *helper,
2514 const Incidence::Ptr &incidence )
2521 if ( incidence->type() == Incidence::TypeFreeBusy ) {
2527 if ( !attachments.isEmpty() ) {
2528 tmpStr += i18n(
"Attached Documents:" ) +
"<ol>";
2530 Attachment::List::ConstIterator it;
2531 for ( it = attachments.constBegin(); it != attachments.constEnd(); ++it ) {
2535 KMimeType::Ptr
mimeType = KMimeType::mimeType( a->mimeType() );
2536 const QString iconStr = ( mimeType ?
2537 mimeType->iconName( a->uri() ) :
2538 QString(
"application-octet-stream" ) );
2539 const QString iconPath = KIconLoader::global()->iconPath( iconStr, KIconLoader::Small );
2540 if ( !iconPath.isEmpty() ) {
2541 tmpStr +=
"<img valign=\"top\" src=\"" + iconPath +
"\">";
2543 tmpStr += helper->makeLink(
"ATTACH:" + a->label().toUtf8().toBase64(), a->label() );
2553 class KCalUtils::IncidenceFormatter::ScheduleMessageVisitor :
public Visitor
2556 ScheduleMessageVisitor() : mMessage( 0 ) { mResult =
""; }
2557 bool act(
const IncidenceBase::Ptr &incidence,
2558 const Incidence::Ptr &existingIncidence,
2561 mExistingIncidence = existingIncidence;
2564 return incidence->accept( *
this, incidence );
2566 QString result()
const {
return mResult; }
2570 Incidence::Ptr mExistingIncidence;
2575 class KCalUtils::IncidenceFormatter::InvitationHeaderVisitor :
2576 public IncidenceFormatter::ScheduleMessageVisitor
2581 mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender );
2582 return !mResult.isEmpty();
2586 mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender );
2587 return !mResult.isEmpty();
2591 mResult = invitationHeaderJournal( journal, mMessage );
2592 return !mResult.isEmpty();
2596 mResult = invitationHeaderFreeBusy( fb, mMessage );
2597 return !mResult.isEmpty();
2601 class KCalUtils::IncidenceFormatter::InvitationBodyVisitor
2602 :
public IncidenceFormatter::ScheduleMessageVisitor
2605 InvitationBodyVisitor(
bool noHtmlMode, KDateTime::Spec spec )
2606 : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ), mSpec( spec ) {}
2612 mResult = invitationDetailsEvent( event, oldevent, mMessage, mNoHtmlMode, mSpec );
2613 return !mResult.isEmpty();
2618 mResult = invitationDetailsTodo( todo, oldtodo, mMessage, mNoHtmlMode, mSpec );
2619 return !mResult.isEmpty();
2624 mResult = invitationDetailsJournal( journal, oldjournal, mNoHtmlMode, mSpec );
2625 return !mResult.isEmpty();
2629 mResult = invitationDetailsFreeBusy( fb,
FreeBusy::Ptr(), mNoHtmlMode, mSpec );
2630 return !mResult.isEmpty();
2635 KDateTime::Spec mSpec;
2639 InvitationFormatterHelper::InvitationFormatterHelper()
2644 InvitationFormatterHelper::~InvitationFormatterHelper()
2648 QString InvitationFormatterHelper::generateLinkURL(
const QString &
id )
2654 class IncidenceFormatter::IncidenceCompareVisitor :
public Visitor
2657 IncidenceCompareVisitor() {}
2658 bool act(
const IncidenceBase::Ptr &incidence,
2659 const Incidence::Ptr &existingIncidence )
2661 if ( !existingIncidence ) {
2664 Incidence::Ptr inc = incidence.staticCast<
Incidence>();
2665 if ( !inc || !existingIncidence ||
2666 inc->revision() <= existingIncidence->revision() ) {
2669 mExistingIncidence = existingIncidence;
2670 return incidence->
accept( *
this, incidence );
2673 QString result()
const
2675 if ( mChanges.isEmpty() ) {
2678 QString html =
"<div align=\"left\"><ul><li>";
2679 html += mChanges.join(
"</li><li>" );
2680 html +=
"</li><ul></div>";
2687 compareEvents( event, mExistingIncidence.dynamicCast<
Event>() );
2688 compareIncidences( event, mExistingIncidence );
2689 return !mChanges.isEmpty();
2693 compareTodos( todo, mExistingIncidence.dynamicCast<
Todo>() );
2694 compareIncidences( todo, mExistingIncidence );
2695 return !mChanges.isEmpty();
2699 compareIncidences( journal, mExistingIncidence );
2700 return !mChanges.isEmpty();
2705 return !mChanges.isEmpty();
2709 void compareEvents(
const Event::Ptr &newEvent,
2712 if ( !oldEvent || !newEvent ) {
2715 if ( oldEvent->dtStart() != newEvent->dtStart() ||
2716 oldEvent->allDay() != newEvent->allDay() ) {
2717 mChanges += i18n(
"The invitation starting time has been changed from %1 to %2",
2718 eventStartTimeStr( oldEvent ), eventStartTimeStr( newEvent ) );
2720 if ( oldEvent->dtEnd() != newEvent->dtEnd() ||
2721 oldEvent->allDay() != newEvent->allDay() ) {
2722 mChanges += i18n(
"The invitation ending time has been changed from %1 to %2",
2723 eventEndTimeStr( oldEvent ), eventEndTimeStr( newEvent ) );
2727 void compareTodos(
const Todo::Ptr &newTodo,
2730 if ( !oldTodo || !newTodo ) {
2734 if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) {
2735 mChanges += i18n(
"The to-do has been completed" );
2737 if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) {
2738 mChanges += i18n(
"The to-do is no longer completed" );
2740 if ( oldTodo->percentComplete() != newTodo->percentComplete() ) {
2741 const QString oldPer = i18n(
"%1%", oldTodo->percentComplete() );
2742 const QString newPer = i18n(
"%1%", newTodo->percentComplete() );
2743 mChanges += i18n(
"The task completed percentage has changed from %1 to %2",
2747 if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) {
2748 mChanges += i18n(
"A to-do starting time has been added" );
2750 if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) {
2751 mChanges += i18n(
"The to-do starting time has been removed" );
2753 if ( oldTodo->hasStartDate() && newTodo->hasStartDate() &&
2754 oldTodo->dtStart() != newTodo->dtStart() ) {
2755 mChanges += i18n(
"The to-do starting time has been changed from %1 to %2",
2760 if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) {
2761 mChanges += i18n(
"A to-do due time has been added" );
2763 if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) {
2764 mChanges += i18n(
"The to-do due time has been removed" );
2766 if ( oldTodo->hasDueDate() && newTodo->hasDueDate() &&
2767 oldTodo->dtDue() != newTodo->dtDue() ) {
2768 mChanges += i18n(
"The to-do due time has been changed from %1 to %2",
2774 void compareIncidences(
const Incidence::Ptr &newInc,
2775 const Incidence::Ptr &oldInc )
2777 if ( !oldInc || !newInc ) {
2781 if ( oldInc->summary() != newInc->summary() ) {
2782 mChanges += i18n(
"The summary has been changed to: \"%1\"",
2783 newInc->richSummary() );
2786 if ( oldInc->location() != newInc->location() ) {
2787 mChanges += i18n(
"The location has been changed to: \"%1\"",
2788 newInc->richLocation() );
2791 if ( oldInc->description() != newInc->description() ) {
2792 mChanges += i18n(
"The description has been changed to: \"%1\"",
2793 newInc->richDescription() );
2798 for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
2799 it != newAttendees.constEnd(); ++it ) {
2800 Attendee::Ptr oldAtt = oldInc->attendeeByMail( (*it)->email() );
2802 mChanges += i18n(
"Attendee %1 has been added", (*it)->fullName() );
2804 if ( oldAtt->status() != (*it)->status() ) {
2805 mChanges += i18n(
"The status of attendee %1 has been changed to: %2",
2806 (*it)->fullName(), Stringify::attendeeStatus( (*it)->status() ) );
2811 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
2812 it != oldAttendees.constEnd(); ++it ) {
2813 if ( !attendeeIsOrganizer( oldInc, (*it) ) ) {
2814 Attendee::Ptr newAtt = newInc->attendeeByMail( (*it)->email() );
2816 mChanges += i18n(
"Attendee %1 has been removed", (*it)->fullName() );
2823 Incidence::Ptr mExistingIncidence;
2824 QStringList mChanges;
2828 QString InvitationFormatterHelper::makeLink(
const QString &
id,
const QString &text )
2830 if ( !
id.startsWith( QLatin1String(
"ATTACH:" ) ) ) {
2831 QString res = QString(
"<a href=\"%1\"><font size=\"-1\"><b>%2</b></font></a>" ).
2832 arg( generateLinkURL(
id ), text );
2836 QString res = QString(
"<a href=\"%1\">%2</a>" ).
2837 arg( generateLinkURL(
id ), text );
2844 static bool incidenceOwnedByMe(
const Calendar::Ptr &calendar,
2845 const Incidence::Ptr &incidence )
2847 Q_UNUSED( calendar );
2848 Q_UNUSED( incidence );
2852 static QString inviteButton( InvitationFormatterHelper *helper,
2853 const QString &
id,
const QString &text )
2860 html +=
"<td style=\"border-width:2px;border-style:outset\">";
2861 if ( !
id.isEmpty() ) {
2862 html += helper->makeLink(
id, text );
2870 static QString inviteLink( InvitationFormatterHelper *helper,
2871 const QString &
id,
const QString &text )
2875 if ( helper && !
id.isEmpty() ) {
2876 html += helper->makeLink(
id, text );
2883 static QString responseButtons(
const Incidence::Ptr &incidence,
2884 bool rsvpReq,
bool rsvpRec,
2885 InvitationFormatterHelper *helper )
2892 if ( !rsvpReq && ( incidence && incidence->revision() == 0 ) ) {
2894 html += inviteButton( helper,
"record", i18n(
"Record" ) );
2897 html += inviteButton( helper,
"delete", i18n(
"Move to Trash" ) );
2902 html += inviteButton( helper,
"accept",
2903 i18nc(
"accept invitation",
"Accept" ) );
2906 html += inviteButton( helper,
"accept_conditionally",
2907 i18nc(
"Accept invitation conditionally",
"Accept cond." ) );
2910 html += inviteButton( helper,
"counter",
2911 i18nc(
"invitation counter proposal",
"Counter proposal" ) );
2914 html += inviteButton( helper,
"decline",
2915 i18nc(
"decline invitation",
"Decline" ) );
2918 if ( !rsvpRec || ( incidence && incidence->revision() > 0 ) ) {
2920 html += inviteButton( helper,
"delegate",
2921 i18nc(
"delegate inviation to another",
"Delegate" ) );
2924 html += inviteButton( helper,
"forward", i18nc(
"forward request to another",
"Forward" ) );
2927 if ( incidence && incidence->type() == Incidence::TypeEvent ) {
2928 html += inviteButton( helper,
"check_calendar",
2929 i18nc(
"look for scheduling conflicts",
"Check my calendar" ) );
2935 static QString counterButtons(
const Incidence::Ptr &incidence,
2936 InvitationFormatterHelper *helper )
2944 html += inviteButton( helper,
"accept_counter", i18n(
"Accept" ) );
2947 html += inviteButton( helper,
"decline_counter", i18n(
"Decline" ) );
2951 if ( incidence->type() == Incidence::TypeTodo ) {
2952 html += inviteButton( helper,
"check_calendar", i18n(
"Check my to-do list" ) );
2954 html += inviteButton( helper,
"check_calendar", i18n(
"Check my calendar" ) );
2960 static QString recordButtons(
const Incidence::Ptr &incidence,
2961 InvitationFormatterHelper *helper )
2969 if ( incidence->type() == Incidence::TypeTodo ) {
2970 html += inviteLink( helper,
"reply",
2971 i18n(
"Record invitation in my to-do list" ) );
2973 html += inviteLink( helper,
"reply",
2974 i18n(
"Record invitation in my calendar" ) );
2980 static QString recordResponseButtons(
const Incidence::Ptr &incidence,
2981 InvitationFormatterHelper *helper )
2989 if ( incidence->type() == Incidence::TypeTodo ) {
2990 html += inviteLink( helper,
"reply",
2991 i18n(
"Record response in my to-do list" ) );
2993 html += inviteLink( helper,
"reply",
2994 i18n(
"Record response in my calendar" ) );
3000 static QString cancelButtons(
const Incidence::Ptr &incidence,
3001 InvitationFormatterHelper *helper )
3010 if ( incidence->type() == Incidence::TypeTodo ) {
3011 html += inviteButton( helper,
"cancel",
3012 i18n(
"Remove invitation from my to-do list" ) );
3014 html += inviteButton( helper,
"cancel",
3015 i18n(
"Remove invitation from my calendar" ) );
3026 static QString formatICalInvitationHelper( QString invitation,
3028 InvitationFormatterHelper *helper,
3030 KDateTime::Spec spec,
3031 const QString &sender,
3032 bool outlookCompareStyle )
3034 if ( invitation.isEmpty() ) {
3044 kDebug() <<
"Failed to parse the scheduling message";
3050 IncidenceBase::Ptr incBase = msg->event();
3052 incBase->shiftTimes( mCalendar->timeSpec(), KDateTime::Spec::LocalZone() );
3055 Incidence::Ptr existingIncidence;
3056 if ( incBase && helper->calendar() ) {
3057 existingIncidence = helper->calendar()->incidence( incBase->uid() );
3059 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
3060 existingIncidence.clear();
3062 if ( !existingIncidence ) {
3063 const Incidence::List list = helper->calendar()->incidences();
3064 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
3065 if ( (*it)->schedulingID() == incBase->uid() &&
3066 incidenceOwnedByMe( helper->calendar(), *it ) ) {
3067 existingIncidence = *it;
3074 Incidence::Ptr inc = incBase.staticCast<
Incidence>();
3078 int incRevision = 0;
3079 if ( inc && inc->type() != Incidence::TypeFreeBusy ) {
3080 incRevision = inc->revision();
3085 html +=
"<div align=\"center\" style=\"border:solid 1px;\">";
3087 IncidenceFormatter::InvitationHeaderVisitor headerVisitor;
3089 if ( !headerVisitor.act( inc, existingIncidence, msg, sender ) ) {
3092 html += htmlAddTag(
"h3", headerVisitor.result() );
3094 if ( outlookCompareStyle ||
3097 IncidenceFormatter::InvitationBodyVisitor bodyVisitor( noHtmlMode, spec );
3101 if ( inc && existingIncidence &&
3102 incRevision < existingIncidence->revision() ) {
3103 bodyOk = bodyVisitor.act( existingIncidence, inc, msg, sender );
3105 bodyOk = bodyVisitor.act( inc, existingIncidence, msg, sender );
3108 bodyOk = bodyVisitor.act( inc, Incidence::Ptr(), msg, sender );
3111 html += bodyVisitor.result();
3117 InvitationBodyVisitor bodyVisitor( noHtmlMode, spec );
3118 if ( !bodyVisitor.act( inc, Incidence::Ptr(), msg, sender ) ) {
3121 html += bodyVisitor.result();
3124 IncidenceFormatter::IncidenceCompareVisitor compareVisitor;
3125 if ( compareVisitor.act( inc, existingIncidence ) ) {
3126 html +=
"<p align=\"left\">";
3127 if ( senderIsOrganizer( inc, sender ) ) {
3128 html += i18n(
"The following changes have been made by the organizer:" );
3129 }
else if ( !sender.isEmpty() ) {
3130 html += i18n(
"The following changes have been made by %1:", sender );
3132 html += i18n(
"The following changes have been made:" );
3135 html += compareVisitor.result();
3139 IncidenceCompareVisitor compareVisitor;
3140 if ( compareVisitor.act( inc, existingIncidence ) ) {
3141 html +=
"<p align=\"left\">";
3142 if ( !sender.isEmpty() ) {
3143 html += i18n(
"The following changes have been made by %1:", sender );
3145 html += i18n(
"The following changes have been made by an attendee:" );
3148 html += compareVisitor.result();
3154 bool myInc = iamOrganizer( inc );
3157 bool rsvpRec =
false;
3160 Incidence::Ptr rsvpIncidence = existingIncidence;
3161 if ( !rsvpIncidence && inc && incRevision > 0 ) {
3162 rsvpIncidence = inc;
3164 if ( rsvpIncidence ) {
3165 ea = findMyAttendee( rsvpIncidence );
3168 ( ea->status() == Attendee::Accepted ||
3169 ea->status() == Attendee::Declined ||
3170 ea->status() == Attendee::Tentative ) ) {
3177 bool isDelegated =
false;
3180 if ( !inc->attendees().isEmpty() ) {
3181 a = inc->attendees().first();
3185 isDelegated = ( a->status() == Attendee::Delegated );
3186 role = Stringify::attendeeRole( a->role() );
3190 bool rsvpReq = rsvpRequested( inc );
3191 if ( !myInc && a ) {
3193 if ( rsvpRec && inc ) {
3194 if ( incRevision == 0 ) {
3195 tStr = i18n(
"Your <b>%1</b> response has been recorded",
3196 Stringify::attendeeStatus( ea->status() ) );
3198 tStr = i18n(
"Your status for this invitation is <b>%1</b>",
3199 Stringify::attendeeStatus( ea->status() ) );
3203 tStr = i18n(
"This invitation was canceled" );
3204 }
else if ( msg->method() ==
iTIPAdd ) {
3205 tStr = i18n(
"This invitation was accepted" );
3208 tStr = rsvpRequestedStr( rsvpReq, role );
3210 if ( !isDelegated ) {
3211 tStr = rsvpRequestedStr( rsvpReq, role );
3213 tStr = i18n(
"Awaiting delegation response" );
3217 html +=
"<i><u>" + tStr +
"</u></i>";
3222 if ( inc && incRevision == 0 ) {
3223 QString statStr = myStatusStr( inc );
3224 if ( !statStr.isEmpty() ) {
3226 html +=
"<i>" + statStr +
"</i>";
3234 html +=
"<table border=\"0\" align=\"center\" cellspacing=\"4\"><tr>";
3236 switch ( msg->method() ) {
3242 if ( inc && incRevision > 0 && ( existingIncidence || !helper->calendar() ) ) {
3243 html += recordButtons( inc, helper );
3248 html += responseButtons( inc, rsvpReq, rsvpRec, helper );
3250 html += responseButtons( inc,
false,
false, helper );
3257 html += cancelButtons( inc, helper );
3267 if ( replyMeansCounter( inc ) ) {
3268 html +=
"<tr>" + counterButtons( inc, helper ) +
"</tr>";
3277 a = findDelegatedFromMyAttendee( inc );
3279 if ( a->status() != Attendee::Accepted ||
3280 a->status() != Attendee::Tentative ) {
3281 html += responseButtons( inc, rsvpReq, rsvpRec, helper );
3287 if ( !inc->attendees().isEmpty() ) {
3288 a = inc->attendees().first();
3290 if ( a && helper->calendar() ) {
3291 ea = findAttendee( existingIncidence, a->email() );
3294 if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
3295 const QString tStr = i18n(
"The <b>%1</b> response has been recorded",
3296 Stringify::attendeeStatus( ea->status() ) );
3297 html += inviteButton( helper, QString(), htmlAddTag(
"i", tStr ) );
3300 html += recordResponseButtons( inc, helper );
3308 html += counterButtons( inc, helper );
3312 html += responseButtons( inc, rsvpReq, rsvpRec, helper );
3320 html +=
"</tr></table>";
3324 html += invitationRsvpList( existingIncidence, a );
3326 html += invitationAttendeeList( inc );
3333 html += invitationAttachments( helper, inc );
3341 InvitationFormatterHelper *helper,
3342 bool outlookCompareStyle )
3344 return formatICalInvitationHelper( invitation, calendar, helper,
false,
3345 KSystemTimeZones::local(), QString(),
3346 outlookCompareStyle );
3351 InvitationFormatterHelper *helper,
3352 const QString &sender,
3353 bool outlookCompareStyle )
3355 return formatICalInvitationHelper( invitation, calendar, helper,
true,
3356 KSystemTimeZones::local(), sender,
3357 outlookCompareStyle );
3365 class KCalUtils::IncidenceFormatter::ToolTipVisitor :
public Visitor
3369 : mRichText( true ), mSpec( KDateTime::Spec() ), mResult(
"" ) {}
3372 const IncidenceBase::Ptr &incidence,
3373 const QDate &date=QDate(),
bool richText=
true,
3374 KDateTime::Spec spec=KDateTime::Spec() )
3376 mCalendar = calendar;
3379 mRichText = richText;
3382 return incidence ? incidence->
accept( *
this, incidence ) :
false;
3385 bool act(
const QString &location,
const IncidenceBase::Ptr &incidence,
3386 const QDate &date=QDate(),
bool richText=
true,
3387 KDateTime::Spec spec=KDateTime::Spec() )
3389 mLocation = location;
3391 mRichText = richText;
3394 return incidence ? incidence->
accept( *
this, incidence ) :
false;
3397 QString result()
const {
return mResult; }
3405 QString dateRangeText(
const Event::Ptr &event,
const QDate &date );
3406 QString dateRangeText(
const Todo::Ptr &todo,
const QDate &date );
3410 QString generateToolTip(
const Incidence::Ptr &incidence, QString dtRangeText );
3417 KDateTime::Spec mSpec;
3421 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const Event::Ptr &event,
3428 KDateTime startDt =
event->dtStart();
3429 KDateTime endDt =
event->dtEnd();
3430 if ( event->recurs() ) {
3431 if ( date.isValid() ) {
3432 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
3433 int diffDays = startDt.daysTo( kdt );
3434 kdt = kdt.addSecs( -1 );
3435 startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
3436 if ( event->hasEndDate() ) {
3437 endDt = endDt.addDays( diffDays );
3438 if ( startDt > endDt ) {
3439 startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
3440 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
3446 if ( event->isMultiDay() ) {
3448 ret +=
"<br>" + i18nc(
"Event start",
"<i>From:</i> %1", tmp );
3451 ret +=
"<br>" + i18nc(
"Event end",
"<i>To:</i> %1", tmp );
3456 i18n(
"<i>Date:</i> %1",
dateToString( startDt,
false, mSpec ) );
3457 if ( !event->allDay() ) {
3458 const QString dtStartTime =
timeToString( startDt,
true, mSpec );
3459 const QString dtEndTime =
timeToString( endDt,
true, mSpec );
3460 if ( dtStartTime == dtEndTime ) {
3463 i18nc(
"time for event",
"<i>Time:</i> %1",
3467 i18nc(
"time range for event",
3468 "<i>Time:</i> %1 - %2",
3469 dtStartTime, dtEndTime );
3474 return ret.replace(
' ',
" " );
3477 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const Todo::Ptr &todo,
3482 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
3483 KDateTime startDt = todo->dtStart();
3484 if ( todo->recurs() ) {
3485 if ( date.isValid() ) {
3486 startDt.setDate( date );
3490 i18n(
"<i>Start:</i> %1",
dateToString( startDt,
false, mSpec ) );
3493 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
3494 KDateTime dueDt = todo->dtDue();
3495 if ( todo->recurs() ) {
3496 if ( date.isValid() ) {
3497 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
3498 kdt = kdt.addSecs( -1 );
3499 dueDt.setDate( todo->recurrence()->getNextDateTime( kdt ).date() );
3503 i18n(
"<i>Due:</i> %1",
3509 if ( todo->priority() > 0 ) {
3511 ret +=
"<i>" + i18n(
"Priority:" ) +
"</i>" +
" ";
3512 ret += QString::number( todo->priority() );
3516 if ( todo->isCompleted() ) {
3517 ret +=
"<i>" + i18nc(
"Completed: date",
"Completed:" ) +
"</i>" +
" ";
3520 ret +=
"<i>" + i18n(
"Percent Done:" ) +
"</i>" +
" ";
3521 ret += i18n(
"%1%", todo->percentComplete() );
3524 return ret.replace(
' ',
" " );
3527 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const Journal::Ptr &journal )
3531 if ( journal->dtStart().isValid() ) {
3533 i18n(
"<i>Date:</i> %1",
dateToString( journal->dtStart(),
false, mSpec ) );
3535 return ret.replace(
' ',
" " );
3538 QString IncidenceFormatter::ToolTipVisitor::dateRangeText(
const FreeBusy::Ptr &fb )
3543 i18n(
"<i>Period start:</i> %1",
3546 i18n(
"<i>Period start:</i> %1",
3548 return ret.replace(
' ',
" " );
3553 mResult = generateToolTip( event, dateRangeText( event, mDate ) );
3554 return !mResult.isEmpty();
3559 mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
3560 return !mResult.isEmpty();
3565 mResult = generateToolTip( journal, dateRangeText( journal ) );
3566 return !mResult.isEmpty();
3572 mResult =
"<qt><b>" +
3573 i18n(
"Free/Busy information for %1", fb->organizer()->fullName() ) +
3575 mResult += dateRangeText( fb );
3577 return !mResult.isEmpty();
3580 static QString tooltipPerson(
const QString &email,
const QString &name,
Attendee::PartStat status )
3583 const QString printName = searchName( email, name );
3586 const QString iconPath = rsvpStatusIconPath( status );
3589 QString personString;
3590 if ( !iconPath.isEmpty() ) {
3591 personString +=
"<img valign=\"top\" src=\"" + iconPath +
"\">" +
" ";
3593 if ( status != Attendee::None ) {
3594 personString += i18nc(
"attendee name (attendee status)",
"%1 (%2)",
3595 printName.isEmpty() ? email : printName,
3596 Stringify::attendeeStatus( status ) );
3598 personString += i18n(
"%1", printName.isEmpty() ? email : printName );
3600 return personString;
3603 static QString tooltipFormatOrganizer(
const QString &email,
const QString &name )
3606 const QString printName = searchName( email, name );
3609 const QString iconPath =
3610 KIconLoader::global()->iconPath(
"meeting-organizer", KIconLoader::Small );
3613 QString personString;
3614 personString +=
"<img valign=\"top\" src=\"" + iconPath +
"\">" +
" ";
3615 personString += ( printName.isEmpty() ? email : printName );
3616 return personString;
3619 static QString tooltipFormatAttendeeRoleList(
const Incidence::Ptr &incidence,
3623 const QString etc = i18nc(
"elipsis",
"..." );
3627 Attendee::List::ConstIterator it;
3630 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
3632 if ( a->role() != role ) {
3636 if ( attendeeIsOrganizer( incidence, a ) ) {
3640 if ( i == maxNumAtts ) {
3641 tmpStr +=
" " + etc;
3644 tmpStr +=
" " + tooltipPerson( a->email(), a->name(),
3645 showStatus ? a->status() : Attendee::None );
3646 if ( !a->delegator().isEmpty() ) {
3647 tmpStr += i18n(
" (delegated by %1)", a->delegator() );
3649 if ( !a->delegate().isEmpty() ) {
3650 tmpStr += i18n(
" (delegated to %1)", a->delegate() );
3655 if ( tmpStr.endsWith( QLatin1String(
"<br>" ) ) ) {
3661 static QString tooltipFormatAttendees(
const Calendar::Ptr &calendar,
3662 const Incidence::Ptr &incidence )
3664 QString tmpStr, str;
3667 int attendeeCount = incidence->attendees().count();
3668 if ( attendeeCount > 1 ||
3669 ( attendeeCount == 1 &&
3670 !attendeeIsOrganizer( incidence, incidence->attendees().first() ) ) ) {
3671 tmpStr +=
"<i>" + i18n(
"Organizer:" ) +
"</i>" +
"<br>";
3672 tmpStr +=
" " + tooltipFormatOrganizer( incidence->organizer()->email(),
3673 incidence->organizer()->name() );
3678 const bool showStatus = attendeeCount > 0 && incOrganizerOwnsCalendar( calendar, incidence );
3681 str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair, showStatus );
3682 if ( !str.isEmpty() ) {
3683 tmpStr +=
"<br><i>" + i18n(
"Chair:" ) +
"</i>" +
"<br>";
3688 str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant, showStatus );
3689 if ( !str.isEmpty() ) {
3690 tmpStr +=
"<br><i>" + i18n(
"Required Participants:" ) +
"</i>" +
"<br>";
3695 str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant, showStatus );
3696 if ( !str.isEmpty() ) {
3697 tmpStr +=
"<br><i>" + i18n(
"Optional Participants:" ) +
"</i>" +
"<br>";
3702 str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant, showStatus );
3703 if ( !str.isEmpty() ) {
3704 tmpStr +=
"<br><i>" + i18n(
"Observers:" ) +
"</i>" +
"<br>";
3711 QString IncidenceFormatter::ToolTipVisitor::generateToolTip(
const Incidence::Ptr &incidence,
3712 QString dtRangeText )
3714 int maxDescLen = 120;
3721 QString tmp =
"<qt>";
3724 tmp +=
"<b>" + incidence->richSummary() +
"</b>";
3727 QString calStr = mLocation;
3731 if ( !calStr.isEmpty() ) {
3732 tmp +=
"<i>" + i18n(
"Calendar:" ) +
"</i>" +
" ";
3738 if ( !incidence->location().isEmpty() ) {
3740 tmp +=
"<i>" + i18n(
"Location:" ) +
"</i>" +
" ";
3741 tmp += incidence->richLocation();
3745 if ( !durStr.isEmpty() ) {
3747 tmp +=
"<i>" + i18n(
"Duration:" ) +
"</i>" +
" ";
3751 if ( incidence->recurs() ) {
3753 tmp +=
"<i>" + i18n(
"Recurrence:" ) +
"</i>" +
" ";
3757 if ( incidence->hasRecurrenceId() ) {
3759 tmp +=
"<i>" + i18n(
"Recurrence:" ) +
"</i>" +
" ";
3760 tmp += i18n(
"Exception" );
3763 if ( !incidence->description().isEmpty() ) {
3764 QString desc( incidence->description() );
3765 if ( !incidence->descriptionIsRich() ) {
3766 if ( desc.length() > maxDescLen ) {
3767 desc = desc.left( maxDescLen ) + i18nc(
"elipsis",
"..." );
3769 desc = Qt::escape( desc ).replace(
'\n',
"<br>" );
3774 tmp +=
"<i>" + i18n(
"Description:" ) +
"</i>" +
"<br>";
3779 int reminderCount = incidence->alarms().count();
3780 if ( reminderCount > 0 && incidence->hasEnabledAlarms() ) {
3782 tmp +=
"<i>" + i18np(
"Reminder:",
"Reminders:", reminderCount ) +
"</i>" +
" ";
3787 tmp += tooltipFormatAttendees( mCalendar, incidence );
3789 int categoryCount = incidence->categories().count();
3790 if ( categoryCount > 0 ) {
3792 tmp +=
"<i>" + i18np(
"Category:",
"Categories:", categoryCount ) +
"</i>" +
" ";
3793 tmp += incidence->categories().join(
", " );
3802 const IncidenceBase::Ptr &incidence,
3805 KDateTime::Spec spec )
3808 if ( incidence && v.act( sourceName, incidence, date, richText, spec ) ) {
3820 static QString mailBodyIncidence(
const Incidence::Ptr &incidence )
3823 if ( !incidence->summary().isEmpty() ) {
3824 body += i18n(
"Summary: %1\n", incidence->richSummary() );
3826 if ( !incidence->organizer()->isEmpty() ) {
3827 body += i18n(
"Organizer: %1\n", incidence->organizer()->fullName() );
3829 if ( !incidence->location().isEmpty() ) {
3830 body += i18n(
"Location: %1\n", incidence->richLocation() );
3837 class KCalUtils::IncidenceFormatter::MailBodyVisitor :
public Visitor
3841 : mSpec( KDateTime::Spec() ), mResult(
"" ) {}
3843 bool act( IncidenceBase::Ptr incidence, KDateTime::Spec spec=KDateTime::Spec() )
3847 return incidence ? incidence->accept( *
this, incidence ) :
false;
3849 QString result()
const
3860 mResult = i18n(
"This is a Free Busy Object" );
3861 return !mResult.isEmpty();
3864 KDateTime::Spec mSpec;
3870 QString recurrence[]= {
3871 i18nc(
"no recurrence",
"None" ),
3872 i18nc(
"event recurs by minutes",
"Minutely" ),
3873 i18nc(
"event recurs by hours",
"Hourly" ),
3874 i18nc(
"event recurs by days",
"Daily" ),
3875 i18nc(
"event recurs by weeks",
"Weekly" ),
3876 i18nc(
"event recurs same position (e.g. first monday) each month",
"Monthly Same Position" ),
3877 i18nc(
"event recurs same day each month",
"Monthly Same Day" ),
3878 i18nc(
"event recurs same month each year",
"Yearly Same Month" ),
3879 i18nc(
"event recurs same day each year",
"Yearly Same Day" ),
3880 i18nc(
"event recurs same position (e.g. first monday) each year",
"Yearly Same Position" )
3883 mResult = mailBodyIncidence( event );
3884 mResult += i18n(
"Start Date: %1\n",
dateToString( event->dtStart(),
true, mSpec ) );
3885 if ( !event->allDay() ) {
3886 mResult += i18n(
"Start Time: %1\n",
timeToString( event->dtStart(),
true, mSpec ) );
3888 if ( event->dtStart() !=
event->dtEnd() ) {
3889 mResult += i18n(
"End Date: %1\n",
dateToString( event->dtEnd(),
true, mSpec ) );
3891 if ( !event->allDay() ) {
3892 mResult += i18n(
"End Time: %1\n",
timeToString( event->dtEnd(),
true, mSpec ) );
3894 if ( event->recurs() ) {
3897 mResult += i18n(
"Recurs: %1\n", recurrence[ recur->
recurrenceType() ] );
3898 mResult += i18n(
"Frequency: %1\n", event->recurrence()->frequency() );
3901 mResult += i18np(
"Repeats once",
"Repeats %1 times", recur->
duration() );
3907 if ( event->allDay() ) {
3908 endstr = KGlobal::locale()->formatDate( recur->
endDate() );
3910 endstr = KGlobal::locale()->formatDateTime( recur->
endDateTime().dateTime() );
3912 mResult += i18n(
"Repeat until: %1\n", endstr );
3914 mResult += i18n(
"Repeats forever\n" );
3919 QString details =
event->richDescription();
3920 if ( !details.isEmpty() ) {
3921 mResult += i18n(
"Details:\n%1\n", details );
3923 return !mResult.isEmpty();
3928 mResult = mailBodyIncidence( todo );
3930 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
3931 mResult += i18n(
"Start Date: %1\n",
dateToString( todo->dtStart(
false ),
true, mSpec ) );
3932 if ( !todo->allDay() ) {
3933 mResult += i18n(
"Start Time: %1\n",
timeToString( todo->dtStart(
false ),
true, mSpec ) );
3936 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
3937 mResult += i18n(
"Due Date: %1\n",
dateToString( todo->dtDue(),
true, mSpec ) );
3938 if ( !todo->allDay() ) {
3939 mResult += i18n(
"Due Time: %1\n",
timeToString( todo->dtDue(),
true, mSpec ) );
3942 QString details = todo->richDescription();
3943 if ( !details.isEmpty() ) {
3944 mResult += i18n(
"Details:\n%1\n", details );
3946 return !mResult.isEmpty();
3951 mResult = mailBodyIncidence( journal );
3952 mResult += i18n(
"Date: %1\n",
dateToString( journal->dtStart(),
true, mSpec ) );
3953 if ( !journal->allDay() ) {
3954 mResult += i18n(
"Time: %1\n",
timeToString( journal->dtStart(),
true, mSpec ) );
3956 if ( !journal->description().isEmpty() ) {
3957 mResult += i18n(
"Text of the journal:\n%1\n", journal->richDescription() );
3959 return !mResult.isEmpty();
3964 KDateTime::Spec spec )
3971 if ( v.act( incidence, spec ) ) {
3978 static QString recurEnd(
const Incidence::Ptr &incidence )
3981 if ( incidence->allDay() ) {
3982 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
3984 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
3996 if ( incidence->hasRecurrenceId() ) {
3997 return QLatin1String(
"Recurrence exception" );
4000 if ( !incidence->recurs() ) {
4001 return i18n(
"No recurrence" );
4003 static QStringList dayList;
4004 if ( dayList.isEmpty() ) {
4005 dayList.append( i18n(
"31st Last" ) );
4006 dayList.append( i18n(
"30th Last" ) );
4007 dayList.append( i18n(
"29th Last" ) );
4008 dayList.append( i18n(
"28th Last" ) );
4009 dayList.append( i18n(
"27th Last" ) );
4010 dayList.append( i18n(
"26th Last" ) );
4011 dayList.append( i18n(
"25th Last" ) );
4012 dayList.append( i18n(
"24th Last" ) );
4013 dayList.append( i18n(
"23rd Last" ) );
4014 dayList.append( i18n(
"22nd Last" ) );
4015 dayList.append( i18n(
"21st Last" ) );
4016 dayList.append( i18n(
"20th Last" ) );
4017 dayList.append( i18n(
"19th Last" ) );
4018 dayList.append( i18n(
"18th Last" ) );
4019 dayList.append( i18n(
"17th Last" ) );
4020 dayList.append( i18n(
"16th Last" ) );
4021 dayList.append( i18n(
"15th Last" ) );
4022 dayList.append( i18n(
"14th Last" ) );
4023 dayList.append( i18n(
"13th Last" ) );
4024 dayList.append( i18n(
"12th Last" ) );
4025 dayList.append( i18n(
"11th Last" ) );
4026 dayList.append( i18n(
"10th Last" ) );
4027 dayList.append( i18n(
"9th Last" ) );
4028 dayList.append( i18n(
"8th Last" ) );
4029 dayList.append( i18n(
"7th Last" ) );
4030 dayList.append( i18n(
"6th Last" ) );
4031 dayList.append( i18n(
"5th Last" ) );
4032 dayList.append( i18n(
"4th Last" ) );
4033 dayList.append( i18n(
"3rd Last" ) );
4034 dayList.append( i18n(
"2nd Last" ) );
4035 dayList.append( i18nc(
"last day of the month",
"Last" ) );
4036 dayList.append( i18nc(
"unknown day of the month",
"unknown" ) );
4037 dayList.append( i18n(
"1st" ) );
4038 dayList.append( i18n(
"2nd" ) );
4039 dayList.append( i18n(
"3rd" ) );
4040 dayList.append( i18n(
"4th" ) );
4041 dayList.append( i18n(
"5th" ) );
4042 dayList.append( i18n(
"6th" ) );
4043 dayList.append( i18n(
"7th" ) );
4044 dayList.append( i18n(
"8th" ) );
4045 dayList.append( i18n(
"9th" ) );
4046 dayList.append( i18n(
"10th" ) );
4047 dayList.append( i18n(
"11th" ) );
4048 dayList.append( i18n(
"12th" ) );
4049 dayList.append( i18n(
"13th" ) );
4050 dayList.append( i18n(
"14th" ) );
4051 dayList.append( i18n(
"15th" ) );
4052 dayList.append( i18n(
"16th" ) );
4053 dayList.append( i18n(
"17th" ) );
4054 dayList.append( i18n(
"18th" ) );
4055 dayList.append( i18n(
"19th" ) );
4056 dayList.append( i18n(
"20th" ) );
4057 dayList.append( i18n(
"21st" ) );
4058 dayList.append( i18n(
"22nd" ) );
4059 dayList.append( i18n(
"23rd" ) );
4060 dayList.append( i18n(
"24th" ) );
4061 dayList.append( i18n(
"25th" ) );
4062 dayList.append( i18n(
"26th" ) );
4063 dayList.append( i18n(
"27th" ) );
4064 dayList.append( i18n(
"28th" ) );
4065 dayList.append( i18n(
"29th" ) );
4066 dayList.append( i18n(
"30th" ) );
4067 dayList.append( i18n(
"31st" ) );
4070 const int weekStart = KGlobal::locale()->weekStartDay();
4072 const KCalendarSystem *calSys = KGlobal::locale()->calendar();
4076 QString txt, recurStr;
4077 static QString noRecurrence = i18n(
"No recurrence" );
4079 case Recurrence::rNone:
4080 return noRecurrence;
4082 case Recurrence::rMinutely:
4084 recurStr = i18np(
"Recurs every minute until %2",
4085 "Recurs every %1 minutes until %2",
4086 recur->
frequency(), recurEnd( incidence ) );
4088 recurStr += i18nc(
"number of occurrences",
4089 " (<numid>%1</numid> occurrences)",
4093 recurStr = i18np(
"Recurs every minute",
4094 "Recurs every %1 minutes", recur->
frequency() );
4098 case Recurrence::rHourly:
4100 recurStr = i18np(
"Recurs hourly until %2",
4101 "Recurs every %1 hours until %2",
4102 recur->
frequency(), recurEnd( incidence ) );
4104 recurStr += i18nc(
"number of occurrences",
4105 " (<numid>%1</numid> occurrences)",
4109 recurStr = i18np(
"Recurs hourly",
"Recurs every %1 hours", recur->
frequency() );
4113 case Recurrence::rDaily:
4115 recurStr = i18np(
"Recurs daily until %2",
4116 "Recurs every %1 days until %2",
4117 recur->
frequency(), recurEnd( incidence ) );
4119 recurStr += i18nc(
"number of occurrences",
4120 " (<numid>%1</numid> occurrences)",
4124 recurStr = i18np(
"Recurs daily",
"Recurs every %1 days", recur->
frequency() );
4128 case Recurrence::rWeekly:
4130 bool addSpace =
false;
4131 for (
int i = 0; i < 7; ++i ) {
4132 if ( recur->
days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
4134 dayNames.append( i18nc(
"separator for list of days",
", " ) );
4136 dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1,
4137 KCalendarSystem::ShortDayName ) );
4141 if ( dayNames.isEmpty() ) {
4142 dayNames = i18nc(
"Recurs weekly on no days",
"no days" );
4145 recurStr = i18ncp(
"Recurs weekly on [list of days] until end-date",
4146 "Recurs weekly on %2 until %3",
4147 "Recurs every <numid>%1</numid> weeks on %2 until %3",
4148 recur->
frequency(), dayNames, recurEnd( incidence ) );
4150 recurStr += i18nc(
"number of occurrences",
4151 " (<numid>%1</numid> occurrences)",
4155 recurStr = i18ncp(
"Recurs weekly on [list of days]",
4156 "Recurs weekly on %2",
4157 "Recurs every <numid>%1</numid> weeks on %2",
4162 case Recurrence::rMonthlyPos:
4167 recurStr = i18ncp(
"Recurs every N months on the [2nd|3rd|...]"
4168 " weekdayname until end-date",
4169 "Recurs every month on the %2 %3 until %4",
4170 "Recurs every <numid>%1</numid> months on the %2 %3 until %4",
4172 dayList[rule.pos() + 31],
4173 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
4174 recurEnd( incidence ) );
4176 recurStr += i18nc(
"number of occurrences",
4177 " (<numid>%1</numid> occurrences)",
4181 recurStr = i18ncp(
"Recurs every N months on the [2nd|3rd|...] weekdayname",
4182 "Recurs every month on the %2 %3",
4183 "Recurs every %1 months on the %2 %3",
4185 dayList[rule.pos() + 31],
4186 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ) );
4191 case Recurrence::rMonthlyDay:
4196 recurStr = i18ncp(
"Recurs monthly on the [1st|2nd|...] day until end-date",
4197 "Recurs monthly on the %2 day until %3",
4198 "Recurs every %1 months on the %2 day until %3",
4201 recurEnd( incidence ) );
4203 recurStr += i18nc(
"number of occurrences",
4204 " (<numid>%1</numid> occurrences)",
4208 recurStr = i18ncp(
"Recurs monthly on the [1st|2nd|...] day",
4209 "Recurs monthly on the %2 day",
4210 "Recurs every <numid>%1</numid> month on the %2 day",
4212 dayList[days + 31] );
4217 case Recurrence::rYearlyMonth:
4221 recurStr = i18ncp(
"Recurs Every N years on month-name [1st|2nd|...]"
4223 "Recurs yearly on %2 %3 until %4",
4224 "Recurs every %1 years on %2 %3 until %4",
4228 recurEnd( incidence ) );
4230 recurStr += i18nc(
"number of occurrences",
4231 " (<numid>%1</numid> occurrences)",
4237 recurStr = i18ncp(
"Recurs Every N years on month-name [1st|2nd|...]",
4238 "Recurs yearly on %2 %3",
4239 "Recurs every %1 years on %2 %3",
4243 dayList[ recur->
yearDates()[0] + 31 ] );
4246 recurStr = i18nc(
"Recurs Every year on month-name [1st|2nd|...]",
4247 "Recurs yearly on %1 %2",
4250 dayList[ recur->
startDate().day() + 31 ] );
4252 recurStr = i18nc(
"Recurs Every year on month-name [1st|2nd|...]",
4253 "Recurs yearly on %1 %2",
4254 calSys->monthName( recur->
startDate().month(),
4256 dayList[ recur->
startDate().day() + 31 ] );
4262 case Recurrence::rYearlyDay:
4263 if ( !recur->
yearDays().isEmpty() ) {
4265 recurStr = i18ncp(
"Recurs every N years on day N until end-date",
4266 "Recurs every year on day <numid>%2</numid> until %3",
4267 "Recurs every <numid>%1</numid> years"
4268 " on day <numid>%2</numid> until %3",
4271 recurEnd( incidence ) );
4273 recurStr += i18nc(
"number of occurrences",
4274 " (<numid>%1</numid> occurrences)",
4278 recurStr = i18ncp(
"Recurs every N YEAR[S] on day N",
4279 "Recurs every year on day <numid>%2</numid>",
4280 "Recurs every <numid>%1</numid> years"
4281 " on day <numid>%2</numid>",
4286 case Recurrence::rYearlyPos:
4291 recurStr = i18ncp(
"Every N years on the [2nd|3rd|...] weekdayname "
4292 "of monthname until end-date",
4293 "Every year on the %2 %3 of %4 until %5",
4294 "Every <numid>%1</numid> years on the %2 %3 of %4"
4297 dayList[rule.pos() + 31],
4298 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
4300 recurEnd( incidence ) );
4302 recurStr += i18nc(
"number of occurrences",
4303 " (<numid>%1</numid> occurrences)",
4307 recurStr = i18ncp(
"Every N years on the [2nd|3rd|...] weekdayname "
4309 "Every year on the %2 %3 of %4",
4310 "Every <numid>%1</numid> years on the %2 %3 of %4",
4312 dayList[rule.pos() + 31],
4313 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
4321 if ( recurStr.isEmpty() ) {
4322 recurStr = i18n(
"Incidence recurs" );
4327 DateTimeList::ConstIterator il;
4329 for ( il = l.constBegin(); il != l.constEnd(); ++il ) {
4331 case Recurrence::rMinutely:
4332 exStr << i18n(
"minute %1", (*il).time().minute() );
4334 case Recurrence::rHourly:
4335 exStr << KGlobal::locale()->formatTime( (*il).time() );
4337 case Recurrence::rDaily:
4338 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4340 case Recurrence::rWeekly:
4341 exStr << calSys->weekDayName( (*il).date(), KCalendarSystem::ShortDayName );
4343 case Recurrence::rMonthlyPos:
4344 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4346 case Recurrence::rMonthlyDay:
4347 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4349 case Recurrence::rYearlyMonth:
4350 exStr << calSys->monthName( (*il).date(), KCalendarSystem::LongName );
4352 case Recurrence::rYearlyDay:
4353 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4355 case Recurrence::rYearlyPos:
4356 exStr << KGlobal::locale()->formatDate( (*il).date(), KLocale::ShortDate );
4362 DateList::ConstIterator dl;
4363 for ( dl = d.constBegin(); dl != d.constEnd(); ++dl ) {
4365 case Recurrence::rDaily:
4366 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4368 case Recurrence::rWeekly:
4371 if ( exStr.isEmpty() ) {
4372 exStr << i18np(
"1 day",
"%1 days", recur->exDates().count() );
4375 case Recurrence::rMonthlyPos:
4376 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4378 case Recurrence::rMonthlyDay:
4379 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4381 case Recurrence::rYearlyMonth:
4382 exStr << calSys->monthName( (*dl), KCalendarSystem::LongName );
4384 case Recurrence::rYearlyDay:
4385 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4387 case Recurrence::rYearlyPos:
4388 exStr << KGlobal::locale()->formatDate( (*dl), KLocale::ShortDate );
4393 if ( !exStr.isEmpty() ) {
4394 recurStr = i18n(
"%1 (excluding %2)", recurStr, exStr.join(
"," ) );
4402 const KDateTime::Spec &spec )
4404 if ( spec.isValid() ) {
4407 if ( spec.timeZone() != KSystemTimeZones::local() ) {
4408 timeZone =
' ' + spec.timeZone().name();
4411 return KGlobal::locale()->formatTime( date.toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
4413 return KGlobal::locale()->formatTime( date.time(), !shortfmt );
4419 const KDateTime::Spec &spec )
4421 if ( spec.isValid() ) {
4424 if ( spec.timeZone() != KSystemTimeZones::local() ) {
4425 timeZone =
' ' + spec.timeZone().name();
4429 KGlobal::locale()->formatDate( date.toTimeSpec( spec ).date(),
4430 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) +
4434 KGlobal::locale()->formatDate( date.date(),
4435 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
4442 const KDateTime::Spec &spec )
4448 if ( spec.isValid() ) {
4450 if ( spec.timeZone() != KSystemTimeZones::local() ) {
4451 timeZone =
' ' + spec.timeZone().name();
4454 return KGlobal::locale()->formatDateTime(
4455 date.toTimeSpec( spec ).dateTime(),
4456 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
4458 return KGlobal::locale()->formatDateTime(
4460 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
4465 const Incidence::Ptr &incidence )
4467 Q_UNUSED( calendar );
4468 Q_UNUSED( incidence );
4472 static QString secs2Duration(
int secs )
4475 int days = secs / 86400;
4477 tmp += i18np(
"1 day",
"%1 days", days );
4479 secs -= ( days * 86400 );
4481 int hours = secs / 3600;
4483 tmp += i18np(
"1 hour",
"%1 hours", hours );
4485 secs -= ( hours * 3600 );
4487 int mins = secs / 60;
4489 tmp += i18np(
"1 minute",
"%1 minutes", mins );
4497 if ( incidence->type() == Incidence::TypeEvent ) {
4499 if ( event->hasEndDate() ) {
4500 if ( !event->allDay() ) {
4501 tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) );
4503 tmp = i18np(
"1 day",
"%1 days",
4504 event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 );
4507 tmp = i18n(
"forever" );
4509 }
else if ( incidence->type() == Incidence::TypeTodo ) {
4511 if ( todo->hasDueDate() ) {
4512 if ( todo->hasStartDate() ) {
4513 if ( !todo->allDay() ) {
4514 tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) );
4516 tmp = i18np(
"1 day",
"%1 days",
4517 todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 );
4529 Q_UNUSED( shortfmt );
4535 Alarm::List::ConstIterator it;
4536 for ( it = alarms.constBegin(); it != alarms.constEnd(); ++it ) {
4539 QString remStr, atStr, offsetStr;
4540 if ( alarm->hasTime() ) {
4542 if ( alarm->time().isValid() ) {
4543 atStr = KGlobal::locale()->formatDateTime( alarm->time() );
4545 }
else if ( alarm->hasStartOffset() ) {
4546 offset = alarm->startOffset().asSeconds();
4549 offsetStr = i18nc(
"N days/hours/minutes before the start datetime",
4550 "%1 before the start", secs2Duration( offset ) );
4551 }
else if ( offset > 0 ) {
4552 offsetStr = i18nc(
"N days/hours/minutes after the start datetime",
4553 "%1 after the start", secs2Duration( offset ) );
4555 if ( incidence->dtStart().isValid() ) {
4556 atStr = KGlobal::locale()->formatDateTime( incidence->dtStart() );
4559 }
else if ( alarm->hasEndOffset() ) {
4560 offset = alarm->endOffset().asSeconds();
4563 if ( incidence->type() == Incidence::TypeTodo ) {
4564 offsetStr = i18nc(
"N days/hours/minutes before the due datetime",
4565 "%1 before the to-do is due", secs2Duration( offset ) );
4567 offsetStr = i18nc(
"N days/hours/minutes before the end datetime",
4568 "%1 before the end", secs2Duration( offset ) );
4570 }
else if ( offset > 0 ) {
4571 if ( incidence->type() == Incidence::TypeTodo ) {
4572 offsetStr = i18nc(
"N days/hours/minutes after the due datetime",
4573 "%1 after the to-do is due", secs2Duration( offset ) );
4575 offsetStr = i18nc(
"N days/hours/minutes after the end datetime",
4576 "%1 after the end", secs2Duration( offset ) );
4579 if ( incidence->type() == Incidence::TypeTodo ) {
4581 if ( t->dtDue().isValid() ) {
4582 atStr = KGlobal::locale()->formatDateTime( t->dtDue() );
4586 if ( e->dtEnd().isValid() ) {
4587 atStr = KGlobal::locale()->formatDateTime( e->dtEnd() );
4592 if ( offset == 0 ) {
4593 if ( !atStr.isEmpty() ) {
4594 remStr = i18nc(
"reminder occurs at datetime",
"at %1", atStr );
4600 if ( alarm->repeatCount() > 0 ) {
4601 QString countStr = i18np(
"repeats once",
"repeats %1 times", alarm->repeatCount() );
4602 QString intervalStr = i18nc(
"interval is N days/hours/minutes",
4604 secs2Duration( alarm->snoozeTime().asSeconds() ) );
4605 QString repeatStr = i18nc(
"(repeat string, interval string)",
4606 "(%1, %2)", countStr, intervalStr );
4607 remStr = remStr +
' ' + repeatStr;
4610 reminderStringList << remStr;
QSharedPointer< Attachment > Ptr
QSharedPointer< Alarm > Ptr
virtual bool visit(Event::Ptr event)
QSharedPointer< Event > Ptr
QList< int > yearDays() const
KCALUTILS_EXPORT QString formatDate(const KDateTime &dt, bool shortfmt=true, const KDateTime::Spec &spec=KDateTime::Spec())
Build a QString date representation of a KDateTime object.
QList< int > yearDates() const
KCALUTILS_EXPORT QString formatDateTime(const KDateTime &dt, bool dateOnly=false, bool shortfmt=true, const KDateTime::Spec &spec=KDateTime::Spec())
Build a QString date/time representation of a KDateTime object.
QSharedPointer< ScheduleMessage > Ptr
virtual bool accept(Visitor &v, IncidenceBase::Ptr incidence)
KCALUTILS_EXPORT QString mimeType()
Mime-type of iCalendar.
QSharedPointer< MemoryCalendar > Ptr
This file is part of the API for handling calendar data and provides static functions for formatting ...
QList< int > monthDays() const
KCALUTILS_EXPORT QString errorMessage(const KCalCore::Exception &exception)
Build a translated message representing an exception.
QList< int > yearMonths() const
QSharedPointer< Calendar > Ptr
QSharedPointer< Attendee > Ptr
KCALUTILS_EXPORT QString todoCompletedDateTime(const KCalCore::Todo::Ptr &todo, bool shortfmt=false)
Returns string containing the date/time when the to-do was completed, formatted according to the user...
QSharedPointer< FreeBusy > Ptr
ushort recurrenceType() const
QSharedPointer< Todo > Ptr
KDateTime endDateTime() const
QSharedPointer< Journal > Ptr
QList< RecurrenceRule::WDayPos > monthPositions() const
QList< RecurrenceRule::WDayPos > yearPositions() const