00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00037 #include "incidenceformatter.h"
00038 #include "attachment.h"
00039 #include "calendar.h"
00040 #include "calendarlocal.h"
00041 #ifndef KDEPIM_NO_KRESOURCES
00042 #include "calendarresources.h"
00043 #endif
00044 #include "event.h"
00045 #include "freebusy.h"
00046 #include "icalformat.h"
00047 #include "journal.h"
00048 #include "todo.h"
00049
00050 #include "kpimutils/email.h"
00051 #include "kabc/phonenumber.h"
00052 #include "kabc/vcardconverter.h"
00053 #include "kabc/stdaddressbook.h"
00054
00055 #include <kdatetime.h>
00056 #include <kemailsettings.h>
00057
00058 #include <kglobal.h>
00059 #include <kiconloader.h>
00060 #include <klocale.h>
00061 #include <kcalendarsystem.h>
00062 #include <ksystemtimezone.h>
00063 #include <kmimetype.h>
00064
00065 #include <QtCore/QBuffer>
00066 #include <QtCore/QList>
00067 #include <QtGui/QTextDocument>
00068 #include <QtGui/QApplication>
00069
00070 using namespace KCal;
00071 using namespace IncidenceFormatter;
00072
00073
00074
00075
00076
00077
00078 static QString htmlAddLink( const QString &ref, const QString &text,
00079 bool newline = true )
00080 {
00081 QString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
00082 if ( newline ) {
00083 tmpStr += '\n';
00084 }
00085 return tmpStr;
00086 }
00087
00088 static QString htmlAddTag( const QString &tag, const QString &text )
00089 {
00090 int numLineBreaks = text.count( "\n" );
00091 QString str = '<' + tag + '>';
00092 QString tmpText = text;
00093 QString tmpStr = str;
00094 if( numLineBreaks >= 0 ) {
00095 if ( numLineBreaks > 0 ) {
00096 int pos = 0;
00097 QString tmp;
00098 for ( int i = 0; i <= numLineBreaks; ++i ) {
00099 pos = tmpText.indexOf( "\n" );
00100 tmp = tmpText.left( pos );
00101 tmpText = tmpText.right( tmpText.length() - pos - 1 );
00102 tmpStr += tmp + "<br>";
00103 }
00104 } else {
00105 tmpStr += tmpText;
00106 }
00107 }
00108 tmpStr += "</" + tag + '>';
00109 return tmpStr;
00110 }
00111
00112 static bool iamAttendee( Attendee *attendee )
00113 {
00114
00115
00116 bool iam = false;
00117 KEMailSettings settings;
00118 QStringList profiles = settings.profiles();
00119 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
00120 settings.setProfile( *it );
00121 if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
00122 iam = true;
00123 break;
00124 }
00125 }
00126 return iam;
00127 }
00128
00129 static bool iamOrganizer( Incidence *incidence )
00130 {
00131
00132
00133 if ( !incidence ) {
00134 return false;
00135 }
00136
00137 bool iam = false;
00138 KEMailSettings settings;
00139 QStringList profiles = settings.profiles();
00140 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
00141 settings.setProfile( *it );
00142 if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) {
00143 iam = true;
00144 break;
00145 }
00146 }
00147 return iam;
00148 }
00149
00150 static bool senderIsOrganizer( Incidence *incidence, const QString &sender )
00151 {
00152
00153
00154 if ( !incidence || sender.isEmpty() ) {
00155 return true;
00156 }
00157
00158 bool isorg = true;
00159 QString senderName, senderEmail;
00160 if ( KPIMUtils::extractEmailAddressAndName( sender, senderEmail, senderName ) ) {
00161
00162 if ( incidence->organizer().email() != senderEmail &&
00163 incidence->organizer().name() != senderName ) {
00164 isorg = false;
00165 }
00166 }
00167 return isorg;
00168 }
00169
00170 static QString firstAttendeeName( Incidence *incidence, const QString &defName )
00171 {
00172 QString name;
00173 if ( !incidence ) {
00174 return name;
00175 }
00176
00177 Attendee::List attendees = incidence->attendees();
00178 if( attendees.count() > 0 ) {
00179 Attendee *attendee = *attendees.begin();
00180 name = attendee->name();
00181 if ( name.isEmpty() ) {
00182 name = attendee->email();
00183 }
00184 if ( name.isEmpty() ) {
00185 name = defName;
00186 }
00187 }
00188 return name;
00189 }
00190
00191
00192
00193
00194
00195
00196
00197 static QString displayViewLinkPerson( const QString &email, QString name,
00198 QString uid, const QString &iconPath )
00199 {
00200
00201
00202 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00203 #ifndef KDEPIM_NO_KRESOURCES
00204 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00205 KABC::Addressee::List addressList = add_book->findByEmail( email );
00206 KABC::Addressee o = ( !addressList.isEmpty() ? addressList.first() : KABC::Addressee() );
00207 if ( !o.isEmpty() && addressList.size() < 2 ) {
00208 if ( name.isEmpty() ) {
00209
00210 name = o.formattedName();
00211 }
00212 uid = o.uid();
00213 } else {
00214
00215 uid.clear();
00216 }
00217 #else
00218 uid.clear();
00219 #endif
00220 }
00221
00222
00223 QString tmpString;
00224 if ( !uid.isEmpty() ) {
00225
00226 if ( name.isEmpty() ) {
00227
00228 tmpString += htmlAddLink( "uid:" + uid, email );
00229 } else {
00230 tmpString += htmlAddLink( "uid:" + uid, name );
00231 }
00232 } else {
00233
00234 tmpString += ( name.isEmpty() ? email : name );
00235 }
00236
00237
00238 if ( !email.isEmpty() && !iconPath.isNull() ) {
00239 KUrl mailto;
00240 mailto.setProtocol( "mailto" );
00241 mailto.setPath( email );
00242 tmpString += htmlAddLink( mailto.url(),
00243 "<img valign=\"top\" src=\"" + iconPath + "\">" );
00244 }
00245
00246 return tmpString;
00247 }
00248
00249 static QString displayViewFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
00250 {
00251 QString tmpStr;
00252 Attendee::List::ConstIterator it;
00253 Attendee::List attendees = incidence->attendees();
00254 KIconLoader *iconLoader = KIconLoader::global();
00255 const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
00256
00257 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
00258 Attendee *a = *it;
00259 if ( a->role() != role ) {
00260
00261 continue;
00262 }
00263 if ( a->email() == incidence->organizer().email() ) {
00264
00265 continue;
00266 }
00267 tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid(), iconPath );
00268 if ( !a->delegator().isEmpty() ) {
00269 tmpStr += i18n( " (delegated by %1)", a->delegator() );
00270 }
00271 if ( !a->delegate().isEmpty() ) {
00272 tmpStr += i18n( " (delegated to %1)", a->delegate() );
00273 }
00274 tmpStr += "<br>";
00275 }
00276 if ( tmpStr.endsWith( QLatin1String( "<br>" ) ) ) {
00277 tmpStr.chop( 4 );
00278 }
00279 return tmpStr;
00280 }
00281
00282 static QString displayViewFormatAttendees( Incidence *incidence )
00283 {
00284 QString tmpStr, str;
00285
00286 KIconLoader *iconLoader = KIconLoader::global();
00287 const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
00288
00289
00290 int attendeeCount = incidence->attendees().count();
00291 if ( attendeeCount > 1 ||
00292 ( attendeeCount == 1 &&
00293 incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
00294 tmpStr += "<tr>";
00295 tmpStr += "<td><b>" + i18n( "Organizer:" ) + "</b></td>";
00296 tmpStr += "<td>" +
00297 displayViewLinkPerson( incidence->organizer().email(),
00298 incidence->organizer().name(),
00299 QString(), iconPath ) +
00300 "</td>";
00301 tmpStr += "</tr>";
00302 }
00303
00304
00305 str = displayViewFormatAttendeeRoleList( incidence, Attendee::Chair );
00306 if ( !str.isEmpty() ) {
00307 tmpStr += "<tr>";
00308 tmpStr += "<td><b>" + i18n( "Chair:" ) + "</b></td>";
00309 tmpStr += "<td>" + str + "</td>";
00310 tmpStr += "</tr>";
00311 }
00312
00313
00314 str = displayViewFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
00315 if ( !str.isEmpty() ) {
00316 tmpStr += "<tr>";
00317 tmpStr += "<td><b>" + i18n( "Required Participants:" ) + "</b></td>";
00318 tmpStr += "<td>" + str + "</td>";
00319 tmpStr += "</tr>";
00320 }
00321
00322
00323 str = displayViewFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
00324 if ( !str.isEmpty() ) {
00325 tmpStr += "<tr>";
00326 tmpStr += "<td><b>" + i18n( "Optional Participants:" ) + "</b></td>";
00327 tmpStr += "<td>" + str + "</td>";
00328 tmpStr += "</tr>";
00329 }
00330
00331
00332 str = displayViewFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
00333 if ( !str.isEmpty() ) {
00334 tmpStr += "<tr>";
00335 tmpStr += "<td><b>" + i18n( "Observers:" ) + "</b></td>";
00336 tmpStr += "<td>" + str + "</td>";
00337 tmpStr += "</tr>";
00338 }
00339
00340 return tmpStr;
00341 }
00342
00343 static QString displayViewFormatAttachments( Incidence *incidence )
00344 {
00345 QString tmpStr;
00346 Attachment::List as = incidence->attachments();
00347 Attachment::List::ConstIterator it;
00348 int count = 0;
00349 for ( it = as.constBegin(); it != as.constEnd(); ++it ) {
00350 count++;
00351 if ( (*it)->isUri() ) {
00352 QString name;
00353 if ( (*it)->uri().startsWith( QLatin1String( "kmail:" ) ) ) {
00354 name = i18n( "Show mail" );
00355 } else {
00356 name = (*it)->label();
00357 }
00358 tmpStr += htmlAddLink( (*it)->uri(), name );
00359 } else {
00360 tmpStr += (*it)->label();
00361 }
00362 if ( count < as.count() ) {
00363 tmpStr += "<br>";
00364 }
00365 }
00366 return tmpStr;
00367 }
00368
00369 static QString displayViewFormatCategories( Incidence *incidence )
00370 {
00371
00372 return incidence->categories().join( ", " );
00373 }
00374
00375 static QString displayViewFormatCreationDate( Incidence *incidence, KDateTime::Spec spec )
00376 {
00377 KDateTime kdt = incidence->created().toTimeSpec( spec );
00378 return i18n( "Creation date: %1", dateTimeToString( incidence->created(), false, true, spec ) );
00379 }
00380
00381 static QString displayViewFormatBirthday( Event *event )
00382 {
00383 if ( !event ) {
00384 return QString();
00385 }
00386 if ( event->customProperty( "KABC", "BIRTHDAY" ) != "YES" &&
00387 event->customProperty( "KABC", "ANNIVERSARY" ) != "YES" ) {
00388 return QString();
00389 }
00390
00391 QString uid_1 = event->customProperty( "KABC", "UID-1" );
00392 QString name_1 = event->customProperty( "KABC", "NAME-1" );
00393 QString email_1= event->customProperty( "KABC", "EMAIL-1" );
00394
00395 KIconLoader *iconLoader = KIconLoader::global();
00396 const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
00397
00398 QString tmpStr = displayViewLinkPerson( email_1, name_1, uid_1, iconPath );
00399
00400 return tmpStr;
00401 }
00402
00403 static QString displayViewFormatHeader( Incidence *incidence )
00404 {
00405 QString tmpStr = "<table><tr>";
00406
00407
00408 KIconLoader *iconLoader = KIconLoader::global();
00409 tmpStr += "<td>";
00410
00411
00412
00413
00414 if ( incidence->type() == "Todo" ) {
00415 tmpStr += "<img valign=\"top\" src=\"";
00416 Todo *todo = static_cast<Todo *>( incidence );
00417 if ( !todo->isCompleted() ) {
00418 tmpStr += iconLoader->iconPath( "view-calendar-tasks", KIconLoader::Small );
00419 } else {
00420 tmpStr += iconLoader->iconPath( "task-complete", KIconLoader::Small );
00421 }
00422 tmpStr += "\">";
00423 }
00424
00425 if ( incidence->type() == "Event" ) {
00426 QString iconPath;
00427 if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00428 iconPath = iconLoader->iconPath( "view-calendar-birthday", KIconLoader::Small );
00429 } else if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00430 iconPath = iconLoader->iconPath( "view-calendar-wedding-anniversary", KIconLoader::Small );
00431 } else {
00432 iconPath = iconLoader->iconPath( "view-calendar-day", KIconLoader::Small );
00433 }
00434 tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
00435 }
00436
00437 if ( incidence->type() == "Journal" ) {
00438 tmpStr += "<img valign=\"top\" src=\"" +
00439 iconLoader->iconPath( "view-pim-journal", KIconLoader::Small ) +
00440 "\">";
00441 }
00442
00443 if ( incidence->isAlarmEnabled() ) {
00444 tmpStr += "<img valign=\"top\" src=\"" +
00445 iconLoader->iconPath( "preferences-desktop-notification-bell", KIconLoader::Small ) +
00446 "\">";
00447 }
00448 if ( incidence->recurs() ) {
00449 tmpStr += "<img valign=\"top\" src=\"" +
00450 iconLoader->iconPath( "edit-redo", KIconLoader::Small ) +
00451 "\">";
00452 }
00453 if ( incidence->isReadOnly() ) {
00454 tmpStr += "<img valign=\"top\" src=\"" +
00455 iconLoader->iconPath( "object-locked", KIconLoader::Small ) +
00456 "\">";
00457 }
00458 tmpStr += "</td>";
00459
00460 tmpStr += "<td>";
00461 tmpStr += "<b><u>" + incidence->richSummary() + "</u></b>";
00462 tmpStr += "</td>";
00463
00464 tmpStr += "</tr></table>";
00465
00466 return tmpStr;
00467 }
00468
00469 static QString displayViewFormatEvent( const QString &calStr, Event *event,
00470 const QDate &date, KDateTime::Spec spec )
00471 {
00472 if ( !event ) {
00473 return QString();
00474 }
00475
00476 QString tmpStr = displayViewFormatHeader( event );
00477
00478 tmpStr += "<table>";
00479 tmpStr += "<col width=\"25%\"/>";
00480 tmpStr += "<col width=\"75%\"/>";
00481
00482 if ( !calStr.isEmpty() ) {
00483 tmpStr += "<tr>";
00484 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00485 tmpStr += "<td>" + calStr + "</td>";
00486 tmpStr += "</tr>";
00487 }
00488
00489 if ( !event->location().isEmpty() ) {
00490 tmpStr += "<tr>";
00491 tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00492 tmpStr += "<td>" + event->richLocation() + "</td>";
00493 tmpStr += "</tr>";
00494 }
00495
00496 KDateTime startDt = event->dtStart();
00497 KDateTime endDt = event->dtEnd();
00498 if ( event->recurs() ) {
00499 if ( date.isValid() ) {
00500 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
00501 int diffDays = startDt.daysTo( kdt );
00502 kdt = kdt.addSecs( -1 );
00503 startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
00504 if ( event->hasEndDate() ) {
00505 endDt = endDt.addDays( diffDays );
00506 if ( startDt > endDt ) {
00507 startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
00508 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
00509 }
00510 }
00511 }
00512 }
00513
00514 tmpStr += "<tr>";
00515 if ( event->allDay() ) {
00516 if ( event->isMultiDay() ) {
00517 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00518 tmpStr += "<td>" +
00519 i18nc( "<beginTime> - <endTime>","%1 - %2",
00520 dateToString( startDt, false, spec ),
00521 dateToString( endDt, false, spec ) ) +
00522 "</td>";
00523 } else {
00524 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00525 tmpStr += "<td>" +
00526 i18nc( "date as string","%1",
00527 dateToString( startDt, false, spec ) ) +
00528 "</td>";
00529 }
00530 } else {
00531 if ( event->isMultiDay() ) {
00532 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00533 tmpStr += "<td>" +
00534 i18nc( "<beginTime> - <endTime>","%1 - %2",
00535 dateToString( startDt, false, spec ),
00536 dateToString( endDt, false, spec ) ) +
00537 "</td>";
00538 } else {
00539 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00540 tmpStr += "<td>" +
00541 i18nc( "date as string", "%1",
00542 dateToString( startDt, false, spec ) ) +
00543 "</td>";
00544
00545 tmpStr += "</tr><tr>";
00546 tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>";
00547 if ( event->hasEndDate() && startDt != endDt ) {
00548 tmpStr += "<td>" +
00549 i18nc( "<beginTime> - <endTime>","%1 - %2",
00550 timeToString( startDt, true, spec ),
00551 timeToString( endDt, true, spec ) ) +
00552 "</td>";
00553 } else {
00554 tmpStr += "<td>" +
00555 timeToString( startDt, true, spec ) +
00556 "</td>";
00557 }
00558 }
00559 }
00560 tmpStr += "</tr>";
00561
00562 QString durStr = durationString( event );
00563 if ( !durStr.isEmpty() ) {
00564 tmpStr += "<tr>";
00565 tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
00566 tmpStr += "<td>" + durStr + "</td>";
00567 tmpStr += "</tr>";
00568 }
00569
00570 if ( event->recurs() ) {
00571 tmpStr += "<tr>";
00572 tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
00573 tmpStr += "<td>" +
00574 recurrenceString( event ) +
00575 "</td>";
00576 tmpStr += "</tr>";
00577 }
00578
00579 const bool isBirthday = event->customProperty( "KABC", "BIRTHDAY" ) == "YES";
00580 const bool isAnniversary = event->customProperty( "KABC", "ANNIVERSARY" ) == "YES";
00581
00582 if ( isBirthday || isAnniversary ) {
00583 tmpStr += "<tr>";
00584 if ( isAnniversary ) {
00585 tmpStr += "<td><b>" + i18n( "Anniversary:" ) + "</b></td>";
00586 } else {
00587 tmpStr += "<td><b>" + i18n( "Birthday:" ) + "</b></td>";
00588 }
00589 tmpStr += "<td>" + displayViewFormatBirthday( event ) + "</td>";
00590 tmpStr += "</tr>";
00591 tmpStr += "</table>";
00592 return tmpStr;
00593 }
00594
00595 if ( !event->description().isEmpty() ) {
00596 tmpStr += "<tr>";
00597 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00598 tmpStr += "<td>" + event->richDescription() + "</td>";
00599 tmpStr += "</tr>";
00600 }
00601
00602
00603
00604 int reminderCount = event->alarms().count();
00605 if ( reminderCount > 0 && event->isAlarmEnabled() ) {
00606 tmpStr += "<tr>";
00607 tmpStr += "<td><b>" +
00608 i18np( "Reminder:", "Reminders:", reminderCount ) +
00609 "</b></td>";
00610 tmpStr += "<td>" + reminderStringList( event ).join( "<br>" ) + "</td>";
00611 tmpStr += "</tr>";
00612 }
00613
00614 tmpStr += displayViewFormatAttendees( event );
00615
00616 int categoryCount = event->categories().count();
00617 if ( categoryCount > 0 ) {
00618 tmpStr += "<tr>";
00619 tmpStr += "<td><b>";
00620 tmpStr += i18np( "Category:", "Categories:", categoryCount ) +
00621 "</b></td>";
00622 tmpStr += "<td>" + displayViewFormatCategories( event ) + "</td>";
00623 tmpStr += "</tr>";
00624 }
00625
00626 int attachmentCount = event->attachments().count();
00627 if ( attachmentCount > 0 ) {
00628 tmpStr += "<tr>";
00629 tmpStr += "<td><b>" +
00630 i18np( "Attachment:", "Attachments:", attachmentCount ) +
00631 "</b></td>";
00632 tmpStr += "<td>" + displayViewFormatAttachments( event ) + "</td>";
00633 tmpStr += "</tr>";
00634 }
00635 tmpStr += "</table>";
00636
00637 tmpStr += "<p><em>" + displayViewFormatCreationDate( event, spec ) + "</em>";
00638
00639 return tmpStr;
00640 }
00641
00642 static QString displayViewFormatTodo( const QString &calStr, Todo *todo,
00643 const QDate &date, KDateTime::Spec spec )
00644 {
00645 if ( !todo ) {
00646 return QString();
00647 }
00648
00649 QString tmpStr = displayViewFormatHeader( todo );
00650
00651 tmpStr += "<table>";
00652 tmpStr += "<col width=\"25%\"/>";
00653 tmpStr += "<col width=\"75%\"/>";
00654
00655 if ( !calStr.isEmpty() ) {
00656 tmpStr += "<tr>";
00657 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00658 tmpStr += "<td>" + calStr + "</td>";
00659 tmpStr += "</tr>";
00660 }
00661
00662 if ( !todo->location().isEmpty() ) {
00663 tmpStr += "<tr>";
00664 tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00665 tmpStr += "<td>" + todo->richLocation() + "</td>";
00666 tmpStr += "</tr>";
00667 }
00668
00669 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
00670 KDateTime startDt = todo->dtStart();
00671 if ( todo->recurs() ) {
00672 if ( date.isValid() ) {
00673 startDt.setDate( date );
00674 }
00675 }
00676 tmpStr += "<tr>";
00677 tmpStr += "<td><b>" +
00678 i18nc( "to-do start date/time", "Start:" ) +
00679 "</b></td>";
00680 tmpStr += "<td>" +
00681 dateTimeToString( startDt, todo->allDay(), false, spec ) +
00682 "</td>";
00683 tmpStr += "</tr>";
00684 }
00685
00686 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
00687 KDateTime dueDt = todo->dtDue();
00688 if ( todo->recurs() ) {
00689 if ( date.isValid() ) {
00690 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
00691 kdt = kdt.addSecs( -1 );
00692 dueDt.setDate( todo->recurrence()->getNextDateTime( kdt ).date() );
00693 }
00694 }
00695 tmpStr += "<tr>";
00696 tmpStr += "<td><b>" +
00697 i18nc( "to-do due date/time", "Due:" ) +
00698 "</b></td>";
00699 tmpStr += "<td>" +
00700 dateTimeToString( dueDt, todo->allDay(), false, spec ) +
00701 "</td>";
00702 tmpStr += "</tr>";
00703 }
00704
00705 QString durStr = durationString( todo );
00706 if ( !durStr.isEmpty() ) {
00707 tmpStr += "<tr>";
00708 tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
00709 tmpStr += "<td>" + durStr + "</td>";
00710 tmpStr += "</tr>";
00711 }
00712
00713 if ( todo->recurs() ) {
00714 tmpStr += "<tr>";
00715 tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
00716 tmpStr += "<td>" +
00717 recurrenceString( todo ) +
00718 "</td>";
00719 tmpStr += "</tr>";
00720 }
00721
00722 if ( !todo->description().isEmpty() ) {
00723 tmpStr += "<tr>";
00724 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00725 tmpStr += "<td>" + todo->richDescription() + "</td>";
00726 tmpStr += "</tr>";
00727 }
00728
00729
00730
00731 int reminderCount = todo->alarms().count();
00732 if ( reminderCount > 0 && todo->isAlarmEnabled() ) {
00733 tmpStr += "<tr>";
00734 tmpStr += "<td><b>" +
00735 i18np( "Reminder:", "Reminders:", reminderCount ) +
00736 "</b></td>";
00737 tmpStr += "<td>" + reminderStringList( todo ).join( "<br>" ) + "</td>";
00738 tmpStr += "</tr>";
00739 }
00740
00741 tmpStr += displayViewFormatAttendees( todo );
00742
00743 int categoryCount = todo->categories().count();
00744 if ( categoryCount > 0 ) {
00745 tmpStr += "<tr>";
00746 tmpStr += "<td><b>" +
00747 i18np( "Category:", "Categories:", categoryCount ) +
00748 "</b></td>";
00749 tmpStr += "<td>" + displayViewFormatCategories( todo ) + "</td>";
00750 tmpStr += "</tr>";
00751 }
00752
00753 if ( todo->priority() > 0 ) {
00754 tmpStr += "<tr>";
00755 tmpStr += "<td><b>" + i18n( "Priority:" ) + "</b></td>";
00756 tmpStr += "<td>";
00757 tmpStr += QString::number( todo->priority() );
00758 tmpStr += "</td>";
00759 tmpStr += "</tr>";
00760 }
00761
00762 tmpStr += "<tr>";
00763 if ( todo->isCompleted() ) {
00764 tmpStr += "<td><b>" + i18nc( "Completed: date", "Completed:" ) + "</b></td>";
00765 tmpStr += "<td>";
00766 tmpStr += todo->completedStr();
00767 } else {
00768 tmpStr += "<td><b>" + i18n( "Percent Done:" ) + "</b></td>";
00769 tmpStr += "<td>";
00770 tmpStr += i18n( "%1%", todo->percentComplete() );
00771 }
00772 tmpStr += "</td>";
00773 tmpStr += "</tr>";
00774
00775 int attachmentCount = todo->attachments().count();
00776 if ( attachmentCount > 0 ) {
00777 tmpStr += "<tr>";
00778 tmpStr += "<td><b>" +
00779 i18np( "Attachment:", "Attachments:", attachmentCount ) +
00780 "</b></td>";
00781 tmpStr += "<td>" + displayViewFormatAttachments( todo ) + "</td>";
00782 tmpStr += "</tr>";
00783 }
00784 tmpStr += "</table>";
00785
00786 tmpStr += "<p><em>" + displayViewFormatCreationDate( todo, spec ) + "</em>";
00787
00788 return tmpStr;
00789 }
00790
00791 static QString displayViewFormatJournal( const QString &calStr, Journal *journal,
00792 KDateTime::Spec spec )
00793 {
00794 if ( !journal ) {
00795 return QString();
00796 }
00797
00798 QString tmpStr = displayViewFormatHeader( journal );
00799
00800 tmpStr += "<table>";
00801 tmpStr += "<col width=\"25%\"/>";
00802 tmpStr += "<col width=\"75%\"/>";
00803
00804 if ( !calStr.isEmpty() ) {
00805 tmpStr += "<tr>";
00806 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00807 tmpStr += "<td>" + calStr + "</td>";
00808 tmpStr += "</tr>";
00809 }
00810
00811 tmpStr += "<tr>";
00812 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00813 tmpStr += "<td>" +
00814 dateToString( journal->dtStart(), false, spec ) +
00815 "</td>";
00816 tmpStr += "</tr>";
00817
00818 if ( !journal->description().isEmpty() ) {
00819 tmpStr += "<tr>";
00820 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00821 tmpStr += "<td>" + journal->richDescription() + "</td>";
00822 tmpStr += "</tr>";
00823 }
00824
00825 int categoryCount = journal->categories().count();
00826 if ( categoryCount > 0 ) {
00827 tmpStr += "<tr>";
00828 tmpStr += "<td><b>" +
00829 i18np( "Category:", "Categories:", categoryCount ) +
00830 "</b></td>";
00831 tmpStr += "<td>" + displayViewFormatCategories( journal ) + "</td>";
00832 tmpStr += "</tr>";
00833 }
00834
00835 tmpStr += "</table>";
00836
00837 tmpStr += "<p><em>" + displayViewFormatCreationDate( journal, spec ) + "</em>";
00838
00839 return tmpStr;
00840 }
00841
00842 static QString displayViewFormatFreeBusy( const QString &calStr, FreeBusy *fb,
00843 KDateTime::Spec spec )
00844 {
00845 Q_UNUSED( calStr );
00846 if ( !fb ) {
00847 return QString();
00848 }
00849
00850 QString tmpStr(
00851 htmlAddTag(
00852 "h2", i18n( "Free/Busy information for %1", fb->organizer().fullName() ) ) );
00853
00854 tmpStr += htmlAddTag( "h4",
00855 i18n( "Busy times in date range %1 - %2:",
00856 dateToString( fb->dtStart(), true, spec ),
00857 dateToString( fb->dtEnd(), true, spec ) ) );
00858
00859 QList<Period> periods = fb->busyPeriods();
00860
00861 QString text =
00862 htmlAddTag( "em",
00863 htmlAddTag( "b", i18nc( "tag for busy periods list", "Busy:" ) ) );
00864
00865 QList<Period>::iterator it;
00866 for ( it = periods.begin(); it != periods.end(); ++it ) {
00867 Period per = *it;
00868 if ( per.hasDuration() ) {
00869 int dur = per.duration().asSeconds();
00870 QString cont;
00871 if ( dur >= 3600 ) {
00872 cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
00873 dur %= 3600;
00874 }
00875 if ( dur >= 60 ) {
00876 cont += i18ncp( "minutes part duration", "1 minute ", "%1 minutes ", dur / 60 );
00877 dur %= 60;
00878 }
00879 if ( dur > 0 ) {
00880 cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
00881 }
00882 text += i18nc( "startDate for duration", "%1 for %2",
00883 dateTimeToString( per.start(), false, true, spec ),
00884 cont );
00885 text += "<br>";
00886 } else {
00887 if ( per.start().date() == per.end().date() ) {
00888 text += i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
00889 dateToString( per.start(), true, spec ),
00890 timeToString( per.start(), true, spec ),
00891 timeToString( per.end(), true, spec ) );
00892 } else {
00893 text += i18nc( "fromDateTime - toDateTime", "%1 - %2",
00894 dateTimeToString( per.start(), false, true, spec ),
00895 dateTimeToString( per.end(), false, true, spec ) );
00896 }
00897 text += "<br>";
00898 }
00899 }
00900 tmpStr += htmlAddTag( "p", text );
00901 return tmpStr;
00902 }
00903
00904
00905
00906 class KCal::IncidenceFormatter::EventViewerVisitor
00907 : public IncidenceBase::Visitor
00908 {
00909 public:
00910 EventViewerVisitor()
00911 : mCalendar( 0 ), mSpec( KDateTime::Spec() ), mResult( "" ) {}
00912
00913 bool act( Calendar *calendar, IncidenceBase *incidence, const QDate &date,
00914 KDateTime::Spec spec=KDateTime::Spec() )
00915 {
00916 mCalendar = calendar;
00917 mSourceName.clear();
00918 mDate = date;
00919 mSpec = spec;
00920 mResult = "";
00921 return incidence->accept( *this );
00922 }
00923
00924 bool act( const QString &sourceName, IncidenceBase *incidence, const QDate &date,
00925 KDateTime::Spec spec=KDateTime::Spec() )
00926 {
00927 mCalendar = 0;
00928 mSourceName = sourceName;
00929 mDate = date;
00930 mSpec = spec;
00931 mResult = "";
00932 return incidence->accept( *this );
00933 }
00934
00935 QString result() const { return mResult; }
00936
00937 protected:
00938 bool visit( Event *event )
00939 {
00940 const QString calStr = mCalendar ? resourceString( mCalendar, event ) : mSourceName;
00941 mResult = displayViewFormatEvent( calStr, event, mDate, mSpec );
00942 return !mResult.isEmpty();
00943 }
00944 bool visit( Todo *todo )
00945 {
00946 const QString calStr = mCalendar ? resourceString( mCalendar, todo ) : mSourceName;
00947 mResult = displayViewFormatTodo( calStr, todo, mDate, mSpec );
00948 return !mResult.isEmpty();
00949 }
00950 bool visit( Journal *journal )
00951 {
00952 const QString calStr = mCalendar ? resourceString( mCalendar, journal ) : mSourceName;
00953 mResult = displayViewFormatJournal( calStr, journal, mSpec );
00954 return !mResult.isEmpty();
00955 }
00956 bool visit( FreeBusy *fb )
00957 {
00958 mResult = displayViewFormatFreeBusy( mSourceName, fb, mSpec );
00959 return !mResult.isEmpty();
00960 }
00961
00962 protected:
00963 Calendar *mCalendar;
00964 QString mSourceName;
00965 QDate mDate;
00966 KDateTime::Spec mSpec;
00967 QString mResult;
00968 };
00969
00970
00971 QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
00972 {
00973 return extensiveDisplayStr( 0, incidence, QDate(), KDateTime::Spec() );
00974 }
00975
00976 QString IncidenceFormatter::extensiveDisplayStr( IncidenceBase *incidence,
00977 KDateTime::Spec spec )
00978 {
00979 if ( !incidence ) {
00980 return QString();
00981 }
00982
00983 EventViewerVisitor v;
00984 if ( v.act( 0, incidence, QDate(), spec ) ) {
00985 return v.result();
00986 } else {
00987 return QString();
00988 }
00989 }
00990
00991 QString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar,
00992 IncidenceBase *incidence,
00993 const QDate &date,
00994 KDateTime::Spec spec )
00995 {
00996 if ( !incidence ) {
00997 return QString();
00998 }
00999
01000 EventViewerVisitor v;
01001 if ( v.act( calendar, incidence, date, spec ) ) {
01002 return v.result();
01003 } else {
01004 return QString();
01005 }
01006 }
01007
01008 QString IncidenceFormatter::extensiveDisplayStr( const QString &sourceName,
01009 IncidenceBase *incidence,
01010 const QDate &date,
01011 KDateTime::Spec spec )
01012 {
01013 if ( !incidence ) {
01014 return QString();
01015 }
01016
01017 EventViewerVisitor v;
01018 if ( v.act( sourceName, incidence, date, spec ) ) {
01019 return v.result();
01020 } else {
01021 return QString();
01022 }
01023 }
01024
01025
01026
01027
01028
01029 static QString string2HTML( const QString &str )
01030 {
01031 return Qt::convertFromPlainText( str, Qt::WhiteSpaceNormal );
01032 }
01033
01034 static QString cleanHtml( const QString &html )
01035 {
01036 QRegExp rx( "<body[^>]*>(.*)</body>", Qt::CaseInsensitive );
01037 rx.indexIn( html );
01038 QString body = rx.cap( 1 );
01039
01040 return Qt::escape( body.remove( QRegExp( "<[^>]*>" ) ).trimmed() );
01041 }
01042
01043 static QString eventStartTimeStr( Event *event )
01044 {
01045 QString tmp;
01046 if ( !event->allDay() ) {
01047 tmp = i18nc( "%1: Start Date, %2: Start Time", "%1 %2",
01048 dateToString( event->dtStart(), true, KSystemTimeZones::local() ),
01049 timeToString( event->dtStart(), true, KSystemTimeZones::local() ) );
01050 } else {
01051 tmp = i18nc( "%1: Start Date", "%1 (all day)",
01052 dateToString( event->dtStart(), true, KSystemTimeZones::local() ) );
01053 }
01054 return tmp;
01055 }
01056
01057 static QString eventEndTimeStr( Event *event )
01058 {
01059 QString tmp;
01060 if ( event->hasEndDate() && event->dtEnd().isValid() ) {
01061 if ( !event->allDay() ) {
01062 tmp = i18nc( "%1: End Date, %2: End Time", "%1 %2",
01063 dateToString( event->dtEnd(), true, KSystemTimeZones::local() ),
01064 timeToString( event->dtEnd(), true, KSystemTimeZones::local() ) );
01065 } else {
01066 tmp = i18nc( "%1: End Date", "%1 (all day)",
01067 dateToString( event->dtEnd(), true, KSystemTimeZones::local() ) );
01068 }
01069 }
01070 return tmp;
01071 }
01072
01073 static QString invitationRow( const QString &cell1, const QString &cell2 )
01074 {
01075 return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
01076 }
01077
01078 static Attendee *findDelegatedFromMyAttendee( Incidence *incidence )
01079 {
01080
01081
01082 Attendee *attendee = 0;
01083 if ( !incidence ) {
01084 return attendee;
01085 }
01086
01087 KEMailSettings settings;
01088 QStringList profiles = settings.profiles();
01089 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
01090 settings.setProfile( *it );
01091
01092 QString delegatorName, delegatorEmail;
01093 Attendee::List attendees = incidence->attendees();
01094 Attendee::List::ConstIterator it2;
01095 for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) {
01096 Attendee *a = *it2;
01097 KPIMUtils::extractEmailAddressAndName( a->delegator(), delegatorEmail, delegatorName );
01098 if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) {
01099 attendee = a;
01100 break;
01101 }
01102 }
01103 }
01104 return attendee;
01105 }
01106
01107 static Attendee *findMyAttendee( Incidence *incidence )
01108 {
01109
01110
01111 Attendee *attendee = 0;
01112 if ( !incidence ) {
01113 return attendee;
01114 }
01115
01116 KEMailSettings settings;
01117 QStringList profiles = settings.profiles();
01118 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
01119 settings.setProfile( *it );
01120
01121 Attendee::List attendees = incidence->attendees();
01122 Attendee::List::ConstIterator it2;
01123 for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) {
01124 Attendee *a = *it2;
01125 if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
01126 attendee = a;
01127 break;
01128 }
01129 }
01130 }
01131 return attendee;
01132 }
01133
01134 static Attendee *findAttendee( Incidence *incidence, const QString &email )
01135 {
01136
01137
01138 Attendee *attendee = 0;
01139 if ( !incidence ) {
01140 return attendee;
01141 }
01142
01143 Attendee::List attendees = incidence->attendees();
01144 Attendee::List::ConstIterator it;
01145 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
01146 Attendee *a = *it;
01147 if ( email == a->email() ) {
01148 attendee = a;
01149 break;
01150 }
01151 }
01152 return attendee;
01153 }
01154
01155 static bool rsvpRequested( Incidence *incidence )
01156 {
01157 if ( !incidence ) {
01158 return false;
01159 }
01160
01161
01162
01163 bool rsvp = true;
01164 Attendee::List attendees = incidence->attendees();
01165 Attendee::List::ConstIterator it;
01166 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
01167 if ( it == attendees.constBegin() ) {
01168 rsvp = (*it)->RSVP();
01169 } else {
01170 if ( (*it)->RSVP() != rsvp ) {
01171 rsvp = true;
01172 break;
01173 }
01174 }
01175 }
01176 return rsvp;
01177 }
01178
01179 static QString rsvpRequestedStr( bool rsvpRequested, const QString &role )
01180 {
01181 if ( rsvpRequested ) {
01182 if ( role.isEmpty() ) {
01183 return i18n( "Your response is requested" );
01184 } else {
01185 return i18n( "Your response as <b>%1</b> is requested", role );
01186 }
01187 } else {
01188 if ( role.isEmpty() ) {
01189 return i18n( "No response is necessary" );
01190 } else {
01191 return i18n( "No response as <b>%1</b> is necessary", role );
01192 }
01193 }
01194 }
01195
01196 static QString myStatusStr( Incidence *incidence )
01197 {
01198 QString ret;
01199 Attendee *a = findMyAttendee( incidence );
01200 if ( a &&
01201 a->status() != Attendee::NeedsAction && a->status() != Attendee::Delegated ) {
01202 ret = i18n( "(<b>Note</b>: the Organizer preset your response to <b>%1</b>)",
01203 Attendee::statusName( a->status() ) );
01204 }
01205 return ret;
01206 }
01207
01208 static QString invitationPerson( const QString &email, QString name, QString uid )
01209 {
01210
01211
01212 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
01213 #ifndef KDEPIM_NO_KRESOURCES
01214 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
01215 KABC::Addressee::List addressList = add_book->findByEmail( email );
01216 if ( !addressList.isEmpty() ) {
01217 KABC::Addressee o = addressList.first();
01218 if ( !o.isEmpty() && addressList.size() < 2 ) {
01219 if ( name.isEmpty() ) {
01220
01221 name = o.formattedName();
01222 }
01223 uid = o.uid();
01224 } else {
01225
01226 uid.clear();
01227 }
01228 }
01229 #else
01230 uid.clear();
01231 #endif
01232 }
01233
01234
01235 QString tmpString;
01236 if ( !uid.isEmpty() ) {
01237
01238 if ( name.isEmpty() ) {
01239
01240 tmpString += htmlAddLink( "uid:" + uid, email );
01241 } else {
01242 tmpString += htmlAddLink( "uid:" + uid, name );
01243 }
01244 } else {
01245
01246 tmpString += ( name.isEmpty() ? email : name );
01247 }
01248 tmpString += '\n';
01249
01250
01251 if ( !email.isEmpty() ) {
01252 KCal::Person person( name, email );
01253 KUrl mailto;
01254 mailto.setProtocol( "mailto" );
01255 mailto.setPath( person.fullName() );
01256 const QString iconPath =
01257 KIconLoader::global()->iconPath( "mail-message-new", KIconLoader::Small );
01258 tmpString += htmlAddLink( mailto.url(),
01259 "<img valign=\"top\" src=\"" + iconPath + "\">" );
01260 }
01261 tmpString += '\n';
01262
01263 return tmpString;
01264 }
01265
01266 static QString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode )
01267 {
01268
01269
01270
01271
01272 QString html;
01273 QString descr;
01274 QStringList comments;
01275
01276 if ( incidence->comments().isEmpty() ) {
01277 if ( !incidence->description().isEmpty() ) {
01278
01279 if ( !incidence->descriptionIsRich() ) {
01280 comments << string2HTML( incidence->description() );
01281 } else {
01282 comments << incidence->richDescription();
01283 if ( noHtmlMode ) {
01284 comments[0] = cleanHtml( comments[0] );
01285 }
01286 comments[0] = htmlAddTag( "p", comments[0] );
01287 }
01288 }
01289
01290 } else {
01291
01292 foreach ( const QString &c, incidence->comments() ) {
01293 if ( !c.isEmpty() ) {
01294
01295 if ( !Qt::mightBeRichText( c ) ) {
01296 comments << string2HTML( c );
01297 } else {
01298 if ( noHtmlMode ) {
01299 comments << cleanHtml( cleanHtml( "<body>" + c + "</body>" ) );
01300 } else {
01301 comments << c;
01302 }
01303 }
01304 }
01305 }
01306 if ( !incidence->description().isEmpty() ) {
01307
01308 if ( !incidence->descriptionIsRich() ) {
01309 descr = string2HTML( incidence->description() );
01310 } else {
01311 descr = incidence->richDescription();
01312 if ( noHtmlMode ) {
01313 descr = cleanHtml( descr );
01314 }
01315 descr = htmlAddTag( "p", descr );
01316 }
01317 }
01318 }
01319
01320 if( !descr.isEmpty() ) {
01321 html += "<p>";
01322 html += "<table border=\"0\" style=\"margin-top:4px;\">";
01323 html += "<tr><td><center>" +
01324 htmlAddTag( "u", i18n( "Description:" ) ) +
01325 "</center></td></tr>";
01326 html += "<tr><td>" + descr + "</td></tr>";
01327 html += "</table>";
01328 }
01329
01330 if ( !comments.isEmpty() ) {
01331 html += "<p>";
01332 html += "<table border=\"0\" style=\"margin-top:4px;\">";
01333 html += "<tr><td><center>" +
01334 htmlAddTag( "u", i18n( "Comments:" ) ) +
01335 "</center></td></tr>";
01336 html += "<tr><td>";
01337 if ( comments.count() > 1 ) {
01338 html += "<ul>";
01339 for ( int i=0; i < comments.count(); ++i ) {
01340 html += "<li>" + comments[i] + "</li>";
01341 }
01342 html += "</ul>";
01343 } else {
01344 html += comments[0];
01345 }
01346 html += "</td></tr>";
01347 html += "</table>";
01348 }
01349 return html;
01350 }
01351
01352 static QString invitationDetailsEvent( Event *event, bool noHtmlMode, KDateTime::Spec spec )
01353 {
01354
01355 if ( !event ) {
01356 return QString();
01357 }
01358
01359 QString sSummary = i18n( "Summary unspecified" );
01360 if ( !event->summary().isEmpty() ) {
01361 if ( !event->summaryIsRich() ) {
01362 sSummary = Qt::escape( event->summary() );
01363 } else {
01364 sSummary = event->richSummary();
01365 if ( noHtmlMode ) {
01366 sSummary = cleanHtml( sSummary );
01367 }
01368 }
01369 }
01370
01371 QString sLocation = i18n( "Location unspecified" );
01372 if ( !event->location().isEmpty() ) {
01373 if ( !event->locationIsRich() ) {
01374 sLocation = Qt::escape( event->location() );
01375 } else {
01376 sLocation = event->richLocation();
01377 if ( noHtmlMode ) {
01378 sLocation = cleanHtml( sLocation );
01379 }
01380 }
01381 }
01382
01383 QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" );
01384 QString html = QString( "<div dir=\"%1\">\n" ).arg( dir );
01385 html += "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
01386
01387
01388 html += invitationRow( i18n( "What:" ), sSummary );
01389 html += invitationRow( i18n( "Where:" ), sLocation );
01390
01391
01392 if ( event->dtStart().date() == event->dtEnd().date() ) {
01393 html += invitationRow( i18n( "Date:" ), dateToString( event->dtStart(), false, spec ) );
01394 if ( !event->allDay() ) {
01395 html += invitationRow( i18n( "Time:" ),
01396 timeToString( event->dtStart(), true, spec ) +
01397 " - " +
01398 timeToString( event->dtEnd(), true, spec ) );
01399 }
01400 } else {
01401 html += invitationRow( i18nc( "starting date", "From:" ),
01402 dateToString( event->dtStart(), false, spec ) );
01403 if ( !event->allDay() ) {
01404 html += invitationRow( i18nc( "starting time", "At:" ),
01405 timeToString( event->dtStart(), true, spec ) );
01406 }
01407 if ( event->hasEndDate() ) {
01408 html += invitationRow( i18nc( "ending date", "To:" ),
01409 dateToString( event->dtEnd(), false, spec ) );
01410 if ( !event->allDay() ) {
01411 html += invitationRow( i18nc( "ending time", "At:" ),
01412 timeToString( event->dtEnd(), true, spec ) );
01413 }
01414 } else {
01415 html += invitationRow( i18nc( "ending date", "To:" ),
01416 i18n( "no end date specified" ) );
01417 }
01418 }
01419
01420
01421 QString durStr = durationString( event );
01422 if ( !durStr.isEmpty() ) {
01423 html += invitationRow( i18n( "Duration:" ), durStr );
01424 }
01425
01426 if ( event->recurs() ) {
01427 html += invitationRow( i18n( "Recurrence:" ), recurrenceString( event ) );
01428 }
01429
01430 html += "</table></div>\n";
01431 html += invitationsDetailsIncidence( event, noHtmlMode );
01432
01433 return html;
01434 }
01435
01436 static QString invitationDetailsTodo( Todo *todo, bool noHtmlMode, KDateTime::Spec spec )
01437 {
01438
01439 if ( !todo ) {
01440 return QString();
01441 }
01442
01443 QString sSummary = i18n( "Summary unspecified" );
01444 if ( !todo->summary().isEmpty() ) {
01445 if ( !todo->summaryIsRich() ) {
01446 sSummary = Qt::escape( todo->summary() );
01447 } else {
01448 sSummary = todo->richSummary();
01449 if ( noHtmlMode ) {
01450 sSummary = cleanHtml( sSummary );
01451 }
01452 }
01453 }
01454
01455 QString sLocation = i18n( "Location unspecified" );
01456 if ( !todo->location().isEmpty() ) {
01457 if ( !todo->locationIsRich() ) {
01458 sLocation = Qt::escape( todo->location() );
01459 } else {
01460 sLocation = todo->richLocation();
01461 if ( noHtmlMode ) {
01462 sLocation = cleanHtml( sLocation );
01463 }
01464 }
01465 }
01466
01467 QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" );
01468 QString html = QString( "<div dir=\"%1\">\n" ).arg( dir );
01469 html += "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
01470
01471
01472 html += invitationRow( i18n( "What:" ), sSummary );
01473 html += invitationRow( i18n( "Where:" ), sLocation );
01474
01475 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
01476 html += invitationRow( i18n( "Start Date:" ), dateToString( todo->dtStart(), false, spec ) );
01477 if ( !todo->allDay() ) {
01478 html += invitationRow( i18n( "Start Time:" ), timeToString( todo->dtStart(), false, spec ) );
01479 }
01480 }
01481 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
01482 html += invitationRow( i18n( "Due Date:" ), dateToString( todo->dtDue(), false, spec ) );
01483 if ( !todo->allDay() ) {
01484 html += invitationRow( i18n( "Due Time:" ), timeToString( todo->dtDue(), false, spec ) );
01485 }
01486 } else {
01487 html += invitationRow( i18n( "Due Date:" ), i18nc( "no to-do due date", "None" ) );
01488 }
01489
01490 html += "</table></div>\n";
01491 html += invitationsDetailsIncidence( todo, noHtmlMode );
01492
01493 return html;
01494 }
01495
01496 static QString invitationDetailsJournal( Journal *journal, bool noHtmlMode, KDateTime::Spec spec )
01497 {
01498 if ( !journal ) {
01499 return QString();
01500 }
01501
01502 QString sSummary = i18n( "Summary unspecified" );
01503 QString sDescr = i18n( "Description unspecified" );
01504 if ( ! journal->summary().isEmpty() ) {
01505 sSummary = journal->richSummary();
01506 if ( noHtmlMode ) {
01507 sSummary = cleanHtml( sSummary );
01508 }
01509 }
01510 if ( ! journal->description().isEmpty() ) {
01511 sDescr = journal->richDescription();
01512 if ( noHtmlMode ) {
01513 sDescr = cleanHtml( sDescr );
01514 }
01515 }
01516 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01517 html += invitationRow( i18n( "Summary:" ), sSummary );
01518 html += invitationRow( i18n( "Date:" ), dateToString( journal->dtStart(), false, spec ) );
01519 html += invitationRow( i18n( "Description:" ), sDescr );
01520 html += "</table>\n";
01521 html += invitationsDetailsIncidence( journal, noHtmlMode );
01522
01523 return html;
01524 }
01525
01526 static QString invitationDetailsFreeBusy( FreeBusy *fb, bool noHtmlMode, KDateTime::Spec spec )
01527 {
01528 Q_UNUSED( noHtmlMode );
01529
01530 if ( !fb ) {
01531 return QString();
01532 }
01533
01534 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01535 html += invitationRow( i18n( "Person:" ), fb->organizer().fullName() );
01536 html += invitationRow( i18n( "Start date:" ), dateToString( fb->dtStart(), true, spec ) );
01537 html += invitationRow( i18n( "End date:" ), dateToString( fb->dtEnd(), true, spec ) );
01538
01539 html += "<tr><td colspan=2><hr></td></tr>\n";
01540 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
01541
01542 QList<Period> periods = fb->busyPeriods();
01543 QList<Period>::iterator it;
01544 for ( it = periods.begin(); it != periods.end(); ++it ) {
01545 Period per = *it;
01546 if ( per.hasDuration() ) {
01547 int dur = per.duration().asSeconds();
01548 QString cont;
01549 if ( dur >= 3600 ) {
01550 cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
01551 dur %= 3600;
01552 }
01553 if ( dur >= 60 ) {
01554 cont += i18ncp( "minutes part of duration", "1 minute", "%1 minutes ", dur / 60 );
01555 dur %= 60;
01556 }
01557 if ( dur > 0 ) {
01558 cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
01559 }
01560 html += invitationRow(
01561 QString(), i18nc( "startDate for duration", "%1 for %2",
01562 KGlobal::locale()->formatDateTime(
01563 per.start().dateTime(), KLocale::LongDate ), cont ) );
01564 } else {
01565 QString cont;
01566 if ( per.start().date() == per.end().date() ) {
01567 cont = i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
01568 KGlobal::locale()->formatDate( per.start().date() ),
01569 KGlobal::locale()->formatTime( per.start().time() ),
01570 KGlobal::locale()->formatTime( per.end().time() ) );
01571 } else {
01572 cont = i18nc( "fromDateTime - toDateTime", "%1 - %2",
01573 KGlobal::locale()->formatDateTime(
01574 per.start().dateTime(), KLocale::LongDate ),
01575 KGlobal::locale()->formatDateTime(
01576 per.end().dateTime(), KLocale::LongDate ) );
01577 }
01578
01579 html += invitationRow( QString(), cont );
01580 }
01581 }
01582
01583 html += "</table>\n";
01584 return html;
01585 }
01586
01587 static bool replyMeansCounter( Incidence * )
01588 {
01589 return false;
01604 }
01605
01606 static QString invitationHeaderEvent( Event *event, Incidence *existingIncidence,
01607 ScheduleMessage *msg, const QString &sender )
01608 {
01609 if ( !msg || !event ) {
01610 return QString();
01611 }
01612
01613 switch ( msg->method() ) {
01614 case iTIPPublish:
01615 return i18n( "This invitation has been published" );
01616 case iTIPRequest:
01617 if ( existingIncidence && event->revision() > 0 ) {
01618 return i18n( "This invitation has been updated by the organizer %1",
01619 event->organizer().fullName() );
01620 }
01621 if ( iamOrganizer( event ) ) {
01622 return i18n( "I created this invitation" );
01623 } else {
01624 if ( senderIsOrganizer( event, sender ) ) {
01625 if ( !event->organizer().fullName().isEmpty() ) {
01626 return i18n( "You received an invitation from %1",
01627 event->organizer().fullName() );
01628 } else {
01629 return i18n( "You received an invitation" );
01630 }
01631 } else {
01632 if ( !event->organizer().fullName().isEmpty() ) {
01633 return i18n( "You received an invitation from %1 as a representative of %2",
01634 sender, event->organizer().fullName() );
01635 } else {
01636 return i18n( "You received an invitation from %1 as the organizer's representative",
01637 sender );
01638 }
01639 }
01640 }
01641 case iTIPRefresh:
01642 return i18n( "This invitation was refreshed" );
01643 case iTIPCancel:
01644 return i18n( "This invitation has been canceled" );
01645 case iTIPAdd:
01646 return i18n( "Addition to the invitation" );
01647 case iTIPReply:
01648 {
01649 if ( replyMeansCounter( event ) ) {
01650 return i18n( "%1 makes this counter proposal",
01651 firstAttendeeName( event, i18n( "Sender" ) ) );
01652 }
01653
01654 Attendee::List attendees = event->attendees();
01655 if( attendees.count() == 0 ) {
01656 kDebug() << "No attendees in the iCal reply!";
01657 return QString();
01658 }
01659 if ( attendees.count() != 1 ) {
01660 kDebug() << "Warning: attendeecount in the reply should be 1"
01661 << "but is" << attendees.count();
01662 }
01663 QString attendeeName = firstAttendeeName( event, i18n( "Sender" ) );
01664
01665 QString delegatorName, dummy;
01666 Attendee *attendee = *attendees.begin();
01667 KPIMUtils::extractEmailAddressAndName( attendee->delegator(), dummy, delegatorName );
01668 if ( delegatorName.isEmpty() ) {
01669 delegatorName = attendee->delegator();
01670 }
01671
01672 switch( attendee->status() ) {
01673 case Attendee::NeedsAction:
01674 return i18n( "%1 indicates this invitation still needs some action", attendeeName );
01675 case Attendee::Accepted:
01676 if ( event->revision() > 0 ) {
01677 if ( !sender.isEmpty() ) {
01678 return i18n( "This invitation has been updated by attendee %1", sender );
01679 } else {
01680 return i18n( "This invitation has been updated by an attendee" );
01681 }
01682 } else {
01683 if ( delegatorName.isEmpty() ) {
01684 return i18n( "%1 accepts this invitation", attendeeName );
01685 } else {
01686 return i18n( "%1 accepts this invitation on behalf of %2",
01687 attendeeName, delegatorName );
01688 }
01689 }
01690 case Attendee::Tentative:
01691 if ( delegatorName.isEmpty() ) {
01692 return i18n( "%1 tentatively accepts this invitation", attendeeName );
01693 } else {
01694 return i18n( "%1 tentatively accepts this invitation on behalf of %2",
01695 attendeeName, delegatorName );
01696 }
01697 case Attendee::Declined:
01698 if ( delegatorName.isEmpty() ) {
01699 return i18n( "%1 declines this invitation", attendeeName );
01700 } else {
01701 return i18n( "%1 declines this invitation on behalf of %2",
01702 attendeeName, delegatorName );
01703 }
01704 case Attendee::Delegated:
01705 {
01706 QString delegate, dummy;
01707 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
01708 if ( delegate.isEmpty() ) {
01709 delegate = attendee->delegate();
01710 }
01711 if ( !delegate.isEmpty() ) {
01712 return i18n( "%1 has delegated this invitation to %2", attendeeName, delegate );
01713 } else {
01714 return i18n( "%1 has delegated this invitation", attendeeName );
01715 }
01716 }
01717 case Attendee::Completed:
01718 return i18n( "This invitation is now completed" );
01719 case Attendee::InProcess:
01720 return i18n( "%1 is still processing the invitation", attendeeName );
01721 case Attendee::None:
01722 return i18n( "Unknown response to this invitation" );
01723 }
01724 break;
01725 }
01726 case iTIPCounter:
01727 return i18n( "%1 makes this counter proposal",
01728 firstAttendeeName( event, i18n( "Sender" ) ) );
01729
01730 case iTIPDeclineCounter:
01731 return i18n( "%1 declines the counter proposal",
01732 firstAttendeeName( event, i18n( "Sender" ) ) );
01733
01734 case iTIPNoMethod:
01735 return i18n( "Error: Event iTIP message with unknown method" );
01736 }
01737 kError() << "encountered an iTIP method that we do not support";
01738 return QString();
01739 }
01740
01741 static QString invitationHeaderTodo( Todo *todo, Incidence *existingIncidence,
01742 ScheduleMessage *msg, const QString &sender )
01743 {
01744 if ( !msg || !todo ) {
01745 return QString();
01746 }
01747
01748 switch ( msg->method() ) {
01749 case iTIPPublish:
01750 return i18n( "This to-do has been published" );
01751 case iTIPRequest:
01752 if ( existingIncidence && todo->revision() > 0 ) {
01753 return i18n( "This to-do has been updated by the organizer %1",
01754 todo->organizer().fullName() );
01755 } else {
01756 if ( iamOrganizer( todo ) ) {
01757 return i18n( "I created this to-do" );
01758 } else {
01759 if ( senderIsOrganizer( todo, sender ) ) {
01760 if ( !todo->organizer().fullName().isEmpty() ) {
01761 return i18n( "You have been assigned this to-do by %1", todo->organizer().fullName() );
01762 } else {
01763 return i18n( "You have been assigned this to-do" );
01764 }
01765 } else {
01766 if ( !todo->organizer().fullName().isEmpty() ) {
01767 return i18n( "You have been assigned this to-do by %1 as a representative of %2",
01768 sender, todo->organizer().fullName() );
01769 } else {
01770 return i18n( "You have been assigned this to-do by %1 as the "
01771 "organizer's representative", sender );
01772 }
01773 }
01774 }
01775 }
01776 case iTIPRefresh:
01777 return i18n( "This to-do was refreshed" );
01778 case iTIPCancel:
01779 return i18n( "This to-do was canceled" );
01780 case iTIPAdd:
01781 return i18n( "Addition to the to-do" );
01782 case iTIPReply:
01783 {
01784 if ( replyMeansCounter( todo ) ) {
01785 return i18n( "%1 makes this counter proposal",
01786 firstAttendeeName( todo, i18n( "Sender" ) ) );
01787 }
01788
01789 Attendee::List attendees = todo->attendees();
01790 if ( attendees.count() == 0 ) {
01791 kDebug() << "No attendees in the iCal reply!";
01792 return QString();
01793 }
01794 if ( attendees.count() != 1 ) {
01795 kDebug() << "Warning: attendeecount in the reply should be 1"
01796 << "but is" << attendees.count();
01797 }
01798 QString attendeeName = firstAttendeeName( todo, i18n( "Sender" ) );
01799
01800 QString delegatorName, dummy;
01801 Attendee *attendee = *attendees.begin();
01802 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegatorName );
01803 if ( delegatorName.isEmpty() ) {
01804 delegatorName = attendee->delegator();
01805 }
01806
01807 switch( attendee->status() ) {
01808 case Attendee::NeedsAction:
01809 return i18n( "%1 indicates this to-do assignment still needs some action",
01810 attendeeName );
01811 case Attendee::Accepted:
01812 if ( todo->revision() > 0 ) {
01813 if ( !sender.isEmpty() ) {
01814 if ( todo->isCompleted() ) {
01815 return i18n( "This to-do has been completed by assignee %1", sender );
01816 } else {
01817 return i18n( "This to-do has been updated by assignee %1", sender );
01818 }
01819 } else {
01820 if ( todo->isCompleted() ) {
01821 return i18n( "This to-do has been completed by an assignee" );
01822 } else {
01823 return i18n( "This to-do has been updated by an assignee" );
01824 }
01825 }
01826 } else {
01827 if ( delegatorName.isEmpty() ) {
01828 return i18n( "%1 accepts this to-do", attendeeName );
01829 } else {
01830 return i18n( "%1 accepts this to-do on behalf of %2",
01831 attendeeName, delegatorName );
01832 }
01833 }
01834 case Attendee::Tentative:
01835 if ( delegatorName.isEmpty() ) {
01836 return i18n( "%1 tentatively accepts this to-do", attendeeName );
01837 } else {
01838 return i18n( "%1 tentatively accepts this to-do on behalf of %2",
01839 attendeeName, delegatorName );
01840 }
01841 case Attendee::Declined:
01842 if ( delegatorName.isEmpty() ) {
01843 return i18n( "%1 declines this to-do", attendeeName );
01844 } else {
01845 return i18n( "%1 declines this to-do on behalf of %2",
01846 attendeeName, delegatorName );
01847 }
01848 case Attendee::Delegated:
01849 {
01850 QString delegate, dummy;
01851 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
01852 if ( delegate.isEmpty() ) {
01853 delegate = attendee->delegate();
01854 }
01855 if ( !delegate.isEmpty() ) {
01856 return i18n( "%1 has delegated this to-do to %2", attendeeName, delegate );
01857 } else {
01858 return i18n( "%1 has delegated this to-do", attendeeName );
01859 }
01860 }
01861 case Attendee::Completed:
01862 return i18n( "The request for this to-do is now completed" );
01863 case Attendee::InProcess:
01864 return i18n( "%1 is still processing the to-do", attendeeName );
01865 case Attendee::None:
01866 return i18n( "Unknown response to this to-do" );
01867 }
01868 break;
01869 }
01870 case iTIPCounter:
01871 return i18n( "%1 makes this counter proposal",
01872 firstAttendeeName( todo, i18n( "Sender" ) ) );
01873
01874 case iTIPDeclineCounter:
01875 return i18n( "%1 declines the counter proposal",
01876 firstAttendeeName( todo, i18n( "Sender" ) ) );
01877
01878 case iTIPNoMethod:
01879 return i18n( "Error: To-do iTIP message with unknown method" );
01880 }
01881 kError() << "encountered an iTIP method that we do not support";
01882 return QString();
01883 }
01884
01885 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
01886 {
01887 if ( !msg || !journal ) {
01888 return QString();
01889 }
01890
01891 switch ( msg->method() ) {
01892 case iTIPPublish:
01893 return i18n( "This journal has been published" );
01894 case iTIPRequest:
01895 return i18n( "You have been assigned this journal" );
01896 case iTIPRefresh:
01897 return i18n( "This journal was refreshed" );
01898 case iTIPCancel:
01899 return i18n( "This journal was canceled" );
01900 case iTIPAdd:
01901 return i18n( "Addition to the journal" );
01902 case iTIPReply:
01903 {
01904 if ( replyMeansCounter( journal ) ) {
01905 return i18n( "Sender makes this counter proposal" );
01906 }
01907
01908 Attendee::List attendees = journal->attendees();
01909 if ( attendees.count() == 0 ) {
01910 kDebug() << "No attendees in the iCal reply!";
01911 return QString();
01912 }
01913 if( attendees.count() != 1 ) {
01914 kDebug() << "Warning: attendeecount in the reply should be 1 "
01915 << "but is " << attendees.count();
01916 }
01917 Attendee *attendee = *attendees.begin();
01918
01919 switch( attendee->status() ) {
01920 case Attendee::NeedsAction:
01921 return i18n( "Sender indicates this journal assignment still needs some action" );
01922 case Attendee::Accepted:
01923 return i18n( "Sender accepts this journal" );
01924 case Attendee::Tentative:
01925 return i18n( "Sender tentatively accepts this journal" );
01926 case Attendee::Declined:
01927 return i18n( "Sender declines this journal" );
01928 case Attendee::Delegated:
01929 return i18n( "Sender has delegated this request for the journal" );
01930 case Attendee::Completed:
01931 return i18n( "The request for this journal is now completed" );
01932 case Attendee::InProcess:
01933 return i18n( "Sender is still processing the invitation" );
01934 case Attendee::None:
01935 return i18n( "Unknown response to this journal" );
01936 }
01937 break;
01938 }
01939 case iTIPCounter:
01940 return i18n( "Sender makes this counter proposal" );
01941 case iTIPDeclineCounter:
01942 return i18n( "Sender declines the counter proposal" );
01943 case iTIPNoMethod:
01944 return i18n( "Error: Journal iTIP message with unknown method" );
01945 }
01946 kError() << "encountered an iTIP method that we do not support";
01947 return QString();
01948 }
01949
01950 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01951 {
01952 if ( !msg || !fb ) {
01953 return QString();
01954 }
01955
01956 switch ( msg->method() ) {
01957 case iTIPPublish:
01958 return i18n( "This free/busy list has been published" );
01959 case iTIPRequest:
01960 return i18n( "The free/busy list has been requested" );
01961 case iTIPRefresh:
01962 return i18n( "This free/busy list was refreshed" );
01963 case iTIPCancel:
01964 return i18n( "This free/busy list was canceled" );
01965 case iTIPAdd:
01966 return i18n( "Addition to the free/busy list" );
01967 case iTIPReply:
01968 return i18n( "Reply to the free/busy list" );
01969 case iTIPCounter:
01970 return i18n( "Sender makes this counter proposal" );
01971 case iTIPDeclineCounter:
01972 return i18n( "Sender declines the counter proposal" );
01973 case iTIPNoMethod:
01974 return i18n( "Error: Free/Busy iTIP message with unknown method" );
01975 }
01976 kError() << "encountered an iTIP method that we do not support";
01977 return QString();
01978 }
01979
01980
01981 static QString invitationAttendees( Incidence *incidence )
01982 {
01983 QString tmpStr;
01984 if ( !incidence ) {
01985 return tmpStr;
01986 }
01987
01988 tmpStr += i18n( "Invitation List" );
01989
01990 int count=0;
01991 Attendee::List attendees = incidence->attendees();
01992 if ( !attendees.isEmpty() ) {
01993
01994 Attendee::List::ConstIterator it;
01995 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
01996 Attendee *a = *it;
01997 if ( !iamAttendee( a ) ) {
01998 count++;
01999 if ( count == 1 ) {
02000 tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\">";
02001 }
02002 tmpStr += "<tr>";
02003 tmpStr += "<td>";
02004 tmpStr += invitationPerson( a->email(), a->name(), QString() );
02005 if ( !a->delegator().isEmpty() ) {
02006 tmpStr += i18n( " (delegated by %1)", a->delegator() );
02007 }
02008 if ( !a->delegate().isEmpty() ) {
02009 tmpStr += i18n( " (delegated to %1)", a->delegate() );
02010 }
02011 tmpStr += "</td>";
02012 tmpStr += "<td>" + a->statusStr() + "</td>";
02013 tmpStr += "</tr>";
02014 }
02015 }
02016 }
02017 if ( count ) {
02018 tmpStr += "</table>";
02019 } else {
02020 tmpStr += "<i>" + i18nc( "no attendees", "None" ) + "</i>";
02021 }
02022
02023 return tmpStr;
02024 }
02025
02026 static QString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence )
02027 {
02028 QString tmpStr;
02029 if ( !incidence ) {
02030 return tmpStr;
02031 }
02032
02033 Attachment::List attachments = incidence->attachments();
02034 if ( !attachments.isEmpty() ) {
02035 tmpStr += i18n( "Attached Documents:" ) + "<ol>";
02036
02037 Attachment::List::ConstIterator it;
02038 for ( it = attachments.constBegin(); it != attachments.constEnd(); ++it ) {
02039 Attachment *a = *it;
02040 tmpStr += "<li>";
02041
02042 KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() );
02043 const QString iconStr = ( mimeType ?
02044 mimeType->iconName( a->uri() ) :
02045 QString( "application-octet-stream" ) );
02046 const QString iconPath = KIconLoader::global()->iconPath( iconStr, KIconLoader::Small );
02047 if ( !iconPath.isEmpty() ) {
02048 tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
02049 }
02050 tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() );
02051 tmpStr += "</li>";
02052 }
02053 tmpStr += "</ol>";
02054 }
02055
02056 return tmpStr;
02057 }
02058
02059
02060 class KCal::IncidenceFormatter::ScheduleMessageVisitor
02061 : public IncidenceBase::Visitor
02062 {
02063 public:
02064 ScheduleMessageVisitor() : mExistingIncidence( 0 ), mMessage( 0 ) { mResult = ""; }
02065 bool act( IncidenceBase *incidence, Incidence *existingIncidence,
02066 ScheduleMessage *msg, const QString &sender )
02067 {
02068 mExistingIncidence = existingIncidence;
02069 mMessage = msg;
02070 mSender = sender;
02071 return incidence->accept( *this );
02072 }
02073 QString result() const { return mResult; }
02074
02075 protected:
02076 QString mResult;
02077 Incidence *mExistingIncidence;
02078 ScheduleMessage *mMessage;
02079 QString mSender;
02080 };
02081
02082 class KCal::IncidenceFormatter::InvitationHeaderVisitor :
02083 public IncidenceFormatter::ScheduleMessageVisitor
02084 {
02085 protected:
02086 bool visit( Event *event )
02087 {
02088 mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender );
02089 return !mResult.isEmpty();
02090 }
02091 bool visit( Todo *todo )
02092 {
02093 mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender );
02094 return !mResult.isEmpty();
02095 }
02096 bool visit( Journal *journal )
02097 {
02098 mResult = invitationHeaderJournal( journal, mMessage );
02099 return !mResult.isEmpty();
02100 }
02101 bool visit( FreeBusy *fb )
02102 {
02103 mResult = invitationHeaderFreeBusy( fb, mMessage );
02104 return !mResult.isEmpty();
02105 }
02106 };
02107
02108 class KCal::IncidenceFormatter::InvitationBodyVisitor
02109 : public IncidenceFormatter::ScheduleMessageVisitor
02110 {
02111 public:
02112 InvitationBodyVisitor( bool noHtmlMode, KDateTime::Spec spec )
02113 : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ), mSpec( spec ) {}
02114
02115 protected:
02116 bool visit( Event *event )
02117 {
02118 mResult = invitationDetailsEvent( event, mNoHtmlMode, mSpec );
02119 return !mResult.isEmpty();
02120 }
02121 bool visit( Todo *todo )
02122 {
02123 mResult = invitationDetailsTodo( todo, mNoHtmlMode, mSpec );
02124 return !mResult.isEmpty();
02125 }
02126 bool visit( Journal *journal )
02127 {
02128 mResult = invitationDetailsJournal( journal, mNoHtmlMode, mSpec );
02129 return !mResult.isEmpty();
02130 }
02131 bool visit( FreeBusy *fb )
02132 {
02133 mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode, mSpec );
02134 return !mResult.isEmpty();
02135 }
02136
02137 private:
02138 bool mNoHtmlMode;
02139 KDateTime::Spec mSpec;
02140 };
02141
02142
02143 QString InvitationFormatterHelper::generateLinkURL( const QString &id )
02144 {
02145 return id;
02146 }
02147
02148
02149 class IncidenceFormatter::IncidenceCompareVisitor
02150 : public IncidenceBase::Visitor
02151 {
02152 public:
02153 IncidenceCompareVisitor() : mExistingIncidence( 0 ) {}
02154 bool act( IncidenceBase *incidence, Incidence *existingIncidence, iTIPMethod method )
02155 {
02156 if ( !existingIncidence ) {
02157 return false;
02158 }
02159 Incidence *inc = dynamic_cast<Incidence *>( incidence );
02160 if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() ) {
02161 return false;
02162 }
02163 mExistingIncidence = existingIncidence;
02164 mMethod = method;
02165 return incidence->accept( *this );
02166 }
02167
02168 QString result() const
02169 {
02170 if ( mChanges.isEmpty() ) {
02171 return QString();
02172 }
02173 QString html = "<div align=\"left\"><ul><li>";
02174 html += mChanges.join( "</li><li>" );
02175 html += "</li><ul></div>";
02176 return html;
02177 }
02178
02179 protected:
02180 bool visit( Event *event )
02181 {
02182 compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
02183 compareIncidences( event, mExistingIncidence, mMethod );
02184 return !mChanges.isEmpty();
02185 }
02186 bool visit( Todo *todo )
02187 {
02188 compareTodos( todo, dynamic_cast<Todo*>( mExistingIncidence ) );
02189 compareIncidences( todo, mExistingIncidence, mMethod );
02190 return !mChanges.isEmpty();
02191 }
02192 bool visit( Journal *journal )
02193 {
02194 compareIncidences( journal, mExistingIncidence, mMethod );
02195 return !mChanges.isEmpty();
02196 }
02197 bool visit( FreeBusy *fb )
02198 {
02199 Q_UNUSED( fb );
02200 return !mChanges.isEmpty();
02201 }
02202
02203 private:
02204 void compareEvents( Event *newEvent, Event *oldEvent )
02205 {
02206 if ( !oldEvent || !newEvent ) {
02207 return;
02208 }
02209 if ( oldEvent->dtStart() != newEvent->dtStart() ||
02210 oldEvent->allDay() != newEvent->allDay() ) {
02211 mChanges += i18n( "The invitation starting time has been changed from %1 to %2",
02212 eventStartTimeStr( oldEvent ), eventStartTimeStr( newEvent ) );
02213 }
02214 if ( oldEvent->dtEnd() != newEvent->dtEnd() ||
02215 oldEvent->allDay() != newEvent->allDay() ) {
02216 mChanges += i18n( "The invitation ending time has been changed from %1 to %2",
02217 eventEndTimeStr( oldEvent ), eventEndTimeStr( newEvent ) );
02218 }
02219 }
02220
02221 void compareTodos( Todo *newTodo, Todo *oldTodo )
02222 {
02223 if ( !oldTodo || !newTodo ) {
02224 return;
02225 }
02226
02227 if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) {
02228 mChanges += i18n( "The to-do has been completed" );
02229 }
02230 if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) {
02231 mChanges += i18n( "The to-do is no longer completed" );
02232 }
02233 if ( oldTodo->percentComplete() != newTodo->percentComplete() ) {
02234 const QString oldPer = i18n( "%1%", oldTodo->percentComplete() );
02235 const QString newPer = i18n( "%1%", newTodo->percentComplete() );
02236 mChanges += i18n( "The task completed percentage has changed from %1 to %2",
02237 oldPer, newPer );
02238 }
02239
02240 if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) {
02241 mChanges += i18n( "A to-do starting time has been added" );
02242 }
02243 if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) {
02244 mChanges += i18n( "The to-do starting time has been removed" );
02245 }
02246 if ( oldTodo->hasStartDate() && newTodo->hasStartDate() &&
02247 oldTodo->dtStart() != newTodo->dtStart() ) {
02248 mChanges += i18n( "The to-do starting time has been changed from %1 to %2",
02249 dateTimeToString( oldTodo->dtStart(), oldTodo->allDay(), false ),
02250 dateTimeToString( newTodo->dtStart(), newTodo->allDay(), false ) );
02251 }
02252
02253 if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) {
02254 mChanges += i18n( "A to-do due time has been added" );
02255 }
02256 if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) {
02257 mChanges += i18n( "The to-do due time has been removed" );
02258 }
02259 if ( oldTodo->hasDueDate() && newTodo->hasDueDate() &&
02260 oldTodo->dtDue() != newTodo->dtDue() ) {
02261 mChanges += i18n( "The to-do due time has been changed from %1 to %2",
02262 dateTimeToString( oldTodo->dtDue(), oldTodo->allDay(), false ),
02263 dateTimeToString( newTodo->dtDue(), newTodo->allDay(), false ) );
02264 }
02265 }
02266
02267 void compareIncidences( Incidence *newInc, Incidence *oldInc, iTIPMethod method )
02268 {
02269 if ( !oldInc || !newInc ) {
02270 return;
02271 }
02272
02273 if ( oldInc->summary() != newInc->summary() ) {
02274 mChanges += i18n( "The summary has been changed to: \"%1\"",
02275 newInc->richSummary() );
02276 }
02277
02278 if ( oldInc->location() != newInc->location() ) {
02279 mChanges += i18n( "The location has been changed to: \"%1\"",
02280 newInc->richLocation() );
02281 }
02282
02283 if ( oldInc->description() != newInc->description() ) {
02284 mChanges += i18n( "The description has been changed to: \"%1\"",
02285 newInc->richDescription() );
02286 }
02287
02288 Attendee::List oldAttendees = oldInc->attendees();
02289 Attendee::List newAttendees = newInc->attendees();
02290 for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
02291 it != newAttendees.constEnd(); ++it ) {
02292 Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
02293 if ( !oldAtt ) {
02294 mChanges += i18n( "Attendee %1 has been added", (*it)->fullName() );
02295 } else {
02296 if ( oldAtt->status() != (*it)->status() ) {
02297 mChanges += i18n( "The status of attendee %1 has been changed to: %2",
02298 (*it)->fullName(), (*it)->statusStr() );
02299 }
02300 }
02301 }
02302
02303 if ( method == iTIPRequest ) {
02304 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
02305 it != oldAttendees.constEnd(); ++it ) {
02306 if ( (*it)->email() != oldInc->organizer().email() ) {
02307 Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
02308 if ( !newAtt ) {
02309 mChanges += i18n( "Attendee %1 has been removed", (*it)->fullName() );
02310 }
02311 }
02312 }
02313 }
02314 }
02315
02316 private:
02317 Incidence *mExistingIncidence;
02318 iTIPMethod mMethod;
02319 QStringList mChanges;
02320 };
02321
02322
02323 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
02324 {
02325 if ( !id.startsWith( QLatin1String( "ATTACH:" ) ) ) {
02326 QString res = QString( "<a href=\"%1\"><b>%2</b></a>" ).
02327 arg( generateLinkURL( id ), text );
02328 return res;
02329 } else {
02330
02331 QString res = QString( "<a href=\"%1\">%2</a>" ).
02332 arg( generateLinkURL( id ), text );
02333 return res;
02334 }
02335 }
02336
02337
02338
02339 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
02340 {
02341 #ifndef KDEPIM_NO_KRESOURCES
02342 CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
02343 if ( !cal || !incidence ) {
02344 return true;
02345 }
02346 ResourceCalendar *res = cal->resource( incidence );
02347 if ( !res ) {
02348 return true;
02349 }
02350 const QString subRes = res->subresourceIdentifier( incidence );
02351 if ( !subRes.contains( "/.INBOX.directory/" ) ) {
02352 return false;
02353 }
02354 #endif
02355 return true;
02356 }
02357
02358
02359 static QString tdOpen = "<td style=\"border-width:2px;border-style:outset\">";
02360 static QString tdClose = "</td>";
02361
02362 static QString responseButtons( Incidence *inc, bool rsvpReq, bool rsvpRec,
02363 InvitationFormatterHelper *helper )
02364 {
02365 QString html;
02366 if ( !helper ) {
02367 return html;
02368 }
02369
02370 if ( !rsvpReq && ( inc && inc->revision() == 0 ) ) {
02371
02372 html += tdOpen;
02373 html += helper->makeLink( "record", i18n( "[Record]" ) );
02374 html += tdClose;
02375
02376
02377 html += tdOpen;
02378 html += helper->makeLink( "delete", i18n( "[Move to Trash]" ) );
02379 html += tdClose;
02380
02381 } else {
02382
02383
02384 html += tdOpen;
02385 html += helper->makeLink( "accept", i18nc( "accept invitation", "Accept" ) );
02386 html += tdClose;
02387
02388
02389 html += tdOpen;
02390 html += helper->makeLink( "accept_conditionally",
02391 i18nc( "Accept invitation conditionally", "Accept cond." ) );
02392 html += tdClose;
02393
02394
02395 html += tdOpen;
02396 html += helper->makeLink( "counter",
02397 i18nc( "invitation counter proposal", "Counter proposal" ) );
02398 html += tdClose;
02399
02400
02401 html += tdOpen;
02402 html += helper->makeLink( "decline",
02403 i18nc( "decline invitation", "Decline" ) );
02404 html += tdClose;
02405 }
02406
02407 if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
02408
02409 html += tdOpen;
02410 html += helper->makeLink( "delegate",
02411 i18nc( "delegate inviation to another", "Delegate" ) );
02412 html += tdClose;
02413
02414
02415 html += tdOpen;
02416 html += helper->makeLink( "forward",
02417 i18nc( "forward request to another", "Forward" ) );
02418 html += tdClose;
02419
02420
02421 if ( inc && inc->type() == "Event" ) {
02422 html += tdOpen;
02423 html += helper->makeLink( "check_calendar",
02424 i18nc( "look for scheduling conflicts", "Check my calendar" ) );
02425 html += tdClose;
02426 }
02427 }
02428 return html;
02429 }
02430
02431 static QString counterButtons( Incidence *incidence,
02432 InvitationFormatterHelper *helper )
02433 {
02434 QString html;
02435 if ( !helper ) {
02436 return html;
02437 }
02438
02439
02440 html += tdOpen;
02441 html += helper->makeLink( "accept_counter", i18n( "[Accept]" ) );
02442 html += tdClose;
02443
02444
02445 html += tdOpen;
02446 html += helper->makeLink( "decline_counter", i18n( "[Decline]" ) );
02447 html += tdClose;
02448
02449
02450 if ( incidence && incidence->type() == "Event" ) {
02451 html += tdOpen;
02452 html += helper->makeLink( "check_calendar", i18n( "[Check my calendar] " ) );
02453 html += tdClose;
02454 }
02455 return html;
02456 }
02457
02458 Calendar *InvitationFormatterHelper::calendar() const
02459 {
02460 return 0;
02461 }
02462
02463 static QString formatICalInvitationHelper( QString invitation,
02464 Calendar *mCalendar,
02465 InvitationFormatterHelper *helper,
02466 bool noHtmlMode,
02467 KDateTime::Spec spec,
02468 const QString &sender )
02469 {
02470 if ( invitation.isEmpty() ) {
02471 return QString();
02472 }
02473
02474 ICalFormat format;
02475
02476
02477 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
02478
02479 if( !msg ) {
02480 kDebug() << "Failed to parse the scheduling message";
02481 Q_ASSERT( format.exception() );
02482 kDebug() << format.exception()->message();
02483 return QString();
02484 }
02485
02486 IncidenceBase *incBase = msg->event();
02487 incBase->shiftTimes( mCalendar->timeSpec(), KDateTime::Spec::LocalZone() );
02488
02489
02490 Incidence *existingIncidence = 0;
02491 if ( incBase && helper->calendar() ) {
02492 existingIncidence = helper->calendar()->incidence( incBase->uid() );
02493 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
02494 existingIncidence = 0;
02495 }
02496 if ( !existingIncidence ) {
02497 const Incidence::List list = helper->calendar()->incidences();
02498 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
02499 if ( (*it)->schedulingID() == incBase->uid() &&
02500 incidenceOwnedByMe( helper->calendar(), *it ) ) {
02501 existingIncidence = *it;
02502 break;
02503 }
02504 }
02505 }
02506 }
02507
02508
02509 QString html;
02510 html += "<div align=\"center\" style=\"border:solid 1px;\">";
02511
02512 IncidenceFormatter::InvitationHeaderVisitor headerVisitor;
02513
02514 if ( !headerVisitor.act( incBase, existingIncidence, msg, sender ) ) {
02515 return QString();
02516 }
02517 html += htmlAddTag( "h3", headerVisitor.result() );
02518
02519 IncidenceFormatter::InvitationBodyVisitor bodyVisitor( noHtmlMode, spec );
02520 if ( !bodyVisitor.act( incBase, existingIncidence, msg, sender ) ) {
02521 return QString();
02522 }
02523 html += bodyVisitor.result();
02524
02525 if ( msg->method() == iTIPRequest ) {
02526 IncidenceFormatter::IncidenceCompareVisitor compareVisitor;
02527 if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
02528 html += "<p align=\"left\">";
02529 html += i18n( "The following changes have been made by the organizer:" );
02530 html += "</p>";
02531 html += compareVisitor.result();
02532 }
02533 }
02534 if ( msg->method() == iTIPReply ) {
02535 IncidenceCompareVisitor compareVisitor;
02536 if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
02537 html += "<p align=\"left\">";
02538 if ( !sender.isEmpty() ) {
02539 html += i18n( "The following changes have been made by %1:", sender );
02540 } else {
02541 html += i18n( "The following changes have been made by an attendee:" );
02542 }
02543 html += "</p>";
02544 html += compareVisitor.result();
02545 }
02546 }
02547
02548 Incidence *inc = dynamic_cast<Incidence*>( incBase );
02549
02550
02551 bool myInc = iamOrganizer( inc );
02552
02553
02554 bool rsvpRec = false;
02555 Attendee *ea = 0;
02556 if ( !myInc ) {
02557 Incidence *rsvpIncidence = existingIncidence;
02558 if ( !rsvpIncidence && inc && inc->revision() > 0 ) {
02559 rsvpIncidence = inc;
02560 }
02561 if ( rsvpIncidence ) {
02562 ea = findMyAttendee( rsvpIncidence );
02563 }
02564 if ( ea &&
02565 ( ea->status() == Attendee::Accepted ||
02566 ea->status() == Attendee::Declined ||
02567 ea->status() == Attendee::Tentative ) ) {
02568 rsvpRec = true;
02569 }
02570 }
02571
02572
02573 QString role;
02574 bool isDelegated = false;
02575 Attendee *a = findMyAttendee( inc );
02576 if ( !a && inc ) {
02577 if ( !inc->attendees().isEmpty() ) {
02578 a = inc->attendees().first();
02579 }
02580 }
02581 if ( a ) {
02582 isDelegated = ( a->status() == Attendee::Delegated );
02583 role = Attendee::roleName( a->role() );
02584 }
02585
02586
02587 bool rsvpReq = rsvpRequested( inc );
02588 if ( !myInc && a ) {
02589 html += "<br/>";
02590 html += "<i><u>";
02591 if ( rsvpRec && inc ) {
02592 if ( inc->revision() == 0 ) {
02593 html += i18n( "Your <b>%1</b> response has already been recorded", ea->statusStr() );
02594 } else {
02595 html += i18n( "Your status for this invitation is <b>%1</b>", ea->statusStr() );
02596 }
02597 rsvpReq = false;
02598 } else if ( msg->method() == iTIPCancel ) {
02599 html += i18n( "This invitation was declined" );
02600 } else if ( msg->method() == iTIPAdd ) {
02601 html += i18n( "This invitation was accepted" );
02602 } else {
02603 if ( !isDelegated ) {
02604 html += rsvpRequestedStr( rsvpReq, role );
02605 } else {
02606 html += i18n( "Awaiting delegation response" );
02607 }
02608 }
02609 html += "</u></i>";
02610 }
02611
02612
02613 if ( !myInc ) {
02614 if ( inc && inc->revision() == 0 ) {
02615 QString statStr = myStatusStr( inc );
02616 if ( !statStr.isEmpty() ) {
02617 html += "<br/>";
02618 html += "<i>";
02619 html += statStr;
02620 html += "</i>";
02621 }
02622 }
02623 }
02624
02625
02626
02627 html += "<p>";
02628 html += "<table border=\"0\" align=\"center\" cellspacing=\"4\"><tr>";
02629
02630 switch ( msg->method() ) {
02631 case iTIPPublish:
02632 case iTIPRequest:
02633 case iTIPRefresh:
02634 case iTIPAdd:
02635 {
02636 if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
02637 if ( inc->type() == "Todo" ) {
02638 html += helper->makeLink( "reply", i18n( "[Record invitation in my to-do list]" ) );
02639 } else {
02640 html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) );
02641 }
02642 }
02643
02644 if ( !myInc && a ) {
02645 html += responseButtons( inc, rsvpReq, rsvpRec, helper );
02646 }
02647 break;
02648 }
02649
02650 case iTIPCancel:
02651
02652 if ( inc ) {
02653 html += tdOpen;
02654 if ( inc->type() == "Todo" ) {
02655 html += helper->makeLink( "cancel",
02656 i18n( "Remove invitation from my to-do list" ) );
02657 } else {
02658 html += helper->makeLink( "cancel",
02659 i18n( "Remove invitation from my calendar" ) );
02660 }
02661 html += tdClose;
02662 }
02663 break;
02664
02665 case iTIPReply:
02666 {
02667
02668 Attendee *a = 0;
02669 Attendee *ea = 0;
02670 if ( inc ) {
02671
02672 if ( replyMeansCounter( inc ) ) {
02673 html += "<tr>" + counterButtons( inc, helper ) + "</tr>";
02674 break;
02675 }
02676
02677
02678
02679
02680
02681
02682 a = findDelegatedFromMyAttendee( inc );
02683 if ( a ) {
02684 if ( a->status() != Attendee::Accepted ||
02685 a->status() != Attendee::Tentative ) {
02686 html += responseButtons( inc, rsvpReq, rsvpRec, helper );
02687 break;
02688 }
02689 }
02690
02691
02692 if ( !inc->attendees().isEmpty() ) {
02693 a = inc->attendees().first();
02694 }
02695 if ( a && helper->calendar() ) {
02696 ea = findAttendee( existingIncidence, a->email() );
02697 }
02698 }
02699 if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
02700 html += tdOpen;
02701 html += htmlAddTag( "i", i18n( "The response has already been recorded" ) );
02702 html += tdClose;
02703 } else {
02704 if ( inc ) {
02705 if ( inc->type() == "Todo" ) {
02706 html += helper->makeLink( "reply", i18n( "[Record response in my to-do list]" ) );
02707 } else {
02708 html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) );
02709 }
02710 }
02711 }
02712 break;
02713 }
02714
02715 case iTIPCounter:
02716
02717 html += counterButtons( inc, helper );
02718 break;
02719
02720 case iTIPDeclineCounter:
02721 case iTIPNoMethod:
02722 break;
02723 }
02724
02725
02726 html += "</tr></table>";
02727
02728
02729 if ( myInc && helper->calendar() ) {
02730 html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
02731 }
02732
02733
02734 html += "</div>";
02735
02736
02737 html += invitationAttachments( helper, inc );
02738
02739 return html;
02740 }
02741
02742
02743 QString IncidenceFormatter::formatICalInvitation( QString invitation,
02744 Calendar *calendar,
02745 InvitationFormatterHelper *helper )
02746 {
02747 return formatICalInvitationHelper( invitation, calendar, helper, false,
02748 KSystemTimeZones::local(), QString() );
02749 }
02750
02751 QString IncidenceFormatter::formatICalInvitationNoHtml( QString invitation,
02752 Calendar *calendar,
02753 InvitationFormatterHelper *helper )
02754 {
02755 return formatICalInvitationHelper( invitation, calendar, helper, true,
02756 KSystemTimeZones::local(), QString() );
02757 }
02758
02759 QString IncidenceFormatter::formatICalInvitationNoHtml( const QString &invitation,
02760 Calendar *calendar,
02761 InvitationFormatterHelper *helper,
02762 const QString &sender )
02763 {
02764 return formatICalInvitationHelper( invitation, calendar, helper, true,
02765 KSystemTimeZones::local(), sender );
02766 }
02767
02768
02769
02770
02771
02772
02773 class KCal::IncidenceFormatter::ToolTipVisitor
02774 : public IncidenceBase::Visitor
02775 {
02776 public:
02777 ToolTipVisitor()
02778 : mRichText( true ), mSpec( KDateTime::Spec() ), mResult( "" ) {}
02779
02780 bool act( Calendar *calendar, IncidenceBase *incidence,
02781 const QDate &date=QDate(), bool richText=true,
02782 KDateTime::Spec spec=KDateTime::Spec() )
02783 {
02784 mCalendar = calendar;
02785 mLocation.clear();
02786 mDate = date;
02787 mRichText = richText;
02788 mSpec = spec;
02789 mResult = "";
02790 return incidence ? incidence->accept( *this ) : false;
02791 }
02792
02793 bool act( const QString &location, IncidenceBase *incidence,
02794 const QDate &date=QDate(), bool richText=true,
02795 KDateTime::Spec spec=KDateTime::Spec() )
02796 {
02797 mCalendar = 0;
02798 mLocation = location;
02799 mDate = date;
02800 mRichText = richText;
02801 mSpec = spec;
02802 mResult = "";
02803 return incidence ? incidence->accept( *this ) : false;
02804 }
02805
02806 QString result() const { return mResult; }
02807
02808 protected:
02809 bool visit( Event *event );
02810 bool visit( Todo *todo );
02811 bool visit( Journal *journal );
02812 bool visit( FreeBusy *fb );
02813
02814 QString dateRangeText( Event *event, const QDate &date );
02815 QString dateRangeText( Todo *todo, const QDate &date );
02816 QString dateRangeText( Journal *journal );
02817 QString dateRangeText( FreeBusy *fb );
02818
02819 QString generateToolTip( Incidence *incidence, QString dtRangeText );
02820
02821 protected:
02822 Calendar *mCalendar;
02823 QString mLocation;
02824 QDate mDate;
02825 bool mRichText;
02826 KDateTime::Spec mSpec;
02827 QString mResult;
02828 };
02829
02830 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const QDate &date )
02831 {
02832
02833 QString ret;
02834 QString tmp;
02835
02836 KDateTime startDt = event->dtStart();
02837 KDateTime endDt = event->dtEnd();
02838 if ( event->recurs() ) {
02839 if ( date.isValid() ) {
02840 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
02841 int diffDays = startDt.daysTo( kdt );
02842 kdt = kdt.addSecs( -1 );
02843 startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
02844 if ( event->hasEndDate() ) {
02845 endDt = endDt.addDays( diffDays );
02846 if ( startDt > endDt ) {
02847 startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
02848 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
02849 }
02850 }
02851 }
02852 }
02853
02854 if ( event->isMultiDay() ) {
02855 tmp = dateToString( startDt, true, mSpec );
02856 ret += "<br>" + i18nc( "Event start", "<i>From:</i> %1", tmp );
02857
02858 tmp = dateToString( endDt, true, mSpec );
02859 ret += "<br>" + i18nc( "Event end","<i>To:</i> %1", tmp );
02860
02861 } else {
02862
02863 ret += "<br>" +
02864 i18n( "<i>Date:</i> %1", dateToString( startDt, false, mSpec ) );
02865 if ( !event->allDay() ) {
02866 const QString dtStartTime = timeToString( startDt, true, mSpec );
02867 const QString dtEndTime = timeToString( endDt, true, mSpec );
02868 if ( dtStartTime == dtEndTime ) {
02869
02870 tmp = "<br>" +
02871 i18nc( "time for event", "<i>Time:</i> %1",
02872 dtStartTime );
02873 } else {
02874 tmp = "<br>" +
02875 i18nc( "time range for event",
02876 "<i>Time:</i> %1 - %2",
02877 dtStartTime, dtEndTime );
02878 }
02879 ret += tmp;
02880 }
02881 }
02882 return ret.replace( ' ', " " );
02883 }
02884
02885 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const QDate &date )
02886 {
02887
02888 QString ret;
02889 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
02890 KDateTime startDt = todo->dtStart();
02891 if ( todo->recurs() ) {
02892 if ( date.isValid() ) {
02893 startDt.setDate( date );
02894 }
02895 }
02896 ret += "<br>" +
02897 i18n( "<i>Start:</i> %1", dateToString( startDt, false, mSpec ) );
02898 }
02899
02900 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
02901 KDateTime dueDt = todo->dtDue();
02902 if ( todo->recurs() ) {
02903 if ( date.isValid() ) {
02904 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
02905 kdt = kdt.addSecs( -1 );
02906 dueDt.setDate( todo->recurrence()->getNextDateTime( kdt ).date() );
02907 }
02908 }
02909 ret += "<br>" +
02910 i18n( "<i>Due:</i> %1",
02911 dateTimeToString( dueDt, todo->allDay(), false, mSpec ) );
02912 }
02913
02914
02915
02916 if ( todo->priority() > 0 ) {
02917 ret += "<br>";
02918 ret += "<i>" + i18n( "Priority:" ) + "</i>" + " ";
02919 ret += QString::number( todo->priority() );
02920 }
02921
02922 ret += "<br>";
02923 if ( todo->isCompleted() ) {
02924 ret += "<i>" + i18nc( "Completed: date", "Completed:" ) + "</i>" + " ";
02925 ret += todo->completedStr().replace( ' ', " " );
02926 } else {
02927 ret += "<i>" + i18n( "Percent Done:" ) + "</i>" + " ";
02928 ret += i18n( "%1%", todo->percentComplete() );
02929 }
02930
02931 return ret.replace( ' ', " " );
02932 }
02933
02934 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal *journal )
02935 {
02936
02937 QString ret;
02938 if ( journal->dtStart().isValid() ) {
02939 ret += "<br>" +
02940 i18n( "<i>Date:</i> %1", dateToString( journal->dtStart(), false, mSpec ) );
02941 }
02942 return ret.replace( ' ', " " );
02943 }
02944
02945 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
02946 {
02947
02948 QString ret;
02949 ret = "<br>" +
02950 i18n( "<i>Period start:</i> %1",
02951 KGlobal::locale()->formatDateTime( fb->dtStart().dateTime() ) );
02952 ret += "<br>" +
02953 i18n( "<i>Period start:</i> %1",
02954 KGlobal::locale()->formatDateTime( fb->dtEnd().dateTime() ) );
02955 return ret.replace( ' ', " " );
02956 }
02957
02958 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
02959 {
02960 mResult = generateToolTip( event, dateRangeText( event, mDate ) );
02961 return !mResult.isEmpty();
02962 }
02963
02964 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
02965 {
02966 mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
02967 return !mResult.isEmpty();
02968 }
02969
02970 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
02971 {
02972 mResult = generateToolTip( journal, dateRangeText( journal ) );
02973 return !mResult.isEmpty();
02974 }
02975
02976 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
02977 {
02978
02979 mResult = "<qt><b>" + i18n( "Free/Busy information for %1", fb->organizer().fullName() ) + "</b>";
02980 mResult += dateRangeText( fb );
02981 mResult += "</qt>";
02982 return !mResult.isEmpty();
02983 }
02984
02985 static QString tooltipPerson( const QString &email, QString name )
02986 {
02987
02988
02989 if ( name.isEmpty() && !email.isEmpty() ) {
02990 #ifndef KDEPIM_NO_KRESOURCES
02991 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
02992 KABC::Addressee::List addressList = add_book->findByEmail( email );
02993 if ( !addressList.isEmpty() ) {
02994 KABC::Addressee o = addressList.first();
02995 if ( !o.isEmpty() && addressList.size() < 2 ) {
02996
02997 name = o.formattedName();
02998 }
02999 }
03000 #endif
03001 }
03002
03003
03004 QString tmpString = ( name.isEmpty() ? email : name );
03005
03006 return tmpString;
03007 }
03008
03009 static QString tooltipFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
03010 {
03011 int maxNumAtts = 8;
03012 QString sep = i18nc( "separator for lists of people names", ", " );
03013 int sepLen = sep.length();
03014
03015 int i = 0;
03016 QString tmpStr;
03017 Attendee::List::ConstIterator it;
03018 Attendee::List attendees = incidence->attendees();
03019
03020 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
03021 Attendee *a = *it;
03022 if ( a->role() != role ) {
03023
03024 continue;
03025 }
03026 if ( a->email() == incidence->organizer().email() ) {
03027
03028 continue;
03029 }
03030 if ( i == maxNumAtts ) {
03031 static QString etc = i18nc( "elipsis", "..." );
03032 tmpStr += etc;
03033 break;
03034 }
03035 tmpStr += tooltipPerson( a->email(), a->name() );
03036 if ( !a->delegator().isEmpty() ) {
03037 tmpStr += i18n( " (delegated by %1)", a->delegator() );
03038 }
03039 if ( !a->delegate().isEmpty() ) {
03040 tmpStr += i18n( " (delegated to %1)", a->delegate() );
03041 }
03042 tmpStr += sep;
03043 i++;
03044 }
03045 if ( tmpStr.endsWith( sep ) ) {
03046 tmpStr.truncate( tmpStr.length() - sepLen );
03047 }
03048 return tmpStr;
03049 }
03050
03051 static QString tooltipFormatAttendees( Incidence *incidence )
03052 {
03053 QString tmpStr, str;
03054
03055
03056 int attendeeCount = incidence->attendees().count();
03057 if ( attendeeCount > 1 ||
03058 ( attendeeCount == 1 &&
03059 incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
03060 tmpStr += "<i>" + i18n( "Organizer:" ) + "</i>" + " ";
03061 tmpStr += tooltipPerson( incidence->organizer().email(),
03062 incidence->organizer().name() );
03063 }
03064
03065
03066 str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair );
03067 if ( !str.isEmpty() ) {
03068 tmpStr += "<br><i>" + i18n( "Chair:" ) + "</i>" + " ";
03069 tmpStr += str;
03070 }
03071
03072
03073 str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
03074 if ( !str.isEmpty() ) {
03075 tmpStr += "<br><i>" + i18n( "Required Participants:" ) + "</i>" + " ";
03076 tmpStr += str;
03077 }
03078
03079
03080 str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
03081 if ( !str.isEmpty() ) {
03082 tmpStr += "<br><i>" + i18n( "Optional Participants:" ) + "</i>" + " ";
03083 tmpStr += str;
03084 }
03085
03086
03087 str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
03088 if ( !str.isEmpty() ) {
03089 tmpStr += "<br><i>" + i18n( "Observers:" ) + "</i>" + " ";
03090 tmpStr += str;
03091 }
03092
03093 return tmpStr;
03094 }
03095
03096 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence *incidence,
03097 QString dtRangeText )
03098 {
03099 int maxDescLen = 120;
03100
03101
03102 if ( !incidence ) {
03103 return QString();
03104 }
03105
03106 QString tmp = "<qt>";
03107
03108
03109 tmp += "<b>" + incidence->richSummary() + "</b>";
03110 tmp += "<hr>";
03111
03112 QString calStr = mLocation;
03113 if ( mCalendar ) {
03114 calStr = resourceString( mCalendar, incidence );
03115 }
03116 if ( !calStr.isEmpty() ) {
03117 tmp += "<i>" + i18n( "Calendar:" ) + "</i>" + " ";
03118 tmp += calStr;
03119 }
03120
03121 tmp += dtRangeText;
03122
03123 if ( !incidence->location().isEmpty() ) {
03124 tmp += "<br>";
03125 tmp += "<i>" + i18n( "Location:" ) + "</i>" + " ";
03126 tmp += incidence->richLocation();
03127 }
03128
03129 QString durStr = durationString( incidence );
03130 if ( !durStr.isEmpty() ) {
03131 tmp += "<br>";
03132 tmp += "<i>" + i18n( "Duration:" ) + "</i>" + " ";
03133 tmp += durStr;
03134 }
03135
03136 if ( incidence->recurs() ) {
03137 tmp += "<br>";
03138 tmp += "<i>" + i18n( "Recurrence:" ) + "</i>" + " ";
03139 tmp += recurrenceString( incidence );
03140 }
03141
03142 if ( !incidence->description().isEmpty() ) {
03143 QString desc( incidence->description() );
03144 if ( !incidence->descriptionIsRich() ) {
03145 if ( desc.length() > maxDescLen ) {
03146 static QString etc = i18nc( "elipsis", "..." );
03147 desc = desc.left( maxDescLen ) + etc;
03148 }
03149 desc = Qt::escape( desc ).replace( '\n', "<br>" );
03150 } else {
03151
03152 }
03153 tmp += "<hr>";
03154 tmp += "<i>" + i18n( "Description:" ) + "</i>" + "<br>";
03155 tmp += desc;
03156 tmp += "<hr>";
03157 }
03158
03159 int reminderCount = incidence->alarms().count();
03160 if ( reminderCount > 0 && incidence->isAlarmEnabled() ) {
03161 tmp += "<br>";
03162 tmp += "<i>" + i18np( "Reminder:", "Reminders:", reminderCount ) + "</i>" + " ";
03163 tmp += reminderStringList( incidence ).join( ", " );
03164 }
03165
03166 tmp += "<br>";
03167 tmp += tooltipFormatAttendees( incidence );
03168
03169 int categoryCount = incidence->categories().count();
03170 if ( categoryCount > 0 ) {
03171 tmp += "<br>";
03172 tmp += "<i>" + i18np( "Category:", "Categories:", categoryCount ) + "</i>" + " ";
03173 tmp += incidence->categories().join( ", " );
03174 }
03175
03176 tmp += "</qt>";
03177 return tmp;
03178 }
03179
03180
03181 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence,
03182 bool richText )
03183 {
03184 return toolTipStr( 0, incidence, QDate(), richText, KDateTime::Spec() );
03185 }
03186
03187 QString IncidenceFormatter::toolTipStr( IncidenceBase *incidence,
03188 bool richText, KDateTime::Spec spec )
03189 {
03190 ToolTipVisitor v;
03191 if ( v.act( 0, incidence, QDate(), richText, spec ) ) {
03192 return v.result();
03193 } else {
03194 return QString();
03195 }
03196 }
03197
03198 QString IncidenceFormatter::toolTipStr( Calendar *calendar,
03199 IncidenceBase *incidence,
03200 const QDate &date,
03201 bool richText, KDateTime::Spec spec )
03202 {
03203 ToolTipVisitor v;
03204 if ( v.act( calendar, incidence, date, richText, spec ) ) {
03205 return v.result();
03206 } else {
03207 return QString();
03208 }
03209 }
03210
03211 QString IncidenceFormatter::toolTipStr( const QString &sourceName,
03212 IncidenceBase *incidence,
03213 const QDate &date,
03214 bool richText, KDateTime::Spec spec )
03215 {
03216 ToolTipVisitor v;
03217 if ( v.act( sourceName, incidence, date, richText, spec ) ) {
03218 return v.result();
03219 } else {
03220 return QString();
03221 }
03222 }
03223
03224
03225
03226
03227
03228
03229 static QString mailBodyIncidence( Incidence *incidence )
03230 {
03231 QString body;
03232 if ( !incidence->summary().isEmpty() ) {
03233 body += i18n( "Summary: %1\n", incidence->richSummary() );
03234 }
03235 if ( !incidence->organizer().isEmpty() ) {
03236 body += i18n( "Organizer: %1\n", incidence->organizer().fullName() );
03237 }
03238 if ( !incidence->location().isEmpty() ) {
03239 body += i18n( "Location: %1\n", incidence->richLocation() );
03240 }
03241 return body;
03242 }
03243
03244
03245
03246 class KCal::IncidenceFormatter::MailBodyVisitor
03247 : public IncidenceBase::Visitor
03248 {
03249 public:
03250 MailBodyVisitor()
03251 : mSpec( KDateTime::Spec() ), mResult( "" ) {}
03252
03253 bool act( IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec() )
03254 {
03255 mSpec = spec;
03256 mResult = "";
03257 return incidence ? incidence->accept( *this ) : false;
03258 }
03259 QString result() const
03260 {
03261 return mResult;
03262 }
03263
03264 protected:
03265 bool visit( Event *event );
03266 bool visit( Todo *todo );
03267 bool visit( Journal *journal );
03268 bool visit( FreeBusy * )
03269 {
03270 mResult = i18n( "This is a Free Busy Object" );
03271 return !mResult.isEmpty();
03272 }
03273 protected:
03274 KDateTime::Spec mSpec;
03275 QString mResult;
03276 };
03277
03278 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
03279 {
03280 QString recurrence[]= {
03281 i18nc( "no recurrence", "None" ),
03282 i18nc( "event recurs by minutes", "Minutely" ),
03283 i18nc( "event recurs by hours", "Hourly" ),
03284 i18nc( "event recurs by days", "Daily" ),
03285 i18nc( "event recurs by weeks", "Weekly" ),
03286 i18nc( "event recurs same position (e.g. first monday) each month", "Monthly Same Position" ),
03287 i18nc( "event recurs same day each month", "Monthly Same Day" ),
03288 i18nc( "event recurs same month each year", "Yearly Same Month" ),
03289 i18nc( "event recurs same day each year", "Yearly Same Day" ),
03290 i18nc( "event recurs same position (e.g. first monday) each year", "Yearly Same Position" )
03291 };
03292
03293 mResult = mailBodyIncidence( event );
03294 mResult += i18n( "Start Date: %1\n", dateToString( event->dtStart(), true, mSpec ) );
03295 if ( !event->allDay() ) {
03296 mResult += i18n( "Start Time: %1\n", timeToString( event->dtStart(), true, mSpec ) );
03297 }
03298 if ( event->dtStart() != event->dtEnd() ) {
03299 mResult += i18n( "End Date: %1\n", dateToString( event->dtEnd(), true, mSpec ) );
03300 }
03301 if ( !event->allDay() ) {
03302 mResult += i18n( "End Time: %1\n", timeToString( event->dtEnd(), true, mSpec ) );
03303 }
03304 if ( event->recurs() ) {
03305 Recurrence *recur = event->recurrence();
03306
03307 mResult += i18n( "Recurs: %1\n", recurrence[ recur->recurrenceType() ] );
03308 mResult += i18n( "Frequency: %1\n", event->recurrence()->frequency() );
03309
03310 if ( recur->duration() > 0 ) {
03311 mResult += i18np( "Repeats once", "Repeats %1 times", recur->duration() );
03312 mResult += '\n';
03313 } else {
03314 if ( recur->duration() != -1 ) {
03315
03316 QString endstr;
03317 if ( event->allDay() ) {
03318 endstr = KGlobal::locale()->formatDate( recur->endDate() );
03319 } else {
03320 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime().dateTime() );
03321 }
03322 mResult += i18n( "Repeat until: %1\n", endstr );
03323 } else {
03324 mResult += i18n( "Repeats forever\n" );
03325 }
03326 }
03327 }
03328
03329 QString details = event->richDescription();
03330 if ( !details.isEmpty() ) {
03331 mResult += i18n( "Details:\n%1\n", details );
03332 }
03333 return !mResult.isEmpty();
03334 }
03335
03336 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
03337 {
03338 mResult = mailBodyIncidence( todo );
03339
03340 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
03341 mResult += i18n( "Start Date: %1\n", dateToString( todo->dtStart( false ), true, mSpec ) );
03342 if ( !todo->allDay() ) {
03343 mResult += i18n( "Start Time: %1\n", timeToString( todo->dtStart( false ), true, mSpec ) );
03344 }
03345 }
03346 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
03347 mResult += i18n( "Due Date: %1\n", dateToString( todo->dtDue(), true, mSpec ) );
03348 if ( !todo->allDay() ) {
03349 mResult += i18n( "Due Time: %1\n", timeToString( todo->dtDue(), true, mSpec ) );
03350 }
03351 }
03352 QString details = todo->richDescription();
03353 if ( !details.isEmpty() ) {
03354 mResult += i18n( "Details:\n%1\n", details );
03355 }
03356 return !mResult.isEmpty();
03357 }
03358
03359 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
03360 {
03361 mResult = mailBodyIncidence( journal );
03362 mResult += i18n( "Date: %1\n", dateToString( journal->dtStart(), true, mSpec ) );
03363 if ( !journal->allDay() ) {
03364 mResult += i18n( "Time: %1\n", timeToString( journal->dtStart(), true, mSpec ) );
03365 }
03366 if ( !journal->description().isEmpty() ) {
03367 mResult += i18n( "Text of the journal:\n%1\n", journal->richDescription() );
03368 }
03369 return !mResult.isEmpty();
03370 }
03371
03372
03373 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
03374 {
03375 return mailBodyStr( incidence, KDateTime::Spec() );
03376 }
03377
03378 QString IncidenceFormatter::mailBodyStr( IncidenceBase *incidence,
03379 KDateTime::Spec spec )
03380 {
03381 if ( !incidence ) {
03382 return QString();
03383 }
03384
03385 MailBodyVisitor v;
03386 if ( v.act( incidence, spec ) ) {
03387 return v.result();
03388 }
03389 return QString();
03390 }
03391
03392
03393 static QString recurEnd( Incidence *incidence )
03394 {
03395 QString endstr;
03396 if ( incidence->allDay() ) {
03397 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
03398 } else {
03399 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
03400 }
03401 return endstr;
03402 }
03403
03404
03405
03406
03407
03408
03409 QString IncidenceFormatter::recurrenceString( Incidence *incidence )
03410 {
03411 if ( !incidence->recurs() ) {
03412 return i18n( "No recurrence" );
03413 }
03414 QStringList dayList;
03415 dayList.append( i18n( "31st Last" ) );
03416 dayList.append( i18n( "30th Last" ) );
03417 dayList.append( i18n( "29th Last" ) );
03418 dayList.append( i18n( "28th Last" ) );
03419 dayList.append( i18n( "27th Last" ) );
03420 dayList.append( i18n( "26th Last" ) );
03421 dayList.append( i18n( "25th Last" ) );
03422 dayList.append( i18n( "24th Last" ) );
03423 dayList.append( i18n( "23rd Last" ) );
03424 dayList.append( i18n( "22nd Last" ) );
03425 dayList.append( i18n( "21st Last" ) );
03426 dayList.append( i18n( "20th Last" ) );
03427 dayList.append( i18n( "19th Last" ) );
03428 dayList.append( i18n( "18th Last" ) );
03429 dayList.append( i18n( "17th Last" ) );
03430 dayList.append( i18n( "16th Last" ) );
03431 dayList.append( i18n( "15th Last" ) );
03432 dayList.append( i18n( "14th Last" ) );
03433 dayList.append( i18n( "13th Last" ) );
03434 dayList.append( i18n( "12th Last" ) );
03435 dayList.append( i18n( "11th Last" ) );
03436 dayList.append( i18n( "10th Last" ) );
03437 dayList.append( i18n( "9th Last" ) );
03438 dayList.append( i18n( "8th Last" ) );
03439 dayList.append( i18n( "7th Last" ) );
03440 dayList.append( i18n( "6th Last" ) );
03441 dayList.append( i18n( "5th Last" ) );
03442 dayList.append( i18n( "4th Last" ) );
03443 dayList.append( i18n( "3rd Last" ) );
03444 dayList.append( i18n( "2nd Last" ) );
03445 dayList.append( i18nc( "last day of the month", "Last" ) );
03446 dayList.append( i18nc( "unknown day of the month", "unknown" ) );
03447 dayList.append( i18n( "1st" ) );
03448 dayList.append( i18n( "2nd" ) );
03449 dayList.append( i18n( "3rd" ) );
03450 dayList.append( i18n( "4th" ) );
03451 dayList.append( i18n( "5th" ) );
03452 dayList.append( i18n( "6th" ) );
03453 dayList.append( i18n( "7th" ) );
03454 dayList.append( i18n( "8th" ) );
03455 dayList.append( i18n( "9th" ) );
03456 dayList.append( i18n( "10th" ) );
03457 dayList.append( i18n( "11th" ) );
03458 dayList.append( i18n( "12th" ) );
03459 dayList.append( i18n( "13th" ) );
03460 dayList.append( i18n( "14th" ) );
03461 dayList.append( i18n( "15th" ) );
03462 dayList.append( i18n( "16th" ) );
03463 dayList.append( i18n( "17th" ) );
03464 dayList.append( i18n( "18th" ) );
03465 dayList.append( i18n( "19th" ) );
03466 dayList.append( i18n( "20th" ) );
03467 dayList.append( i18n( "21st" ) );
03468 dayList.append( i18n( "22nd" ) );
03469 dayList.append( i18n( "23rd" ) );
03470 dayList.append( i18n( "24th" ) );
03471 dayList.append( i18n( "25th" ) );
03472 dayList.append( i18n( "26th" ) );
03473 dayList.append( i18n( "27th" ) );
03474 dayList.append( i18n( "28th" ) );
03475 dayList.append( i18n( "29th" ) );
03476 dayList.append( i18n( "30th" ) );
03477 dayList.append( i18n( "31st" ) );
03478 int weekStart = KGlobal::locale()->weekStartDay();
03479 QString dayNames;
03480 QString txt;
03481 const KCalendarSystem *calSys = KGlobal::locale()->calendar();
03482 Recurrence *recur = incidence->recurrence();
03483 switch ( recur->recurrenceType() ) {
03484 case Recurrence::rNone:
03485 return i18n( "No recurrence" );
03486 case Recurrence::rMinutely:
03487 if ( recur->duration() != -1 ) {
03488 txt = i18np( "Recurs every minute until %2",
03489 "Recurs every %1 minutes until %2",
03490 recur->frequency(), recurEnd( incidence ) );
03491 if ( recur->duration() > 0 ) {
03492 txt += i18nc( "number of occurrences",
03493 " (<numid>%1</numid> occurrences)",
03494 recur->duration() );
03495 }
03496 return txt;
03497 }
03498 return i18np( "Recurs every minute",
03499 "Recurs every %1 minutes", recur->frequency() );
03500 case Recurrence::rHourly:
03501 if ( recur->duration() != -1 ) {
03502 txt = i18np( "Recurs hourly until %2",
03503 "Recurs every %1 hours until %2",
03504 recur->frequency(), recurEnd( incidence ) );
03505 if ( recur->duration() > 0 ) {
03506 txt += i18nc( "number of occurrences",
03507 " (<numid>%1</numid> occurrences)",
03508 recur->duration() );
03509 }
03510 return txt;
03511 }
03512 return i18np( "Recurs hourly", "Recurs every %1 hours", recur->frequency() );
03513 case Recurrence::rDaily:
03514 if ( recur->duration() != -1 ) {
03515 txt = i18np( "Recurs daily until %2",
03516 "Recurs every %1 days until %2",
03517 recur->frequency(), recurEnd( incidence ) );
03518 if ( recur->duration() > 0 ) {
03519 txt += i18nc( "number of occurrences",
03520 " (<numid>%1</numid> occurrences)",
03521 recur->duration() );
03522 }
03523 return txt;
03524 }
03525 return i18np( "Recurs daily", "Recurs every %1 days", recur->frequency() );
03526 case Recurrence::rWeekly:
03527 {
03528 bool addSpace = false;
03529 for ( int i = 0; i < 7; ++i ) {
03530 if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
03531 if ( addSpace ) {
03532 dayNames.append( i18nc( "separator for list of days", ", " ) );
03533 }
03534 dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1,
03535 KCalendarSystem::ShortDayName ) );
03536 addSpace = true;
03537 }
03538 }
03539 if ( dayNames.isEmpty() ) {
03540 dayNames = i18nc( "Recurs weekly on no days", "no days" );
03541 }
03542 if ( recur->duration() != -1 ) {
03543 txt = i18ncp( "Recurs weekly on [list of days] until end-date",
03544 "Recurs weekly on %2 until %3",
03545 "Recurs every <numid>%1</numid> weeks on %2 until %3",
03546 recur->frequency(), dayNames, recurEnd( incidence ) );
03547 if ( recur->duration() > 0 ) {
03548 txt += i18nc( "number of occurrences",
03549 " (<numid>%1</numid> occurrences)",
03550 recur->duration() );
03551 }
03552 return txt;
03553 }
03554 return i18ncp( "Recurs weekly on [list of days]",
03555 "Recurs weekly on %2",
03556 "Recurs every <numid>%1</numid> weeks on %2",
03557 recur->frequency(), dayNames );
03558 }
03559 case Recurrence::rMonthlyPos:
03560 {
03561 if ( !recur->monthPositions().isEmpty() ) {
03562 KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0];
03563 if ( recur->duration() != -1 ) {
03564 txt = i18ncp( "Recurs every N months on the [2nd|3rd|...]"
03565 " weekdayname until end-date",
03566 "Recurs every month on the %2 %3 until %4",
03567 "Recurs every <numid>%1</numid> months on the %2 %3 until %4",
03568 recur->frequency(),
03569 dayList[rule.pos() + 31],
03570 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
03571 recurEnd( incidence ) );
03572 if ( recur->duration() > 0 ) {
03573 txt += i18nc( "number of occurrences",
03574 " (<numid>%1</numid> occurrences)",
03575 recur->duration() );
03576 }
03577 return txt;
03578 }
03579 return i18ncp( "Recurs every N months on the [2nd|3rd|...] weekdayname",
03580 "Recurs every month on the %2 %3",
03581 "Recurs every %1 months on the %2 %3",
03582 recur->frequency(),
03583 dayList[rule.pos() + 31],
03584 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ) );
03585 }
03586 break;
03587 }
03588 case Recurrence::rMonthlyDay:
03589 {
03590 if ( !recur->monthDays().isEmpty() ) {
03591 int days = recur->monthDays()[0];
03592 if ( recur->duration() != -1 ) {
03593 txt = i18ncp( "Recurs monthly on the [1st|2nd|...] day until end-date",
03594 "Recurs monthly on the %2 day until %3",
03595 "Recurs every %1 months on the %2 day until %3",
03596 recur->frequency(),
03597 dayList[days + 31],
03598 recurEnd( incidence ) );
03599 if ( recur->duration() > 0 ) {
03600 txt += i18nc( "number of occurrences",
03601 " (<numid>%1</numid> occurrences)",
03602 recur->duration() );
03603 }
03604 return txt;
03605 }
03606 return i18ncp( "Recurs monthly on the [1st|2nd|...] day",
03607 "Recurs monthly on the %2 day",
03608 "Recurs every <numid>%1</numid> month on the %2 day",
03609 recur->frequency(),
03610 dayList[days + 31] );
03611 }
03612 break;
03613 }
03614 case Recurrence::rYearlyMonth:
03615 {
03616 if ( recur->duration() != -1 ) {
03617 if ( !recur->yearDates().isEmpty() && !recur->yearMonths().isEmpty() ) {
03618 txt = i18ncp( "Recurs Every N years on month-name [1st|2nd|...]"
03619 " until end-date",
03620 "Recurs yearly on %2 %3 until %4",
03621 "Recurs every %1 years on %2 %3 until %4",
03622 recur->frequency(),
03623 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
03624 dayList[ recur->yearDates()[0] + 31 ],
03625 recurEnd( incidence ) );
03626 if ( recur->duration() > 0 ) {
03627 txt += i18nc( "number of occurrences",
03628 " (<numid>%1</numid> occurrences)",
03629 recur->duration() );
03630 }
03631 return txt;
03632 }
03633 }
03634 if ( !recur->yearDates().isEmpty() && !recur->yearMonths().isEmpty() ) {
03635 return i18ncp( "Recurs Every N years on month-name [1st|2nd|...]",
03636 "Recurs yearly on %2 %3",
03637 "Recurs every %1 years on %2 %3",
03638 recur->frequency(),
03639 calSys->monthName( recur->yearMonths()[0],
03640 recur->startDate().year() ),
03641 dayList[ recur->yearDates()[0] + 31 ] );
03642 } else {
03643 if (!recur->yearMonths().isEmpty() ) {
03644 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
03645 "Recurs yearly on %1 %2",
03646 calSys->monthName( recur->yearMonths()[0],
03647 recur->startDate().year() ),
03648 dayList[ recur->startDate().day() + 31 ] );
03649 } else {
03650 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
03651 "Recurs yearly on %1 %2",
03652 calSys->monthName( recur->startDate().month(),
03653 recur->startDate().year() ),
03654 dayList[ recur->startDate().day() + 31 ] );
03655 }
03656 }
03657 break;
03658 }
03659 case Recurrence::rYearlyDay:
03660 if ( !recur->yearDays().isEmpty() ) {
03661 if ( recur->duration() != -1 ) {
03662 txt = i18ncp( "Recurs every N years on day N until end-date",
03663 "Recurs every year on day <numid>%2</numid> until %3",
03664 "Recurs every <numid>%1</numid> years"
03665 " on day <numid>%2</numid> until %3",
03666 recur->frequency(),
03667 recur->yearDays()[0],
03668 recurEnd( incidence ) );
03669 if ( recur->duration() > 0 ) {
03670 txt += i18nc( "number of occurrences",
03671 " (<numid>%1</numid> occurrences)",
03672 recur->duration() );
03673 }
03674 return txt;
03675 }
03676 return i18ncp( "Recurs every N YEAR[S] on day N",
03677 "Recurs every year on day <numid>%2</numid>",
03678 "Recurs every <numid>%1</numid> years"
03679 " on day <numid>%2</numid>",
03680 recur->frequency(), recur->yearDays()[0] );
03681 }
03682 break;
03683 case Recurrence::rYearlyPos:
03684 {
03685 if ( !recur->yearMonths().isEmpty() && !recur->yearPositions().isEmpty() ) {
03686 KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0];
03687 if ( recur->duration() != -1 ) {
03688 txt = i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
03689 "of monthname until end-date",
03690 "Every year on the %2 %3 of %4 until %5",
03691 "Every <numid>%1</numid> years on the %2 %3 of %4"
03692 " until %5",
03693 recur->frequency(),
03694 dayList[rule.pos() + 31],
03695 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
03696 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
03697 recurEnd( incidence ) );
03698 if ( recur->duration() > 0 ) {
03699 txt += i18nc( "number of occurrences",
03700 " (<numid>%1</numid> occurrences)",
03701 recur->duration() );
03702 }
03703 return txt;
03704 }
03705 return i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
03706 "of monthname",
03707 "Every year on the %2 %3 of %4",
03708 "Every <numid>%1</numid> years on the %2 %3 of %4",
03709 recur->frequency(),
03710 dayList[rule.pos() + 31],
03711 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
03712 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) );
03713 }
03714 }
03715 break;
03716 }
03717 return i18n( "Incidence recurs" );
03718 }
03719
03720 QString IncidenceFormatter::timeToString( const KDateTime &date,
03721 bool shortfmt,
03722 const KDateTime::Spec &spec )
03723 {
03724 if ( spec.isValid() ) {
03725
03726 QString timeZone;
03727 if ( spec.timeZone() != KSystemTimeZones::local() ) {
03728 timeZone = ' ' + spec.timeZone().name();
03729 }
03730
03731 return KGlobal::locale()->formatTime( date.toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
03732 } else {
03733 return KGlobal::locale()->formatTime( date.time(), !shortfmt );
03734 }
03735 }
03736
03737 QString IncidenceFormatter::dateToString( const KDateTime &date,
03738 bool shortfmt,
03739 const KDateTime::Spec &spec )
03740 {
03741 if ( spec.isValid() ) {
03742
03743 QString timeZone;
03744 if ( spec.timeZone() != KSystemTimeZones::local() ) {
03745 timeZone = ' ' + spec.timeZone().name();
03746 }
03747
03748 return
03749 KGlobal::locale()->formatDate( date.toTimeSpec( spec ).date(),
03750 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) +
03751 timeZone;
03752 } else {
03753 return
03754 KGlobal::locale()->formatDate( date.date(),
03755 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
03756 }
03757 }
03758
03759 QString IncidenceFormatter::dateTimeToString( const KDateTime &date,
03760 bool allDay,
03761 bool shortfmt,
03762 const KDateTime::Spec &spec )
03763 {
03764 if ( allDay ) {
03765 return dateToString( date, shortfmt, spec );
03766 }
03767
03768 if ( spec.isValid() ) {
03769 QString timeZone;
03770 if ( spec.timeZone() != KSystemTimeZones::local() ) {
03771 timeZone = ' ' + spec.timeZone().name();
03772 }
03773
03774 return KGlobal::locale()->formatDateTime(
03775 date.toTimeSpec( spec ).dateTime(),
03776 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
03777 } else {
03778 return KGlobal::locale()->formatDateTime(
03779 date.dateTime(),
03780 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
03781 }
03782 }
03783
03784 QString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence )
03785 {
03786 #ifndef KDEPIM_NO_KRESOURCES
03787 if ( !calendar || !incidence ) {
03788 return QString();
03789 }
03790
03791 CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar );
03792 if ( !calendarResource ) {
03793 return QString();
03794 }
03795
03796 ResourceCalendar *resourceCalendar = calendarResource->resource( incidence );
03797 if ( resourceCalendar ) {
03798 if ( !resourceCalendar->subresources().isEmpty() ) {
03799 QString subRes = resourceCalendar->subresourceIdentifier( incidence );
03800 if ( subRes.isEmpty() ) {
03801 return resourceCalendar->resourceName();
03802 } else {
03803 return resourceCalendar->labelForSubresource( subRes );
03804 }
03805 }
03806 return resourceCalendar->resourceName();
03807 }
03808 #endif
03809 return QString();
03810 }
03811
03812 static QString secs2Duration( int secs )
03813 {
03814 QString tmp;
03815 int days = secs / 86400;
03816 if ( days > 0 ) {
03817 tmp += i18np( "1 day", "%1 days", days );
03818 tmp += ' ';
03819 secs -= ( days * 86400 );
03820 }
03821 int hours = secs / 3600;
03822 if ( hours > 0 ) {
03823 tmp += i18np( "1 hour", "%1 hours", hours );
03824 tmp += ' ';
03825 secs -= ( hours * 3600 );
03826 }
03827 int mins = secs / 60;
03828 if ( mins > 0 ) {
03829 tmp += i18np( "1 minute", "%1 minutes", mins );
03830 }
03831 return tmp;
03832 }
03833
03834 QString IncidenceFormatter::durationString( Incidence *incidence )
03835 {
03836 QString tmp;
03837 if ( incidence->type() == "Event" ) {
03838 Event *event = static_cast<Event *>( incidence );
03839 if ( event->hasEndDate() ) {
03840 if ( !event->allDay() ) {
03841 tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) );
03842 } else {
03843 tmp = i18np( "1 day", "%1 days",
03844 event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 );
03845 }
03846 } else {
03847 tmp = i18n( "forever" );
03848 }
03849 } else if ( incidence->type() == "Todo" ) {
03850 Todo *todo = static_cast<Todo *>( incidence );
03851 if ( todo->hasDueDate() ) {
03852 if ( todo->hasStartDate() ) {
03853 if ( !todo->allDay() ) {
03854 tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) );
03855 } else {
03856 tmp = i18np( "1 day", "%1 days",
03857 todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 );
03858 }
03859 }
03860 }
03861 }
03862 return tmp;
03863 }
03864
03865 QStringList IncidenceFormatter::reminderStringList( Incidence *incidence, bool shortfmt )
03866 {
03867
03868 Q_UNUSED( shortfmt );
03869
03870 QStringList reminderStringList;
03871
03872 if ( incidence ) {
03873 Alarm::List alarms = incidence->alarms();
03874 Alarm::List::ConstIterator it;
03875 for ( it = alarms.constBegin(); it != alarms.constEnd(); ++it ) {
03876 Alarm *alarm = *it;
03877 int offset = 0;
03878 QString remStr, atStr, offsetStr;
03879 if ( alarm->hasTime() ) {
03880 offset = 0;
03881 if ( alarm->time().isValid() ) {
03882 atStr = KGlobal::locale()->formatDateTime( alarm->time() );
03883 }
03884 } else if ( alarm->hasStartOffset() ) {
03885 offset = alarm->startOffset().asSeconds();
03886 if ( offset < 0 ) {
03887 offset = -offset;
03888 offsetStr = i18nc( "N days/hours/minutes before the start datetime",
03889 "%1 before the start", secs2Duration( offset ) );
03890 } else if ( offset > 0 ) {
03891 offsetStr = i18nc( "N days/hours/minutes after the start datetime",
03892 "%1 after the start", secs2Duration( offset ) );
03893 } else {
03894 if ( incidence->dtStart().isValid() ) {
03895 atStr = KGlobal::locale()->formatDateTime( incidence->dtStart() );
03896 }
03897 }
03898 } else if ( alarm->hasEndOffset() ) {
03899 offset = alarm->endOffset().asSeconds();
03900 if ( offset < 0 ) {
03901 offset = -offset;
03902 if ( incidence->type() == "Todo" ) {
03903 offsetStr = i18nc( "N days/hours/minutes before the due datetime",
03904 "%1 before the to-do is due", secs2Duration( offset ) );
03905 } else {
03906 offsetStr = i18nc( "N days/hours/minutes before the end datetime",
03907 "%1 before the end", secs2Duration( offset ) );
03908 }
03909 } else if ( offset > 0 ) {
03910 if ( incidence->type() == "Todo" ) {
03911 offsetStr = i18nc( "N days/hours/minutes after the due datetime",
03912 "%1 after the to-do is due", secs2Duration( offset ) );
03913 } else {
03914 offsetStr = i18nc( "N days/hours/minutes after the end datetime",
03915 "%1 after the end", secs2Duration( offset ) );
03916 }
03917 } else {
03918 if ( incidence->type() == "Todo" ) {
03919 Todo *t = static_cast<Todo *>( incidence );
03920 if ( t->dtDue().isValid() ) {
03921 atStr = KGlobal::locale()->formatDateTime( t->dtDue() );
03922 }
03923 } else {
03924 Event *e = static_cast<Event *>( incidence );
03925 if ( e->dtEnd().isValid() ) {
03926 atStr = KGlobal::locale()->formatDateTime( e->dtEnd() );
03927 }
03928 }
03929 }
03930 }
03931 if ( offset == 0 ) {
03932 if ( !atStr.isEmpty() ) {
03933 remStr = i18nc( "reminder occurs at datetime", "at %1", atStr );
03934 }
03935 } else {
03936 remStr = offsetStr;
03937 }
03938
03939 if ( alarm->repeatCount() > 0 ) {
03940 QString countStr = i18np( "repeats once", "repeats %1 times", alarm->repeatCount() );
03941 QString intervalStr = i18nc( "interval is N days/hours/minutes",
03942 "interval is %1",
03943 secs2Duration( alarm->snoozeTime().asSeconds() ) );
03944 QString repeatStr = i18nc( "(repeat string, interval string)",
03945 "(%1, %2)", countStr, intervalStr );
03946 remStr = remStr + ' ' + repeatStr;
03947
03948 }
03949 reminderStringList << remStr;
03950 }
03951 }
03952
03953 return reminderStringList;
03954 }