00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00035 #include "incidenceformatter.h"
00036 #include "attachment.h"
00037 #include "event.h"
00038 #include "todo.h"
00039 #include "journal.h"
00040 #include "calendar.h"
00041 #include "calendarlocal.h"
00042 #include "icalformat.h"
00043 #include "freebusy.h"
00044 #include "calendarresources.h"
00045
00046 #include "kpimutils/email.h"
00047 #include "kabc/phonenumber.h"
00048 #include "kabc/vcardconverter.h"
00049 #include "kabc/stdaddressbook.h"
00050
00051 #include <kdatetime.h>
00052 #include <kiconloader.h>
00053 #include <klocale.h>
00054 #include <kcalendarsystem.h>
00055 #include <ksystemtimezone.h>
00056
00057 #include <QtCore/QBuffer>
00058 #include <QtCore/QList>
00059 #include <QtGui/QTextDocument>
00060 #include <QtGui/QApplication>
00061
00062 #include <time.h>
00063
00064 using namespace KCal;
00065
00066
00067
00068
00069
00070
00071 static QString eventViewerAddLink( const QString &ref, const QString &text,
00072 bool newline = true )
00073 {
00074 QString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
00075 if ( newline ) {
00076 tmpStr += '\n';
00077 }
00078 return tmpStr;
00079 }
00080
00081 static QString eventViewerAddTag( const QString &tag, const QString &text )
00082 {
00083 int numLineBreaks = text.count( "\n" );
00084 QString str = '<' + tag + '>';
00085 QString tmpText = text;
00086 QString tmpStr = str;
00087 if( numLineBreaks >= 0 ) {
00088 if ( numLineBreaks > 0 ) {
00089 int pos = 0;
00090 QString tmp;
00091 for ( int i = 0; i <= numLineBreaks; ++i ) {
00092 pos = tmpText.indexOf( "\n" );
00093 tmp = tmpText.left( pos );
00094 tmpText = tmpText.right( tmpText.length() - pos - 1 );
00095 tmpStr += tmp + "<br>";
00096 }
00097 } else {
00098 tmpStr += tmpText;
00099 }
00100 }
00101 tmpStr += "</" + tag + '>';
00102 return tmpStr;
00103 }
00104
00105 static QString eventViewerFormatCategories( Incidence *event )
00106 {
00107 QString tmpStr;
00108 if ( !event->categoriesStr().isEmpty() ) {
00109 if ( event->categories().count() == 1 ) {
00110 tmpStr = eventViewerAddTag( "h3", i18n( "Category" ) );
00111 } else {
00112 tmpStr = eventViewerAddTag( "h3", i18n( "Categories" ) );
00113 }
00114 tmpStr += eventViewerAddTag( "p", event->categoriesStr() );
00115 }
00116 return tmpStr;
00117 }
00118
00119 static QString linkPerson( const QString &email, QString name, QString uid,
00120 const QString &iconPath )
00121 {
00122
00123
00124 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00125 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00126 KABC::Addressee::List addressList = add_book->findByEmail( email );
00127 KABC::Addressee o = ( !addressList.isEmpty() ? addressList.first() : KABC::Addressee() );
00128 if ( !o.isEmpty() && addressList.size() < 2 ) {
00129 if ( name.isEmpty() ) {
00130
00131 name = o.formattedName();
00132 }
00133 uid = o.uid();
00134 } else {
00135
00136 uid.clear();
00137 }
00138 }
00139
00140
00141 QString tmpString = "<li>";
00142 if ( !uid.isEmpty() ) {
00143
00144 if ( name.isEmpty() ) {
00145
00146 tmpString += eventViewerAddLink( "uid:" + uid, email );
00147 } else {
00148 tmpString += eventViewerAddLink( "uid:" + uid, name );
00149 }
00150 } else {
00151
00152 tmpString += ( name.isEmpty() ? email : name );
00153 }
00154 tmpString += '\n';
00155
00156
00157 if ( !email.isEmpty() && !iconPath.isNull() ) {
00158 KUrl mailto;
00159 mailto.setProtocol( "mailto" );
00160 mailto.setPath( email );
00161 tmpString += eventViewerAddLink( mailto.url(), "<img src=\"" + iconPath + "\">" );
00162 }
00163 tmpString += "</li>\n";
00164
00165 return tmpString;
00166 }
00167
00168 static QString eventViewerFormatAttendees( Incidence *event )
00169 {
00170 QString tmpStr;
00171 Attendee::List attendees = event->attendees();
00172 if ( attendees.count() ) {
00173 KIconLoader *iconLoader = KIconLoader::global();
00174 const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
00175
00176
00177 tmpStr += eventViewerAddTag( "h4", i18n( "Organizer" ) );
00178 tmpStr += "<ul>";
00179 tmpStr += linkPerson( event->organizer().email(), event->organizer().name(),
00180 QString(), iconPath );
00181 tmpStr += "</ul>";
00182
00183
00184 tmpStr += eventViewerAddTag( "h4", i18n( "Attendees" ) );
00185 tmpStr += "<ul>";
00186 Attendee::List::ConstIterator it;
00187 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
00188 Attendee *a = *it;
00189 tmpStr += linkPerson( a->email(), a->name(), a->uid(), iconPath );
00190 if ( !a->delegator().isEmpty() ) {
00191 tmpStr += i18n( " (delegated by %1)", a->delegator() );
00192 }
00193 if ( !a->delegate().isEmpty() ) {
00194 tmpStr += i18n( " (delegated to %1)", a->delegate() );
00195 }
00196 }
00197 tmpStr += "</ul>";
00198 }
00199 return tmpStr;
00200 }
00201
00202 static QString eventViewerFormatAttachments( Incidence *i )
00203 {
00204 QString tmpStr;
00205 Attachment::List as = i->attachments();
00206 if ( as.count() > 0 ) {
00207 Attachment::List::ConstIterator it;
00208 for ( it = as.constBegin(); it != as.constEnd(); ++it ) {
00209 if ( (*it)->isUri() ) {
00210 tmpStr += eventViewerAddLink( (*it)->uri(), (*it)->label() );
00211 tmpStr += "<br>";
00212 }
00213 }
00214 }
00215 return tmpStr;
00216 }
00217
00218
00219
00220
00221
00222 static QString eventViewerFormatBirthday( Event *event )
00223 {
00224 if ( !event ) {
00225 return QString();
00226 }
00227 if ( event->customProperty( "KABC", "BIRTHDAY" ) != "YES" ) {
00228 return QString();
00229 }
00230
00231 QString uid_1 = event->customProperty( "KABC", "UID-1" );
00232 QString name_1 = event->customProperty( "KABC", "NAME-1" );
00233 QString email_1= event->customProperty( "KABC", "EMAIL-1" );
00234
00235 KIconLoader *iconLoader = KIconLoader::global();
00236 const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
00237
00238 QString tmpString = "<ul>";
00239 tmpString += linkPerson( email_1, name_1, uid_1, iconPath );
00240
00241 if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00242 QString uid_2 = event->customProperty( "KABC", "UID-2" );
00243 QString name_2 = event->customProperty( "KABC", "NAME-2" );
00244 QString email_2= event->customProperty( "KABC", "EMAIL-2" );
00245 tmpString += linkPerson( email_2, name_2, uid_2, iconPath );
00246 }
00247
00248 tmpString += "</ul>";
00249 return tmpString;
00250 }
00251
00252 static QString eventViewerFormatHeader( Incidence *incidence )
00253 {
00254 QString tmpStr = "<table><tr>";
00255
00256
00257 KIconLoader *iconLoader = KIconLoader::global();
00258 tmpStr += "<td>";
00259
00260
00261
00262
00263 if ( incidence->type() == "Todo" ) {
00264 tmpStr += "<img src=\"";
00265 Todo *todo = static_cast<Todo *>( incidence );
00266 if ( !todo->isCompleted() ) {
00267 tmpStr += iconLoader->iconPath( "view-calendar-tasks", KIconLoader::Small );
00268 } else {
00269 tmpStr += iconLoader->iconPath( "task-complete", KIconLoader::Small );
00270 }
00271 tmpStr += "\">";
00272 }
00273
00274 if ( incidence->type() == "Event" ) {
00275 tmpStr += "<img src=\"" +
00276 iconLoader->iconPath( "view-calendar-day", KIconLoader::Small ) +
00277 "\">";
00278 }
00279
00280 if ( incidence->type() == "Journal" ) {
00281 tmpStr += "<img src=\"" +
00282 iconLoader->iconPath( "view-pim-journal", KIconLoader::Small ) +
00283 "\">";
00284 }
00285
00286 if ( incidence->isAlarmEnabled() ) {
00287 tmpStr += "<img src=\"" +
00288 iconLoader->iconPath( "preferences-desktop-notification-bell", KIconLoader::Small ) +
00289 "\">";
00290 }
00291 if ( incidence->recurs() ) {
00292 tmpStr += "<img src=\"" +
00293 iconLoader->iconPath( "edit-redo", KIconLoader::Small ) +
00294 "\">";
00295 }
00296 if ( incidence->isReadOnly() ) {
00297 tmpStr += "<img src=\"" +
00298 iconLoader->iconPath( "object-locked", KIconLoader::Small ) +
00299 "\">";
00300 }
00301 tmpStr += "</td>";
00302
00303 tmpStr += "<td>" +
00304 eventViewerAddTag( "h2", incidence->richSummary() ) +
00305 "</td>";
00306 tmpStr += "</tr></table>";
00307
00308 return tmpStr;
00309 }
00310
00311 static QString eventViewerFormatEvent( Event *event, KDateTime::Spec spec )
00312 {
00313 if ( !event ) {
00314 return QString();
00315 }
00316
00317 QString tmpStr = eventViewerFormatHeader( event );
00318
00319 tmpStr += "<table>";
00320 if ( !event->location().isEmpty() ) {
00321 tmpStr += "<tr>";
00322 tmpStr += "<td align=\"right\"><b>" + i18n( "Location" ) + "</b></td>";
00323 tmpStr += "<td>" + event->richLocation() + "</td>";
00324 tmpStr += "</tr>";
00325 }
00326
00327 tmpStr += "<tr>";
00328 if ( event->allDay() ) {
00329 if ( event->isMultiDay() ) {
00330 tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00331 tmpStr += "<td>" +
00332 i18nc( "<beginTime> - <endTime>","%1 - %2",
00333 IncidenceFormatter::dateToString( event->dtStart(), true, spec ),
00334 IncidenceFormatter::dateToString( event->dtEnd(), true, spec ) ) +
00335 "</td>";
00336 } else {
00337 tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
00338 tmpStr += "<td>" +
00339 i18nc( "date as string","%1",
00340 IncidenceFormatter::dateToString( event->dtStart(), true, spec ) ) +
00341 "</td>";
00342 }
00343 } else {
00344 if ( event->isMultiDay() ) {
00345 tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00346 tmpStr += "<td>" +
00347 i18nc( "<beginTime> - <endTime>","%1 - %2",
00348 IncidenceFormatter::dateToString( event->dtStart(), true, spec ),
00349 IncidenceFormatter::dateToString( event->dtEnd(), true, spec ) ) +
00350 "</td>";
00351 } else {
00352 tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00353 if ( event->hasEndDate() && event->dtStart() != event->dtEnd() ) {
00354 tmpStr += "<td>" +
00355 i18nc( "<beginTime> - <endTime>","%1 - %2",
00356 IncidenceFormatter::timeToString( event->dtStart(), true, spec ),
00357 IncidenceFormatter::timeToString( event->dtEnd(), true, spec ) ) +
00358 "</td>";
00359 } else {
00360 tmpStr += "<td>" +
00361 IncidenceFormatter::timeToString( event->dtStart(), true, spec ) +
00362 "</td>";
00363 }
00364 tmpStr += "</tr><tr>";
00365 tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
00366 tmpStr += "<td>" +
00367 i18nc( "date as string","%1",
00368 IncidenceFormatter::dateToString( event->dtStart(), true, spec ) ) +
00369 "</td>";
00370 }
00371 }
00372 tmpStr += "</tr>";
00373
00374 if ( event->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00375 tmpStr += "<tr>";
00376 tmpStr += "<td align=\"right\"><b>" + i18n( "Birthday" ) + "</b></td>";
00377 tmpStr += "<td>" + eventViewerFormatBirthday( event ) + "</td>";
00378 tmpStr += "</tr>";
00379 tmpStr += "</table>";
00380 return tmpStr;
00381 }
00382
00383 if ( !event->description().isEmpty() ) {
00384 tmpStr += "<tr>";
00385 tmpStr += "<td></td>";
00386 tmpStr += "<td>" + eventViewerAddTag( "p", event->richDescription() ) + "</td>";
00387 tmpStr += "</tr>";
00388 }
00389
00390 if ( event->categories().count() > 0 ) {
00391 tmpStr += "<tr>";
00392 tmpStr += "<td align=\"right\"><b>";
00393 tmpStr += i18np( "1 category", "%1 categories", event->categories().count() ) +
00394 "</b></td>";
00395 tmpStr += "<td>" + event->categoriesStr() + "</td>";
00396 tmpStr += "</tr>";
00397 }
00398
00399 if ( event->recurs() ) {
00400 KDateTime dt = event->recurrence()->getNextDateTime( KDateTime::currentUtcDateTime() );
00401 tmpStr += "<tr>";
00402 tmpStr += "<td align=\"right\"><b>" + i18n( "Next Occurrence" )+ "</b></td>";
00403 tmpStr += "<td>" +
00404 ( dt.isValid() ?
00405 KGlobal::locale()->formatDateTime( dt.dateTime(), KLocale::ShortDate ) :
00406 i18nc( "no date", "none" ) ) +
00407 "</td>";
00408 tmpStr += "</tr>";
00409 }
00410
00411 tmpStr += "<tr><td colspan=\"2\">";
00412 tmpStr += eventViewerFormatAttendees( event );
00413 tmpStr += "</td></tr>";
00414
00415 int attachmentCount = event->attachments().count();
00416 if ( attachmentCount > 0 ) {
00417 tmpStr += "<tr>";
00418 tmpStr += "<td align=\"right\"><b>";
00419 tmpStr += i18np( "1 attachment", "%1 attachments", attachmentCount )+ "</b></td>";
00420 tmpStr += "<td>" + eventViewerFormatAttachments( event ) + "</td>";
00421 tmpStr += "</tr>";
00422 }
00423 KDateTime kdt = event->created().toTimeSpec( spec );
00424 tmpStr += "</table>";
00425 tmpStr += "<p><em>" +
00426 i18n( "Creation date: %1", KGlobal::locale()->formatDateTime(
00427 kdt.dateTime(),
00428 KLocale::ShortDate ) ) + "</em>";
00429 return tmpStr;
00430 }
00431
00432 static QString eventViewerFormatTodo( Todo *todo, KDateTime::Spec spec )
00433 {
00434 if ( !todo ) {
00435 return QString();
00436 }
00437
00438 QString tmpStr = eventViewerFormatHeader( todo );
00439
00440 if ( !todo->location().isEmpty() ) {
00441 tmpStr += eventViewerAddTag( "b", i18n(" Location: %1", todo->richLocation() ) );
00442 tmpStr += "<br>";
00443 }
00444
00445 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
00446 tmpStr += i18n( "<b>Due on:</b> %1",
00447 IncidenceFormatter::dateTimeToString( todo->dtDue(),
00448 todo->allDay(),
00449 true, spec ) );
00450 }
00451
00452 if ( !todo->description().isEmpty() ) {
00453 tmpStr += eventViewerAddTag( "p", todo->richDescription() );
00454 }
00455
00456 tmpStr += eventViewerFormatCategories( todo );
00457
00458 if ( todo->priority() > 0 ) {
00459 tmpStr += i18n( "<p><b>Priority:</b> %1</p>", todo->priority() );
00460 } else {
00461 tmpStr += i18n( "<p><b>Priority:</b> %1</p>", i18n( "Unspecified" ) );
00462 }
00463
00464 tmpStr += i18n( "<p><i>%1 % completed</i></p>", todo->percentComplete() );
00465
00466 if ( todo->recurs() ) {
00467 KDateTime dt = todo->recurrence()->getNextDateTime( KDateTime::currentUtcDateTime() );
00468 tmpStr += eventViewerAddTag( "p", "<em>" +
00469 i18n( "This is a recurring to-do. The next occurrence will be on %1.",
00470 KGlobal::locale()->formatDateTime( dt.dateTime(), KLocale::ShortDate ) ) + "</em>" );
00471 }
00472 tmpStr += eventViewerFormatAttendees( todo );
00473 tmpStr += eventViewerFormatAttachments( todo );
00474
00475 KDateTime kdt = todo->created().toTimeSpec( spec );
00476 tmpStr += "<p><em>" + i18n( "Creation date: %1",
00477 KGlobal::locale()->formatDateTime( kdt.dateTime(), KLocale::ShortDate ) ) + "</em>";
00478 return tmpStr;
00479 }
00480
00481 static QString eventViewerFormatJournal( Journal *journal, KDateTime::Spec spec )
00482 {
00483 if ( !journal ) {
00484 return QString();
00485 }
00486
00487 QString tmpStr = eventViewerFormatHeader( journal );
00488
00489 tmpStr += eventViewerAddTag(
00490 "h3", i18n( "Journal for %1", IncidenceFormatter::dateToString( journal->dtStart(), false,
00491 spec ) ) );
00492 if ( !journal->description().isEmpty() ) {
00493 tmpStr += eventViewerAddTag( "p", journal->richDescription() );
00494 }
00495 return tmpStr;
00496 }
00497
00498 static QString eventViewerFormatFreeBusy( FreeBusy *fb, KDateTime::Spec spec )
00499 {
00500 Q_UNUSED( spec );
00501
00502 if ( !fb ) {
00503 return QString();
00504 }
00505
00506 QString tmpStr(
00507 eventViewerAddTag(
00508 "h2", i18n( "Free/Busy information for %1", fb->organizer().fullName() ) ) );
00509 tmpStr += eventViewerAddTag(
00510 "h4", i18n( "Busy times in date range %1 - %2:",
00511 KGlobal::locale()->formatDate( fb->dtStart().date(), KLocale::ShortDate ),
00512 KGlobal::locale()->formatDate( fb->dtEnd().date(), KLocale::ShortDate ) ) );
00513
00514 QList<Period> periods = fb->busyPeriods();
00515
00516 QString text =
00517 eventViewerAddTag( "em",
00518 eventViewerAddTag( "b", i18nc( "tag for busy periods list", "Busy:" ) ) );
00519
00520 QList<Period>::iterator it;
00521 for ( it = periods.begin(); it != periods.end(); ++it ) {
00522 Period per = *it;
00523 if ( per.hasDuration() ) {
00524 int dur = per.duration().asSeconds();
00525 QString cont;
00526 if ( dur >= 3600 ) {
00527 cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
00528 dur %= 3600;
00529 }
00530 if ( dur >= 60 ) {
00531 cont += i18ncp( "minutes part duration", "1 minute ", "%1 minutes ", dur / 60 );
00532 dur %= 60;
00533 }
00534 if ( dur > 0 ) {
00535 cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
00536 }
00537 text += i18nc( "startDate for duration", "%1 for %2",
00538 KGlobal::locale()->formatDateTime(
00539 per.start().dateTime(), KLocale::LongDate ), cont );
00540 text += "<br>";
00541 } else {
00542 if ( per.start().date() == per.end().date() ) {
00543 text += i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
00544 KGlobal::locale()->formatDate( per.start().date() ),
00545 KGlobal::locale()->formatTime( per.start().time() ),
00546 KGlobal::locale()->formatTime( per.end().time() ) );
00547 } else {
00548 text += i18nc( "fromDateTime - toDateTime", "%1 - %2",
00549 KGlobal::locale()->formatDateTime(
00550 per.start().dateTime(), KLocale::LongDate ),
00551 KGlobal::locale()->formatDateTime(
00552 per.end().dateTime(), KLocale::LongDate ) );
00553 }
00554 text += "<br>";
00555 }
00556 }
00557 tmpStr += eventViewerAddTag( "p", text );
00558 return tmpStr;
00559 }
00560
00561
00562
00563 class KCal::IncidenceFormatter::EventViewerVisitor
00564 : public IncidenceBase::Visitor
00565 {
00566 public:
00567 EventViewerVisitor()
00568 : mSpec( KDateTime::Spec() ), mResult( "" ) {}
00569
00570 bool act( IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec() )
00571 {
00572 mSpec = spec;
00573 mResult = "";
00574 return incidence->accept( *this );
00575 }
00576 QString result() const { return mResult; }
00577
00578 protected:
00579 bool visit( Event *event )
00580 {
00581 mResult = eventViewerFormatEvent( event, mSpec );
00582 return !mResult.isEmpty();
00583 }
00584 bool visit( Todo *todo )
00585 {
00586 mResult = eventViewerFormatTodo( todo, mSpec );
00587 return !mResult.isEmpty();
00588 }
00589 bool visit( Journal *journal )
00590 {
00591 mResult = eventViewerFormatJournal( journal, mSpec );
00592 return !mResult.isEmpty();
00593 }
00594 bool visit( FreeBusy *fb )
00595 {
00596 mResult = eventViewerFormatFreeBusy( fb, mSpec );
00597 return !mResult.isEmpty();
00598 }
00599
00600 protected:
00601 KDateTime::Spec mSpec;
00602 QString mResult;
00603 };
00604
00605
00606 QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
00607 {
00608 return extensiveDisplayStr( incidence, KDateTime::Spec() );
00609 }
00610
00611 QString IncidenceFormatter::extensiveDisplayStr( IncidenceBase *incidence, KDateTime::Spec spec )
00612 {
00613 if ( !incidence ) {
00614 return QString();
00615 }
00616
00617 EventViewerVisitor v;
00618 if ( v.act( incidence, spec ) ) {
00619 return v.result();
00620 } else {
00621 return QString();
00622 }
00623 }
00624
00625
00626
00627
00628
00629
00630
00631
00632 static QString string2HTML( const QString &str )
00633 {
00634 return Qt::escape( str );
00635 }
00636
00637 static QString cleanHtml( const QString &html )
00638 {
00639 QRegExp rx( "<body[^>]*>(.*)</body>", Qt::CaseInsensitive );
00640 rx.indexIn( html );
00641 QString body = rx.cap( 1 );
00642
00643 return Qt::escape( body.remove( QRegExp( "<[^>]*>" ) ).trimmed() );
00644 }
00645
00646 static QString eventStartTimeStr( Event *event )
00647 {
00648 QString tmp;
00649 if ( !event->allDay() ) {
00650 tmp = i18nc( "%1: Start Date, %2: Start Time", "%1 %2",
00651 IncidenceFormatter::dateToString( event->dtStart(), true,
00652 KSystemTimeZones::local() ),
00653 IncidenceFormatter::timeToString( event->dtStart(), true,
00654 KSystemTimeZones::local() ) );
00655 } else {
00656 tmp = i18nc( "%1: Start Date", "%1 (all day)",
00657 IncidenceFormatter::dateToString( event->dtStart(), true,
00658 KSystemTimeZones::local() ) );
00659 }
00660 return tmp;
00661 }
00662
00663 static QString eventEndTimeStr( Event *event )
00664 {
00665 QString tmp;
00666 if ( event->hasEndDate() && event->dtEnd().isValid() ) {
00667 if ( !event->allDay() ) {
00668 tmp = i18nc( "%1: End Date, %2: End Time", "%1 %2",
00669 IncidenceFormatter::dateToString( event->dtEnd(), true,
00670 KSystemTimeZones::local() ),
00671 IncidenceFormatter::timeToString( event->dtEnd(), true,
00672 KSystemTimeZones::local() ) );
00673 } else {
00674 tmp = i18nc( "%1: End Date", "%1 (all day)",
00675 IncidenceFormatter::dateToString( event->dtEnd(), true,
00676 KSystemTimeZones::local() ) );
00677 }
00678 } else {
00679 tmp = i18n( "Unspecified" );
00680 }
00681 return tmp;
00682 }
00683
00684 static QString invitationRow( const QString &cell1, const QString &cell2 )
00685 {
00686 return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
00687 }
00688
00689 static QString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode )
00690 {
00691
00692
00693
00694
00695 QString html;
00696 QString descr;
00697 QStringList comments;
00698
00699 if ( incidence->comments().isEmpty() ) {
00700 if ( !incidence->description().isEmpty() ) {
00701
00702 if ( !incidence->descriptionIsRich() ) {
00703 comments << string2HTML( incidence->description() );
00704 } else {
00705 comments << incidence->richDescription();
00706 if ( noHtmlMode ) {
00707 comments[0] = cleanHtml( comments[0] );
00708 }
00709 }
00710 }
00711
00712 } else {
00713
00714 for ( int i=0; i < incidence->comments().count(); ++i ) {
00715 comments[i] = string2HTML( incidence->comments()[i] );
00716 }
00717 if ( !incidence->description().isEmpty() ) {
00718
00719 if ( !incidence->descriptionIsRich() ) {
00720 descr = string2HTML( incidence->description() );
00721 } else {
00722 descr = incidence->richDescription();
00723 if ( noHtmlMode ) {
00724 descr = cleanHtml( descr );
00725 }
00726 }
00727 }
00728 }
00729
00730 if( !descr.isEmpty() ) {
00731 html += "<p>";
00732 html += "<table border=\"0\" style=\"margin-top:4px;\">";
00733 html += "<tr><td><center>" +
00734 eventViewerAddTag( "u", i18n( "Description:" ) ) +
00735 "</center></td></tr>";
00736 html += "<tr><td>" + descr + "</td></tr>";
00737 html += "</table>";
00738 }
00739
00740 if ( !comments.isEmpty() ) {
00741 html += "<p>";
00742 html += "<table border=\"0\" style=\"margin-top:4px;\">";
00743 html += "<tr><td><center>" +
00744 eventViewerAddTag( "u", i18n( "Comments:" ) ) +
00745 "</center></td></tr>";
00746 html += "<tr>";
00747 if ( comments.count() > 1 ) {
00748 html += "<td><ul>";
00749 for ( int i=0; i < comments.count(); ++i ) {
00750 html += "<li>" + comments[i] + "</li>";
00751 }
00752 html += "</ul></td>";
00753 } else {
00754 html += "<td>" + comments[0] + "</td>";
00755 }
00756 html += "</tr>";
00757 html += "</table>";
00758 }
00759 return html;
00760 }
00761
00762 static QString invitationDetailsEvent( Event *event, bool noHtmlMode )
00763 {
00764
00765 if ( !event ) {
00766 return QString();
00767 }
00768
00769 QString sSummary = i18n( "Summary unspecified" );
00770 if ( !event->summary().isEmpty() ) {
00771 if ( !event->summaryIsRich() ) {
00772 sSummary = string2HTML( event->summary() );
00773 } else {
00774 sSummary = event->richSummary();
00775 if ( noHtmlMode ) {
00776 sSummary = cleanHtml( sSummary );
00777 }
00778 }
00779 }
00780
00781 QString sLocation = i18n( "Location unspecified" );
00782 if ( !event->location().isEmpty() ) {
00783 if ( !event->locationIsRich() ) {
00784 sLocation = string2HTML( event->location() );
00785 } else {
00786 sLocation = event->richLocation();
00787 if ( noHtmlMode ) {
00788 sLocation = cleanHtml( sLocation );
00789 }
00790 }
00791 }
00792
00793 QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" );
00794 QString html = QString( "<div dir=\"%1\">\n" ).arg( dir );
00795
00796
00797 html += invitationRow( i18n( "What:" ), sSummary );
00798 html += invitationRow( i18n( "Where:" ), sLocation );
00799
00800
00801 html += invitationRow( i18n( "Start Time:" ), eventStartTimeStr( event ) );
00802
00803
00804 html += invitationRow( i18n( "End Time:" ), eventEndTimeStr( event ) );
00805
00806
00807 if ( !event->allDay() && event->hasEndDate() && event->dtEnd().isValid() ) {
00808 QString tmp;
00809 QTime sDuration( 0, 0, 0 ), t;
00810 int secs = event->dtStart().secsTo( event->dtEnd() );
00811 t = sDuration.addSecs( secs );
00812 if ( t.hour() > 0 ) {
00813 tmp += i18np( "1 hour ", "%1 hours ", t.hour() );
00814 }
00815 if ( t.minute() > 0 ) {
00816 tmp += i18np( "1 minute ", "%1 minutes ", t.minute() );
00817 }
00818
00819 html += invitationRow( i18n( "Duration:" ), tmp );
00820 }
00821
00822 if ( event->recurs() ) {
00823 html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) );
00824 }
00825
00826 html += "</div>";
00827 html += invitationsDetailsIncidence( event, noHtmlMode );
00828
00829 return html;
00830 }
00831
00832 static QString invitationDetailsTodo( Todo *todo, bool noHtmlMode )
00833 {
00834
00835 if ( !todo ) {
00836 return QString();
00837 }
00838
00839 QString sSummary = i18n( "Summary unspecified" );
00840 QString sDescr = i18n( "Description unspecified" );
00841 if ( ! todo->summary().isEmpty() ) {
00842 sSummary = todo->richSummary();
00843 if ( noHtmlMode ) {
00844 sSummary = cleanHtml( sSummary );
00845 }
00846 }
00847 if ( ! todo->description().isEmpty() ) {
00848 sDescr = todo->description();
00849 if ( noHtmlMode ) {
00850 sDescr = cleanHtml( sDescr );
00851 }
00852 }
00853 QString html = "<table border=\"0\" width=\"80%\" align=\"center\" "
00854 "cellpadding=\"1\" cellspacing=\"1\">";
00855 html += invitationRow( i18n( "Summary:" ), sSummary );
00856 html += invitationRow( i18n( "Description:" ), sDescr );
00857 html += "</table>\n";
00858 html += invitationsDetailsIncidence( todo, noHtmlMode );
00859
00860 return html;
00861 }
00862
00863 static QString invitationDetailsJournal( Journal *journal, bool noHtmlMode )
00864 {
00865 if ( !journal ) {
00866 return QString();
00867 }
00868
00869 QString sSummary = i18n( "Summary unspecified" );
00870 QString sDescr = i18n( "Description unspecified" );
00871 if ( ! journal->summary().isEmpty() ) {
00872 sSummary = journal->richSummary();
00873 if ( noHtmlMode ) {
00874 sSummary = cleanHtml( sSummary );
00875 }
00876 }
00877 if ( ! journal->description().isEmpty() ) {
00878 sDescr = journal->richDescription();
00879 if ( noHtmlMode ) {
00880 sDescr = cleanHtml( sDescr );
00881 }
00882 }
00883 QString html = "<table border=\"0\" width=\"80%\" align=\"center\" "
00884 "cellpadding=\"1\" cellspacing=\"1\">";
00885 html += invitationRow( i18n( "Summary:" ), sSummary );
00886 html += invitationRow( i18n( "Date:" ),
00887 IncidenceFormatter::dateToString( journal->dtStart(), false,
00888 journal->dtStart().timeSpec() ) );
00889 html += invitationRow( i18n( "Description:" ), sDescr );
00890 html += "</table>\n";
00891 html += invitationsDetailsIncidence( journal, noHtmlMode );
00892
00893 return html;
00894 }
00895
00896 static QString invitationDetailsFreeBusy( FreeBusy *fb )
00897 {
00898 if ( !fb ) {
00899 return QString();
00900 }
00901
00902 QString html = "<table border=\"0\" width=\"80%\" align=\"center\" "
00903 "cellpadding=\"1\" cellspacing=\"1\">";
00904 html += invitationRow( i18n( "Person:" ), fb->organizer().fullName() );
00905 html += invitationRow( i18n( "Start date:" ),
00906 IncidenceFormatter::dateToString( fb->dtStart(),
00907 true,
00908 fb->dtStart().timeSpec() ) );
00909 html += invitationRow( i18n( "End date:" ),
00910 KGlobal::locale()->formatDate( fb->dtEnd().date(), KLocale::ShortDate ) );
00911 html += "<tr><td colspan=2><hr></td></tr>\n";
00912 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
00913
00914 QList<Period> periods = fb->busyPeriods();
00915 QList<Period>::iterator it;
00916 for ( it = periods.begin(); it != periods.end(); ++it ) {
00917 Period per = *it;
00918 if ( per.hasDuration() ) {
00919 int dur = per.duration().asSeconds();
00920 QString cont;
00921 if ( dur >= 3600 ) {
00922 cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
00923 dur %= 3600;
00924 }
00925 if ( dur >= 60 ) {
00926 cont += i18ncp( "minutes part of duration", "1 minute", "%1 minutes ", dur / 60 );
00927 dur %= 60;
00928 }
00929 if ( dur > 0 ) {
00930 cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
00931 }
00932 html += invitationRow(
00933 QString(), i18nc( "startDate for duration", "%1 for %2",
00934 KGlobal::locale()->formatDateTime(
00935 per.start().dateTime(), KLocale::LongDate ), cont ) );
00936 } else {
00937 QString cont;
00938 if ( per.start().date() == per.end().date() ) {
00939 cont = i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
00940 KGlobal::locale()->formatDate( per.start().date() ),
00941 KGlobal::locale()->formatTime( per.start().time() ),
00942 KGlobal::locale()->formatTime( per.end().time() ) );
00943 } else {
00944 cont = i18nc( "fromDateTime - toDateTime", "%1 - %2",
00945 KGlobal::locale()->formatDateTime(
00946 per.start().dateTime(), KLocale::LongDate ),
00947 KGlobal::locale()->formatDateTime(
00948 per.end().dateTime(), KLocale::LongDate ) );
00949 }
00950
00951 html += invitationRow( QString(), cont );
00952 }
00953 }
00954
00955 html += "</table>\n";
00956 return html;
00957 }
00958
00959 static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
00960 {
00961 if ( !msg || !event ) {
00962 return QString();
00963 }
00964
00965 switch ( msg->method() ) {
00966 case iTIPPublish:
00967 return i18n( "This event has been published" );
00968 case iTIPRequest:
00969 if ( event->revision() > 0 ) {
00970
00971 return i18n( "<h3>This meeting has been updated</h3>" );
00972 } else {
00973 return i18n( "You have been invited to this meeting" );
00974 }
00975 case iTIPRefresh:
00976 return i18n( "This invitation was refreshed" );
00977 case iTIPCancel:
00978 return i18n( "This meeting has been canceled" );
00979 case iTIPAdd:
00980 return i18n( "Addition to the meeting invitation" );
00981 case iTIPReply:
00982 {
00983 Attendee::List attendees = event->attendees();
00984 if( attendees.count() == 0 ) {
00985 kDebug() << "No attendees in the iCal reply!";
00986 return QString();
00987 }
00988 if ( attendees.count() != 1 ) {
00989 kDebug() << "Warning: attendeecount in the reply should be 1"
00990 << "but is" << attendees.count();
00991 }
00992 Attendee *attendee = *attendees.begin();
00993 QString attendeeName = attendee->name();
00994 if ( attendeeName.isEmpty() ) {
00995 attendeeName = attendee->email();
00996 }
00997 if ( attendeeName.isEmpty() ) {
00998 attendeeName = i18n( "Sender" );
00999 }
01000
01001 QString delegatorName, dummy;
01002 KPIMUtils::extractEmailAddressAndName( attendee->delegator(), dummy, delegatorName );
01003 if ( delegatorName.isEmpty() ) {
01004 delegatorName = attendee->delegator();
01005 }
01006
01007 switch( attendee->status() ) {
01008 case Attendee::NeedsAction:
01009 return i18n( "%1 indicates this invitation still needs some action", attendeeName );
01010 case Attendee::Accepted:
01011 if ( delegatorName.isEmpty() ) {
01012 return i18n( "%1 accepts this meeting invitation", attendeeName );
01013 }
01014 return i18n( "%1 accepts this meeting invitation on behalf of %2",
01015 attendeeName, delegatorName );
01016 case Attendee::Tentative:
01017 if ( delegatorName.isEmpty() ) {
01018 return i18n( "%1 tentatively accepts this meeting invitation", attendeeName );
01019 }
01020 return i18n( "%1 tentatively accepts this meeting invitation on behalf of %2",
01021 attendeeName, delegatorName );
01022 case Attendee::Declined:
01023 if ( delegatorName.isEmpty() ) {
01024 return i18n( "%1 declines this meeting invitation", attendeeName );
01025 }
01026 return i18n( "%1 declines this meeting invitation on behalf of %2",
01027 attendeeName, delegatorName );
01028 case Attendee::Delegated:
01029 {
01030 QString delegate, dummy;
01031 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
01032 if ( delegate.isEmpty() ) {
01033 delegate = attendee->delegate();
01034 }
01035 if ( !delegate.isEmpty() ) {
01036 return i18n( "%1 has delegated this meeting invitation to %2", attendeeName, delegate );
01037 }
01038 return i18n( "%1 has delegated this meeting invitation", attendeeName );
01039 }
01040 case Attendee::Completed:
01041 return i18n( "This meeting invitation is now completed" );
01042 case Attendee::InProcess:
01043 return i18n( "%1 is still processing the invitation", attendeeName );
01044 default:
01045 return i18n( "Unknown response to this meeting invitation" );
01046 }
01047 break;
01048 }
01049 case iTIPCounter:
01050 return i18n( "Sender makes this counter proposal" );
01051 case iTIPDeclineCounter:
01052 return i18n( "Sender declines the counter proposal" );
01053 case iTIPNoMethod:
01054 return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() );
01055 }
01056 return QString();
01057 }
01058
01059 static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
01060 {
01061 if ( !msg || !todo ) {
01062 return QString();
01063 }
01064
01065 switch ( msg->method() ) {
01066 case iTIPPublish:
01067 return i18n( "This to-do has been published" );
01068 case iTIPRequest:
01069 if ( todo->revision() > 0 ) {
01070 return i18n( "This to-do has been updated" );
01071 } else {
01072 return i18n( "You have been assigned this to-do" );
01073 }
01074 case iTIPRefresh:
01075 return i18n( "This to-do was refreshed" );
01076 case iTIPCancel:
01077 return i18n( "This to-do was canceled" );
01078 case iTIPAdd:
01079 return i18n( "Addition to the to-do" );
01080 case iTIPReply:
01081 {
01082 Attendee::List attendees = todo->attendees();
01083 if ( attendees.count() == 0 ) {
01084 kDebug() << "No attendees in the iCal reply!";
01085 return QString();
01086 }
01087 if ( attendees.count() != 1 ) {
01088 kDebug() << "Warning: attendeecount in the reply should be 1"
01089 << "but is" << attendees.count();
01090 }
01091 Attendee *attendee = *attendees.begin();
01092 switch( attendee->status() ) {
01093 case Attendee::NeedsAction:
01094 return i18n( "Sender indicates this to-do assignment still needs some action" );
01095 case Attendee::Accepted:
01096 return i18n( "Sender accepts this to-do" );
01097 case Attendee::Tentative:
01098 return i18n( "Sender tentatively accepts this to-do" );
01099 case Attendee::Declined:
01100 return i18n( "Sender declines this to-do" );
01101 case Attendee::Delegated:
01102 {
01103 QString delegate, dummy;
01104 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
01105 if ( delegate.isEmpty() ) {
01106 delegate = attendee->delegate();
01107 }
01108 if ( !delegate.isEmpty() ) {
01109 return i18n( "Sender has delegated this request for the to-do to %1", delegate );
01110 }
01111 return i18n( "Sender has delegated this request for the to-do " );
01112 }
01113 case Attendee::Completed:
01114 return i18n( "The request for this to-do is now completed" );
01115 case Attendee::InProcess:
01116 return i18n( "Sender is still processing the invitation" );
01117 default:
01118 return i18n( "Unknown response to this to-do" );
01119 }
01120 break;
01121 }
01122 case iTIPCounter:
01123 return i18n( "Sender makes this counter proposal" );
01124 case iTIPDeclineCounter:
01125 return i18n( "Sender declines the counter proposal" );
01126 case iTIPNoMethod:
01127 return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() );
01128 }
01129 return QString();
01130 }
01131
01132 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
01133 {
01134
01135 if ( !msg || !journal ) {
01136 return QString();
01137 }
01138
01139 switch ( msg->method() ) {
01140 case iTIPPublish:
01141 return i18n( "This journal has been published" );
01142 case iTIPRequest:
01143 return i18n( "You have been assigned this journal" );
01144 case iTIPRefresh:
01145 return i18n( "This journal was refreshed" );
01146 case iTIPCancel:
01147 return i18n( "This journal was canceled" );
01148 case iTIPAdd:
01149 return i18n( "Addition to the journal" );
01150 case iTIPReply:
01151 {
01152 Attendee::List attendees = journal->attendees();
01153 if ( attendees.count() == 0 ) {
01154 kDebug() << "No attendees in the iCal reply!";
01155 return QString();
01156 }
01157
01158 if( attendees.count() != 1 ) {
01159 kDebug() << "Warning: attendeecount in the reply should be 1"
01160 << "but is" << attendees.count();
01161 }
01162
01163 Attendee *attendee = *attendees.begin();
01164 switch( attendee->status() ) {
01165 case Attendee::NeedsAction:
01166 return i18n( "Sender indicates this journal assignment still needs some action" );
01167 case Attendee::Accepted:
01168 return i18n( "Sender accepts this journal" );
01169 case Attendee::Tentative:
01170 return i18n( "Sender tentatively accepts this journal" );
01171 case Attendee::Declined:
01172 return i18n( "Sender declines this journal" );
01173 case Attendee::Delegated:
01174 return i18n( "Sender has delegated this request for the journal" );
01175 case Attendee::Completed:
01176 return i18n( "The request for this journal is now completed" );
01177 case Attendee::InProcess:
01178 return i18n( "Sender is still processing the invitation" );
01179 default:
01180 return i18n( "Unknown response to this journal" );
01181 }
01182 break;
01183 }
01184 case iTIPCounter:
01185 return i18n( "Sender makes this counter proposal" );
01186 case iTIPDeclineCounter:
01187 return i18n( "Sender declines the counter proposal" );
01188 case iTIPNoMethod:
01189 return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() );
01190 }
01191 return QString();
01192 }
01193
01194 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01195 {
01196 if ( !msg || !fb ) {
01197 return QString();
01198 }
01199
01200 switch ( msg->method() ) {
01201 case iTIPPublish:
01202 return i18n( "This free/busy list has been published" );
01203 case iTIPRequest:
01204 return i18n( "The free/busy list has been requested" );
01205 case iTIPRefresh:
01206 return i18n( "This free/busy list was refreshed" );
01207 case iTIPCancel:
01208 return i18n( "This free/busy list was canceled" );
01209 case iTIPAdd:
01210 return i18n( "Addition to the free/busy list" );
01211 case iTIPNoMethod:
01212 default:
01213 return i18n( "Error: Free/Busy iMIP message with unknown method: '%1'", msg->method() );
01214 }
01215 }
01216
01217
01218
01219 class KCal::IncidenceFormatter::ScheduleMessageVisitor
01220 : public IncidenceBase::Visitor
01221 {
01222 public:
01223 ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
01224 bool act( IncidenceBase *incidence, ScheduleMessage *msg )
01225 {
01226 mMessage = msg;
01227 return incidence->accept( *this );
01228 }
01229 QString result() const { return mResult; }
01230
01231 protected:
01232 QString mResult;
01233 ScheduleMessage *mMessage;
01234 };
01235
01236 class KCal::IncidenceFormatter::InvitationHeaderVisitor :
01237 public IncidenceFormatter::ScheduleMessageVisitor
01238 {
01239 protected:
01240 bool visit( Event *event )
01241 {
01242 mResult = invitationHeaderEvent( event, mMessage );
01243 return !mResult.isEmpty();
01244 }
01245 bool visit( Todo *todo )
01246 {
01247 mResult = invitationHeaderTodo( todo, mMessage );
01248 return !mResult.isEmpty();
01249 }
01250 bool visit( Journal *journal )
01251 {
01252 mResult = invitationHeaderJournal( journal, mMessage );
01253 return !mResult.isEmpty();
01254 }
01255 bool visit( FreeBusy *fb )
01256 {
01257 mResult = invitationHeaderFreeBusy( fb, mMessage );
01258 return !mResult.isEmpty();
01259 }
01260 };
01261
01262 class KCal::IncidenceFormatter::InvitationBodyVisitor
01263 : public IncidenceFormatter::ScheduleMessageVisitor
01264 {
01265 public:
01266 InvitationBodyVisitor( bool noHtmlMode )
01267 : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ) { }
01268
01269 protected:
01270 bool visit( Event *event )
01271 {
01272 mResult = invitationDetailsEvent( event, mNoHtmlMode );
01273 return !mResult.isEmpty();
01274 }
01275 bool visit( Todo *todo )
01276 {
01277 mResult = invitationDetailsTodo( todo, mNoHtmlMode );
01278 return !mResult.isEmpty();
01279 }
01280 bool visit( Journal *journal )
01281 {
01282 mResult = invitationDetailsJournal( journal, mNoHtmlMode );
01283 return !mResult.isEmpty();
01284 }
01285 bool visit( FreeBusy *fb )
01286 {
01287 mResult = invitationDetailsFreeBusy( fb );
01288 return !mResult.isEmpty();
01289 }
01290
01291 private:
01292 bool mNoHtmlMode;
01293 };
01294
01295
01296 QString InvitationFormatterHelper::generateLinkURL( const QString &id )
01297 {
01298 return id;
01299 }
01300
01301
01302 class IncidenceFormatter::IncidenceCompareVisitor
01303 : public IncidenceBase::Visitor
01304 {
01305 public:
01306 IncidenceCompareVisitor() : mExistingIncidence( 0 ) {}
01307 bool act( IncidenceBase *incidence, Incidence *existingIncidence )
01308 {
01309 if ( !existingIncidence ) {
01310 return false;
01311 }
01312 Incidence *inc = dynamic_cast<Incidence *>( incidence );
01313 if ( inc && inc->revision() <= existingIncidence->revision() ) {
01314 return false;
01315 }
01316 mExistingIncidence = existingIncidence;
01317 return incidence->accept( *this );
01318 }
01319
01320 QString result() const
01321 {
01322 if ( mChanges.isEmpty() ) {
01323 return QString();
01324 }
01325 QString html = "<div align=\"left\"><ul><li>";
01326 html += mChanges.join( "</li><li>" );
01327 html += "</li><ul></div>";
01328 return html;
01329 }
01330
01331 protected:
01332 bool visit( Event *event )
01333 {
01334 compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
01335 compareIncidences( event, mExistingIncidence );
01336 return !mChanges.isEmpty();
01337 }
01338 bool visit( Todo *todo )
01339 {
01340 compareIncidences( todo, mExistingIncidence );
01341 return !mChanges.isEmpty();
01342 }
01343 bool visit( Journal *journal )
01344 {
01345 compareIncidences( journal, mExistingIncidence );
01346 return !mChanges.isEmpty();
01347 }
01348 bool visit( FreeBusy *fb )
01349 {
01350 Q_UNUSED( fb );
01351 return !mChanges.isEmpty();
01352 }
01353
01354 private:
01355 void compareEvents( Event *newEvent, Event *oldEvent )
01356 {
01357 if ( !oldEvent || !newEvent ) {
01358 return;
01359 }
01360 if ( oldEvent->dtStart() != newEvent->dtStart() ||
01361 oldEvent->allDay() != newEvent->allDay() ) {
01362 mChanges += i18n( "The begin of the meeting has been changed from %1 to %2",
01363 eventStartTimeStr( oldEvent ), eventStartTimeStr( newEvent ) );
01364 }
01365 if ( oldEvent->dtEnd() != newEvent->dtEnd() ||
01366 oldEvent->allDay() != newEvent->allDay() ) {
01367 mChanges += i18n( "The end of the meeting has been changed from %1 to %2",
01368 eventEndTimeStr( oldEvent ), eventEndTimeStr( newEvent ) );
01369 }
01370 }
01371
01372 void compareIncidences( Incidence *newInc, Incidence *oldInc )
01373 {
01374 if ( !oldInc || !newInc ) {
01375 return;
01376 }
01377
01378 if ( oldInc->summary() != newInc->summary() ) {
01379 mChanges += i18n( "The summary has been changed to: \"%1\"",
01380 newInc->richSummary() );
01381 }
01382
01383 if ( oldInc->location() != newInc->location() ) {
01384 mChanges += i18n( "The location has been changed to: \"%1\"",
01385 newInc->richLocation() );
01386 }
01387
01388 if ( oldInc->description() != newInc->description() ) {
01389 mChanges += i18n( "The description has been changed to: \"%1\"",
01390 newInc->richDescription() );
01391 }
01392
01393 Attendee::List oldAttendees = oldInc->attendees();
01394 Attendee::List newAttendees = newInc->attendees();
01395 for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
01396 it != newAttendees.constEnd(); ++it ) {
01397 Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
01398 if ( !oldAtt ) {
01399 mChanges += i18n( "Attendee %1 has been added", (*it)->fullName() );
01400 } else {
01401 if ( oldAtt->status() != (*it)->status() ) {
01402 mChanges += i18n( "The status of attendee %1 has been changed to: %2",
01403 (*it)->fullName(), (*it)->statusStr() );
01404 }
01405 }
01406 }
01407
01408 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
01409 it != oldAttendees.constEnd(); ++it ) {
01410 Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
01411 if ( !newAtt ) {
01412 mChanges += i18n( "Attendee %1 has been removed", (*it)->fullName() );
01413 }
01414 }
01415 }
01416
01417 private:
01418 Incidence *mExistingIncidence;
01419 QStringList mChanges;
01420 };
01421
01422
01423 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
01424 {
01425 QString res( "<a href=\"%1\"><b>%2</b></a>" );
01426 return res.arg( generateLinkURL( id ) ).arg( text );
01427 return res;
01428 }
01429
01430 Calendar *InvitationFormatterHelper::calendar() const
01431 {
01432 return 0;
01433 }
01434
01435
01436
01437
01438 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
01439 {
01440 CalendarResources* cal = dynamic_cast<CalendarResources*>( calendar );
01441 if ( !cal || !incidence ) {
01442 return true;
01443 }
01444
01445 ResourceCalendar *res = cal->resource( incidence );
01446 if ( !res ) {
01447 return true;
01448 }
01449
01450 const QString subRes = res->subresourceIdentifier( incidence );
01451 if ( !subRes.contains( "/.INBOX.directory/" ) ) {
01452 return false;
01453 }
01454 return true;
01455 }
01456
01457 static QString formatICalInvitationHelper( QString invitation, Calendar *mCalendar,
01458 InvitationFormatterHelper *helper, bool noHtmlMode )
01459 {
01460 if ( invitation.isEmpty() ) {
01461 return QString();
01462 }
01463
01464 ICalFormat format;
01465
01466
01467 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
01468
01469 if( !msg ) {
01470 kDebug() << "Failed to parse the scheduling message";
01471 Q_ASSERT( format.exception() );
01472 kDebug() << format.exception()->message();
01473 return QString();
01474 }
01475
01476 IncidenceBase *incBase = msg->event();
01477 incBase->shiftTimes( mCalendar->timeSpec(), KDateTime::Spec::LocalZone() );
01478
01479 Incidence *existingIncidence = 0;
01480 if ( helper->calendar() ) {
01481 existingIncidence = helper->calendar()->incidence( incBase->uid() );
01482 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
01483 existingIncidence = 0;
01484 }
01485 if ( !existingIncidence ) {
01486 const Incidence::List list = helper->calendar()->incidences();
01487 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
01488 if ( (*it)->schedulingID() == incBase->uid() &&
01489 incidenceOwnedByMe( helper->calendar(), *it ) ) {
01490 existingIncidence = *it;
01491 break;
01492 }
01493 }
01494 }
01495 }
01496
01497
01498 QString html;
01499 html += "<div align=\"center\" style=\"border:solid 1px; width:80%;\">";
01500 html += "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
01501
01502 IncidenceFormatter::InvitationHeaderVisitor headerVisitor;
01503
01504 if ( !headerVisitor.act( incBase, msg ) ) {
01505 return QString();
01506 }
01507 html += "<tr>" + eventViewerAddTag( "h3", headerVisitor.result() ) + "</tr>";
01508
01509 IncidenceFormatter::InvitationBodyVisitor bodyVisitor( noHtmlMode );
01510 if ( !bodyVisitor.act( incBase, msg ) ) {
01511 return QString();
01512 }
01513 html += bodyVisitor.result();
01514
01515 if ( msg->method() == iTIPRequest ) {
01516 IncidenceFormatter::IncidenceCompareVisitor compareVisitor;
01517 if ( compareVisitor.act( incBase, existingIncidence ) ) {
01518 html +=
01519 i18n( "<p align=\"left\">The following changes have been made by the organizer:</p>" );
01520 html += compareVisitor.result();
01521 }
01522 }
01523
01524
01525
01526 html += "<p>";
01527 html += "<table border=\"0\" align=\"center\" cellspacing=\"4\"><tr>";
01528
01529
01530
01531 const QString tdOpen = "<td style=\"border-width:0px;border-style:outset\">";
01532 const QString tdClose = "</td>";
01533 Incidence *incidence = dynamic_cast<Incidence*>( incBase );
01534 switch ( msg->method() ) {
01535 case iTIPPublish:
01536 case iTIPRequest:
01537 case iTIPRefresh:
01538 case iTIPAdd:
01539 {
01540 if ( incidence && incidence->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
01541 html += tdOpen;
01542 if ( incBase->type() == "Todo" ) {
01543
01544 html += helper->makeLink( "reply", i18n( "[Enter this into my to-do list]" ) );
01545 } else {
01546 html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
01547 }
01548 html += tdClose;
01549 }
01550
01551 if ( incidence && !existingIncidence ) {
01552
01553 html += tdOpen;
01554
01555 html += helper->makeLink( "accept", i18nc( "accept to-do request", "[Accept]" ) );
01556 html += tdClose;
01557
01558
01559 html += tdOpen;
01560
01561 html += helper->makeLink( "accept_conditionally",
01562 i18nc( "Accept conditionally", "[Accept cond.]" ) );
01563 html += tdClose;
01564
01565
01566 html += tdOpen;
01567
01568 html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
01569 html += tdClose;
01570
01571
01572 html += tdOpen;
01573
01574 html += helper->makeLink( "decline", i18nc( "decline to-do request", "[Decline]" ) );
01575 html += tdClose;
01576
01577
01578 html += tdOpen;
01579
01580 html += helper->makeLink( "delegate", i18nc( "delegate to-do to another", "[Delegate]" ) );
01581 html += tdClose;
01582
01583
01584 html += tdOpen;
01585
01586 html += helper->makeLink( "forward", i18nc( "forward request to another", "[Forward]" ) );
01587 html += tdClose;
01588
01589
01590 if ( incBase->type() == "Event" ) {
01591 html += tdOpen;
01592
01593
01594 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01595 html += tdClose;
01596 }
01597 }
01598 break;
01599 }
01600
01601 case iTIPCancel:
01602
01603 html += tdOpen;
01604
01605 html += helper->makeLink( "cancel", i18n( "[Remove this from my calendar]" ) );
01606 html += tdClose;
01607 break;
01608
01609 case iTIPReply:
01610
01611 html += tdOpen;
01612
01613
01614 if ( incBase->type() == "Todo" ) {
01615 html += helper->makeLink( "reply", i18n( "[Enter this into my to-do list]" ) );
01616 } else {
01617 html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
01618 }
01619 html += tdClose;
01620 break;
01621
01622 case iTIPCounter:
01623 html += tdOpen;
01624
01625 html += helper->makeLink( "accept_counter", i18n( "[Accept]" ) );
01626 html += tdClose;
01627
01628 html += tdOpen;
01629
01630 html += helper->makeLink( "decline_counter", i18n( "[Decline]" ) );
01631 html += tdClose;
01632
01633 html += tdOpen;
01634
01635
01636 html += helper->makeLink( "check_calendar", i18n( "[Check my calendar]" ) );
01637 html += tdClose;
01638 break;
01639
01640 case iTIPDeclineCounter:
01641 case iTIPNoMethod:
01642 break;
01643 }
01644
01645
01646 html += "</tr></table>";
01647
01648
01649 html += "</table></div>";
01650 kDebug() << html;
01651 return html;
01652 }
01653
01654
01655 QString IncidenceFormatter::formatICalInvitation( QString invitation, Calendar *mCalendar,
01656 InvitationFormatterHelper *helper )
01657 {
01658 return formatICalInvitationHelper( invitation, mCalendar, helper, false );
01659 }
01660
01661 QString IncidenceFormatter::formatICalInvitationNoHtml( QString invitation, Calendar *mCalendar,
01662 InvitationFormatterHelper *helper )
01663 {
01664 return formatICalInvitationHelper( invitation, mCalendar, helper, true );
01665 }
01666
01667
01668
01669
01670
01671
01672 class KCal::IncidenceFormatter::ToolTipVisitor
01673 : public IncidenceBase::Visitor
01674 {
01675 public:
01676 ToolTipVisitor()
01677 : mRichText( true ), mSpec( KDateTime::Spec() ), mResult( "" ) {}
01678
01679 bool act( IncidenceBase *incidence, bool richText=true, KDateTime::Spec spec=KDateTime::Spec() )
01680 {
01681 mRichText = richText;
01682 mSpec = spec;
01683 mResult = "";
01684 return incidence ? incidence->accept( *this ) : false;
01685 }
01686 QString result() const { return mResult; }
01687
01688 protected:
01689 bool visit( Event *event );
01690 bool visit( Todo *todo );
01691 bool visit( Journal *journal );
01692 bool visit( FreeBusy *fb );
01693
01694 QString dateRangeText( Event *event );
01695 QString dateRangeText( Todo *todo );
01696 QString dateRangeText( Journal *journal );
01697 QString dateRangeText( FreeBusy *fb );
01698
01699 QString generateToolTip( Incidence *incidence, QString dtRangeText );
01700
01701 protected:
01702 bool mRichText;
01703 KDateTime::Spec mSpec;
01704 QString mResult;
01705 };
01706
01707 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event )
01708 {
01709
01710 QString ret;
01711 QString tmp;
01712 if ( event->isMultiDay() ) {
01713
01714 tmp = IncidenceFormatter::dateToString( event->dtStart(), true, mSpec );
01715 ret += "<br>" + i18nc( "Event start", "<i>From:</i> %1", tmp );
01716
01717 tmp = IncidenceFormatter::dateToString( event->dtEnd(), true, mSpec );
01718 ret += "<br>" + i18nc( "Event end","<i>To:</i> %1", tmp );
01719
01720 } else {
01721
01722 ret += "<br>" +
01723 i18n( "<i>Date:</i> %1", IncidenceFormatter::dateToString( event->dtStart(), true, mSpec ) );
01724 if ( !event->allDay() ) {
01725 const QString dtStartTime = IncidenceFormatter::timeToString( event->dtStart(), true, mSpec );
01726 const QString dtEndTime = IncidenceFormatter::timeToString( event->dtEnd(), true, mSpec );
01727 if ( dtStartTime == dtEndTime ) {
01728
01729 tmp = "<br>" +
01730 i18nc( "time for event", "<i>Time:</i> %1",
01731 dtStartTime );
01732 } else {
01733 tmp = "<br>" +
01734 i18nc( "time range for event",
01735 "<i>Time:</i> %1 - %2",
01736 dtStartTime, dtEndTime );
01737 }
01738 ret += tmp;
01739 }
01740 }
01741 return ret.replace( ' ', " " );
01742 }
01743
01744 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo )
01745 {
01746
01747 QString ret;
01748 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
01749
01750
01751
01752 ret += "<br>" + i18n( "<i>Start:</i> %1",
01753 IncidenceFormatter::dateToString( todo->dtStart( false ), true, mSpec ) );
01754 }
01755 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
01756 ret += "<br>" + i18n( "<i>Due:</i> %1",
01757 IncidenceFormatter::dateTimeToString( todo->dtDue(),
01758 todo->allDay(),
01759 true, mSpec ) );
01760 }
01761 if ( todo->isCompleted() ) {
01762 ret += "<br>" +
01763 i18n( "<i>Completed:</i> %1", todo->completedStr() );
01764 } else {
01765 ret += "<br>" +
01766 i18nc( "percent complete", "%1 % completed", todo->percentComplete() );
01767 }
01768
01769 return ret.replace( ' ', " " );
01770 }
01771
01772 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal *journal )
01773 {
01774
01775 QString ret;
01776 if ( journal->dtStart().isValid() ) {
01777 ret += "<br>" +
01778 i18n( "<i>Date:</i> %1",
01779 IncidenceFormatter::dateToString( journal->dtStart(), false, mSpec ) );
01780 }
01781 return ret.replace( ' ', " " );
01782 }
01783
01784 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
01785 {
01786
01787 QString ret;
01788 ret = "<br>" +
01789 i18n( "<i>Period start:</i> %1",
01790 KGlobal::locale()->formatDateTime( fb->dtStart().dateTime() ) );
01791 ret += "<br>" +
01792 i18n( "<i>Period start:</i> %1",
01793 KGlobal::locale()->formatDateTime( fb->dtEnd().dateTime() ) );
01794 return ret.replace( ' ', " " );
01795 }
01796
01797 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
01798 {
01799 mResult = generateToolTip( event, dateRangeText( event ) );
01800 return !mResult.isEmpty();
01801 }
01802
01803 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
01804 {
01805 mResult = generateToolTip( todo, dateRangeText( todo ) );
01806 return !mResult.isEmpty();
01807 }
01808
01809 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
01810 {
01811 mResult = generateToolTip( journal, dateRangeText( journal ) );
01812 return !mResult.isEmpty();
01813 }
01814
01815 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
01816 {
01817
01818 mResult = "<qt><b>" + i18n( "Free/Busy information for %1", fb->organizer().fullName() ) + "</b>";
01819 mResult += dateRangeText( fb );
01820 mResult += "</qt>";
01821 return !mResult.isEmpty();
01822 }
01823
01824 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence *incidence,
01825 QString dtRangeText )
01826 {
01827
01828 if ( !incidence ) {
01829 return QString();
01830 }
01831
01832 QString tmp = "<qt><b>"+ incidence->richSummary() + "</b>";
01833
01834 tmp += dtRangeText;
01835
01836 if ( !incidence->location().isEmpty() ) {
01837
01838 tmp += "<br>" +
01839 i18n( "<i>Location:</i> %1", incidence->richLocation() );
01840 }
01841
01842 if ( !incidence->description().isEmpty() ) {
01843 QString desc( incidence->description() );
01844 if ( !incidence->descriptionIsRich() ) {
01845 if ( desc.length() > 120 ) {
01846 desc = desc.left( 120 ) + "...";
01847 }
01848 desc = Qt::escape( desc ).replace( '\n', "<br>" );
01849 } else {
01850
01851 }
01852 tmp += "<br>----------<br>" + i18n( "<i>Description:</i>" ) + "<br>" + desc;
01853 }
01854 tmp += "</qt>";
01855 return tmp;
01856 }
01857
01858
01859 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence,
01860 bool richText )
01861 {
01862 return toolTipStr( incidence, richText, KDateTime::Spec() );
01863 }
01864
01865 QString IncidenceFormatter::toolTipStr( IncidenceBase *incidence,
01866 bool richText, KDateTime::Spec spec )
01867 {
01868 ToolTipVisitor v;
01869 if ( v.act( incidence, richText, spec ) ) {
01870 return v.result();
01871 } else {
01872 return QString();
01873 }
01874 }
01875
01876
01877
01878
01879
01880
01881 static QString mailBodyIncidence( Incidence *incidence )
01882 {
01883 QString body;
01884 if ( !incidence->summary().isEmpty() ) {
01885 body += i18n( "Summary: %1\n", incidence->richSummary() );
01886 }
01887 if ( !incidence->organizer().isEmpty() ) {
01888 body += i18n( "Organizer: %1\n", incidence->organizer().fullName() );
01889 }
01890 if ( !incidence->location().isEmpty() ) {
01891 body += i18n( "Location: %1\n", incidence->richLocation() );
01892 }
01893 return body;
01894 }
01895
01896
01897
01898 class KCal::IncidenceFormatter::MailBodyVisitor
01899 : public IncidenceBase::Visitor
01900 {
01901 public:
01902 MailBodyVisitor()
01903 : mSpec( KDateTime::Spec() ), mResult( "" ) {}
01904
01905 bool act( IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec() )
01906 {
01907 mSpec = spec;
01908 mResult = "";
01909 return incidence ? incidence->accept( *this ) : false;
01910 }
01911 QString result() const
01912 {
01913 return mResult;
01914 }
01915
01916 protected:
01917 bool visit( Event *event );
01918 bool visit( Todo *todo );
01919 bool visit( Journal *journal );
01920 bool visit( FreeBusy * )
01921 {
01922 mResult = i18n( "This is a Free Busy Object" );
01923 return !mResult.isEmpty();
01924 }
01925 protected:
01926 KDateTime::Spec mSpec;
01927 QString mResult;
01928 };
01929
01930 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
01931 {
01932 QString recurrence[]= {
01933 i18nc( "no recurrence", "None" ),
01934 i18nc( "event recurs by minutes", "Minutely" ),
01935 i18nc( "event recurs by hours", "Hourly" ),
01936 i18nc( "event recurs by days", "Daily" ),
01937 i18nc( "event recurs by weeks", "Weekly" ),
01938 i18nc( "event recurs same position (e.g. first monday) each month", "Monthly Same Position" ),
01939 i18nc( "event recurs same day each month", "Monthly Same Day" ),
01940 i18nc( "event recurs same month each year", "Yearly Same Month" ),
01941 i18nc( "event recurs same day each year", "Yearly Same Day" ),
01942 i18nc( "event recurs same position (e.g. first monday) each year", "Yearly Same Position" )
01943 };
01944
01945 mResult = mailBodyIncidence( event );
01946 mResult += i18n( "Start Date: %1\n", IncidenceFormatter::dateToString( event->dtStart(), true,
01947 mSpec ) );
01948 if ( !event->allDay() ) {
01949 mResult += i18n( "Start Time: %1\n", IncidenceFormatter::timeToString( event->dtStart(),
01950 true, mSpec ) );
01951 }
01952 if ( event->dtStart() != event->dtEnd() ) {
01953 mResult += i18n( "End Date: %1\n", IncidenceFormatter::dateToString( event->dtEnd(), true,
01954 mSpec ) );
01955 }
01956 if ( !event->allDay() ) {
01957 mResult += i18n( "End Time: %1\n", IncidenceFormatter::timeToString( event->dtEnd(), true,
01958 mSpec ) );
01959 }
01960 if ( event->recurs() ) {
01961 Recurrence *recur = event->recurrence();
01962
01963 mResult += i18n( "Recurs: %1\n", recurrence[ recur->recurrenceType() ] );
01964 mResult += i18n( "Frequency: %1\n", event->recurrence()->frequency() );
01965
01966 if ( recur->duration() > 0 ) {
01967 mResult += i18np( "Repeats once", "Repeats %1 times", recur->duration() );
01968 mResult += '\n';
01969 } else {
01970 if ( recur->duration() != -1 ) {
01971
01972 QString endstr;
01973 if ( event->allDay() ) {
01974 endstr = KGlobal::locale()->formatDate( recur->endDate() );
01975 } else {
01976 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime().dateTime() );
01977 }
01978 mResult += i18n( "Repeat until: %1\n", endstr );
01979 } else {
01980 mResult += i18n( "Repeats forever\n" );
01981 }
01982 }
01983 }
01984
01985 QString details = event->richDescription();
01986 if ( !details.isEmpty() ) {
01987 mResult += i18n( "Details:\n%1\n", details );
01988 }
01989 return !mResult.isEmpty();
01990 }
01991
01992 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
01993 {
01994 mResult = mailBodyIncidence( todo );
01995
01996 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
01997 mResult += i18n( "Start Date: %1\n",
01998 IncidenceFormatter::dateToString( todo->dtStart(false), true, mSpec ) );
01999 if ( !todo->allDay() ) {
02000 mResult += i18n( "Start Time: %1\n",
02001 IncidenceFormatter::timeToString( todo->dtStart(false), true, mSpec ) );
02002 }
02003 }
02004 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
02005 mResult += i18n( "Due Date: %1\n",
02006 IncidenceFormatter::dateToString( todo->dtDue(), true, mSpec ) );
02007 if ( !todo->allDay() ) {
02008 mResult += i18n( "Due Time: %1\n",
02009 IncidenceFormatter::timeToString( todo->dtDue(), true, mSpec ) );
02010 }
02011 }
02012 QString details = todo->richDescription();
02013 if ( !details.isEmpty() ) {
02014 mResult += i18n( "Details:\n%1\n", details );
02015 }
02016 return !mResult.isEmpty();
02017 }
02018
02019 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
02020 {
02021 mResult = mailBodyIncidence( journal );
02022 mResult += i18n( "Date: %1\n", IncidenceFormatter::dateToString( journal->dtStart(), true,
02023 mSpec ) );
02024
02025 if ( !journal->allDay() ) {
02026 mResult += i18n( "Time: %1\n",
02027 IncidenceFormatter::timeToString( journal->dtStart(), true, mSpec ) );
02028
02029 }
02030 if ( !journal->description().isEmpty() ) {
02031 mResult += i18n( "Text of the journal:\n%1\n", journal->richDescription() );
02032 }
02033 return !mResult.isEmpty();
02034 }
02035
02036
02037 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
02038 {
02039 return mailBodyStr( incidence, KDateTime::Spec() );
02040 }
02041
02042 QString IncidenceFormatter::mailBodyStr( IncidenceBase *incidence,
02043 KDateTime::Spec spec )
02044 {
02045 if ( !incidence ) {
02046 return QString();
02047 }
02048
02049 MailBodyVisitor v;
02050 if ( v.act( incidence, spec ) ) {
02051 return v.result();
02052 }
02053 return QString();
02054 }
02055
02056
02057 static QString recurEnd( Incidence *incidence )
02058 {
02059 QString endstr;
02060 if ( incidence->allDay() ) {
02061 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
02062 } else {
02063 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
02064 }
02065 return endstr;
02066 }
02067
02068
02069 QString IncidenceFormatter::recurrenceString( Incidence *incidence )
02070 {
02071 if ( !incidence->recurs() ) {
02072 return i18n( "No recurrence" );
02073 }
02074 QStringList dayList;
02075 dayList.append( i18n( "31st Last" ) );
02076 dayList.append( i18n( "30th Last" ) );
02077 dayList.append( i18n( "29th Last" ) );
02078 dayList.append( i18n( "28th Last" ) );
02079 dayList.append( i18n( "27th Last" ) );
02080 dayList.append( i18n( "26th Last" ) );
02081 dayList.append( i18n( "25th Last" ) );
02082 dayList.append( i18n( "24th Last" ) );
02083 dayList.append( i18n( "23rd Last" ) );
02084 dayList.append( i18n( "22nd Last" ) );
02085 dayList.append( i18n( "21st Last" ) );
02086 dayList.append( i18n( "20th Last" ) );
02087 dayList.append( i18n( "19th Last" ) );
02088 dayList.append( i18n( "18th Last" ) );
02089 dayList.append( i18n( "17th Last" ) );
02090 dayList.append( i18n( "16th Last" ) );
02091 dayList.append( i18n( "15th Last" ) );
02092 dayList.append( i18n( "14th Last" ) );
02093 dayList.append( i18n( "13th Last" ) );
02094 dayList.append( i18n( "12th Last" ) );
02095 dayList.append( i18n( "11th Last" ) );
02096 dayList.append( i18n( "10th Last" ) );
02097 dayList.append( i18n( "9th Last" ) );
02098 dayList.append( i18n( "8th Last" ) );
02099 dayList.append( i18n( "7th Last" ) );
02100 dayList.append( i18n( "6th Last" ) );
02101 dayList.append( i18n( "5th Last" ) );
02102 dayList.append( i18n( "4th Last" ) );
02103 dayList.append( i18n( "3rd Last" ) );
02104 dayList.append( i18n( "2nd Last" ) );
02105 dayList.append( i18nc( "last day of the month", "Last" ) );
02106 dayList.append( i18nc( "unknown day of the month", "unknown" ) );
02107 dayList.append( i18n( "1st" ) );
02108 dayList.append( i18n( "2nd" ) );
02109 dayList.append( i18n( "3rd" ) );
02110 dayList.append( i18n( "4th" ) );
02111 dayList.append( i18n( "5th" ) );
02112 dayList.append( i18n( "6th" ) );
02113 dayList.append( i18n( "7th" ) );
02114 dayList.append( i18n( "8th" ) );
02115 dayList.append( i18n( "9th" ) );
02116 dayList.append( i18n( "10th" ) );
02117 dayList.append( i18n( "11th" ) );
02118 dayList.append( i18n( "12th" ) );
02119 dayList.append( i18n( "13th" ) );
02120 dayList.append( i18n( "14th" ) );
02121 dayList.append( i18n( "15th" ) );
02122 dayList.append( i18n( "16th" ) );
02123 dayList.append( i18n( "17th" ) );
02124 dayList.append( i18n( "18th" ) );
02125 dayList.append( i18n( "19th" ) );
02126 dayList.append( i18n( "20th" ) );
02127 dayList.append( i18n( "21st" ) );
02128 dayList.append( i18n( "22nd" ) );
02129 dayList.append( i18n( "23rd" ) );
02130 dayList.append( i18n( "24th" ) );
02131 dayList.append( i18n( "25th" ) );
02132 dayList.append( i18n( "26th" ) );
02133 dayList.append( i18n( "27th" ) );
02134 dayList.append( i18n( "28th" ) );
02135 dayList.append( i18n( "29th" ) );
02136 dayList.append( i18n( "30th" ) );
02137 dayList.append( i18n( "31st" ) );
02138 int weekStart = KGlobal::locale()->weekStartDay();
02139 QString dayNames;
02140 QString txt;
02141 const KCalendarSystem *calSys = KGlobal::locale()->calendar();
02142 Recurrence *recur = incidence->recurrence();
02143 switch ( recur->recurrenceType() ) {
02144 case Recurrence::rNone:
02145 return i18n( "No recurrence" );
02146 case Recurrence::rMinutely:
02147 if ( recur->duration() != -1 ) {
02148 txt = i18np( "Recurs every minute until %2",
02149 "Recurs every %1 minutes until %2",
02150 recur->frequency(), recurEnd( incidence ) );
02151 if ( recur->duration() > 0 ) {
02152 txt += i18nc( "number of occurrences",
02153 " (<numid>%1</numid> occurrences)",
02154 recur->duration() );
02155 }
02156 return txt;
02157 }
02158 return i18np( "Recurs every minute",
02159 "Recurs every %1 minutes", recur->frequency() );
02160 case Recurrence::rHourly:
02161 if ( recur->duration() != -1 ) {
02162 txt = i18np( "Recurs hourly until %2",
02163 "Recurs every %1 hours until %2",
02164 recur->frequency(), recurEnd( incidence ) );
02165 if ( recur->duration() > 0 ) {
02166 txt += i18nc( "number of occurrences",
02167 " (<numid>%1</numid> occurrences)",
02168 recur->duration() );
02169 }
02170 return txt;
02171 }
02172 return i18np( "Recurs hourly", "Recurs every %1 hours", recur->frequency() );
02173 case Recurrence::rDaily:
02174 if ( recur->duration() != -1 ) {
02175 txt = i18np( "Recurs daily until %2",
02176 "Recurs every %1 days until %2",
02177 recur->frequency(), recurEnd( incidence ) );
02178 if ( recur->duration() > 0 ) {
02179 txt += i18nc( "number of occurrences",
02180 " (<numid>%1</numid> occurrences)",
02181 recur->duration() );
02182 }
02183 return txt;
02184 }
02185 return i18np( "Recurs daily", "Recurs every %1 days", recur->frequency() );
02186 case Recurrence::rWeekly:
02187 {
02188 bool addSpace = false;
02189 for ( int i = 0; i < 7; ++i ) {
02190 if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
02191 if ( addSpace ) {
02192 dayNames.append( i18nc( "separator for list of days", ", " ) );
02193 }
02194 dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1,
02195 KCalendarSystem::ShortDayName ) );
02196 addSpace = true;
02197 }
02198 }
02199 if ( dayNames.isEmpty() ) {
02200 dayNames = i18nc( "Recurs weekly on no days", "no days" );
02201 }
02202 if ( recur->duration() != -1 ) {
02203 txt = i18ncp( "Recurs weekly on [list of days] until end-date",
02204 "Recurs weekly on %2 until %3",
02205 "Recurs every <numid>%1</numid> weeks on %2 until %3",
02206 recur->frequency(), dayNames, recurEnd( incidence ) );
02207 if ( recur->duration() > 0 ) {
02208 txt += i18nc( "number of occurrences",
02209 " (<numid>%1</numid> occurrences)",
02210 recur->duration() );
02211 }
02212 return txt;
02213 }
02214 return i18ncp( "Recurs weekly on [list of days]",
02215 "Recurs weekly on %2",
02216 "Recurs every <numid>%1</numid> weeks on %2",
02217 recur->frequency(), dayNames );
02218 }
02219 case Recurrence::rMonthlyPos:
02220 {
02221 KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0];
02222 if ( recur->duration() != -1 ) {
02223 txt = i18ncp( "Recurs every N months on the [2nd|3rd|...]"
02224 " weekdayname until end-date",
02225 "Recurs every month on the %2 %3 until %4",
02226 "Recurs every <numid>%1</numid> months on the %2 %3 until %4",
02227 recur->frequency(),
02228 dayList[rule.pos() + 31],
02229 calSys->weekDayName( rule.day(),KCalendarSystem::LongDayName ),
02230 recurEnd( incidence ) );
02231 if ( recur->duration() > 0 ) {
02232 txt += i18nc( "number of occurrences",
02233 " (<numid>%1</numid> occurrences)",
02234 recur->duration() );
02235 }
02236 return txt;
02237 }
02238 return i18ncp( "Recurs every N months on the [2nd|3rd|...] weekdayname",
02239 "Recurs every month on the %2 %3",
02240 "Recurs every %1 months on the %2 %3",
02241 recur->frequency(),
02242 dayList[rule.pos() + 31],
02243 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ) );
02244 }
02245 case Recurrence::rMonthlyDay:
02246 {
02247 int days = recur->monthDays()[0];
02248 if ( recur->duration() != -1 ) {
02249 txt = i18ncp( "Recurs monthly on the [1st|2nd|...] day until end-date",
02250 "Recurs monthly on the %2 day until %3",
02251 "Recurs every %1 months on the %2 day until %3",
02252 recur->frequency(),
02253 dayList[days + 31],
02254 recurEnd( incidence ) );
02255 if ( recur->duration() > 0 ) {
02256 txt += i18nc( "number of occurrences",
02257 " (<numid>%1</numid> occurrences)",
02258 recur->duration() );
02259 }
02260 return txt;
02261 }
02262 return i18ncp( "Recurs monthly on the [1st|2nd|...] day",
02263 "Recurs monthly on the %2 day",
02264 "Recurs every <numid>%1</numid> month on the %2 day",
02265 recur->frequency(),
02266 dayList[days + 31] );
02267 }
02268 case Recurrence::rYearlyMonth:
02269 {
02270 if ( recur->duration() != -1 ) {
02271 txt = i18ncp( "Recurs Every N years on month-name [1st|2nd|...]"
02272 " until end-date",
02273 "Recurs yearly on %2 %3 until %4",
02274 "Recurs every %1 years on %2 %3 until %4",
02275 recur->frequency(),
02276 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
02277 dayList[ recur->yearDates()[0] + 31 ],
02278 recurEnd( incidence ) );
02279 if ( recur->duration() > 0 ) {
02280 txt += i18nc( "number of occurrences",
02281 " (<numid>%1</numid> occurrences)",
02282 recur->duration() );
02283 }
02284 return txt;
02285 }
02286 if ( !recur->yearDates().isEmpty() ) {
02287 return i18ncp( "Recurs Every N years on month-name [1st|2nd|...]",
02288 "Recurs yearly on %2 %3",
02289 "Recurs every %1 years on %2 %3",
02290 recur->frequency(),
02291 calSys->monthName( recur->yearMonths()[0],
02292 recur->startDate().year() ),
02293 dayList[ recur->yearDates()[0] + 31 ] );
02294 } else {
02295 if (!recur->yearMonths().isEmpty() ) {
02296 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
02297 "Recurs yearly on %1 %2",
02298 calSys->monthName( recur->yearMonths()[0],
02299 recur->startDate().year() ),
02300 dayList[ recur->startDate().day() + 31 ] );
02301 } else {
02302 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
02303 "Recurs yearly on %1 %2",
02304 calSys->monthName( recur->startDate().month(),
02305 recur->startDate().year() ),
02306 dayList[ recur->startDate().day() + 31 ] );
02307 }
02308 }
02309 }
02310 case Recurrence::rYearlyDay:
02311 if ( recur->duration() != -1 ) {
02312 txt = i18ncp( "Recurs every N years on day N until end-date",
02313 "Recurs every year on day <numid>%2</numid> until %3",
02314 "Recurs every <numid>%1</numid> years"
02315 " on day <numid>%2</numid> until %3",
02316 recur->frequency(),
02317 recur->yearDays()[0],
02318 recurEnd( incidence ) );
02319 if ( recur->duration() > 0 ) {
02320 txt += i18nc( "number of occurrences",
02321 " (<numid>%1</numid> occurrences)",
02322 recur->duration() );
02323 }
02324 return txt;
02325 }
02326 return i18ncp( "Recurs every N YEAR[S] on day N",
02327 "Recurs every year on day <numid>%2</numid>",
02328 "Recurs every <numid>%1</numid> years"
02329 " on day <numid>%2</numid>",
02330 recur->frequency(), recur->yearDays()[0] );
02331 case Recurrence::rYearlyPos:
02332 {
02333 KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0];
02334 if ( recur->duration() != -1 ) {
02335 txt = i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
02336 "of monthname until end-date",
02337 "Every year on the %2 %3 of %4 until %5",
02338 "Every <numid>%1</numid> years on the %2 %3 of %4"
02339 " until %5",
02340 recur->frequency(),
02341 dayList[rule.pos() + 31],
02342 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
02343 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
02344 recurEnd( incidence ) );
02345 if ( recur->duration() > 0 ) {
02346 txt += i18nc( "number of occurrences",
02347 " (<numid>%1</numid> occurrences)",
02348 recur->duration() );
02349 }
02350 return txt;
02351 }
02352 return i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
02353 "of monthname",
02354 "Every year on the %2 %3 of %4",
02355 "Every <numid>%1</numid> years on the %2 %3 of %4",
02356 recur->frequency(),
02357 dayList[rule.pos() + 31],
02358 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
02359 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) );
02360 }
02361 default:
02362 return i18n( "Incidence recurs" );
02363 }
02364 }
02365
02366 QString IncidenceFormatter::timeToString( const KDateTime &date,
02367 bool shortfmt,
02368 const KDateTime::Spec &spec )
02369 {
02370 if ( spec.isValid() ) {
02371
02372 QString timeZone;
02373 if ( spec.timeZone() != KSystemTimeZones::local() ) {
02374 timeZone = ' ' + spec.timeZone().name();
02375 }
02376
02377 return KGlobal::locale()->formatTime( date.toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
02378 } else {
02379 return KGlobal::locale()->formatTime( date.time(), !shortfmt );
02380 }
02381 }
02382
02383 QString IncidenceFormatter::dateToString( const KDateTime &date,
02384 bool shortfmt,
02385 const KDateTime::Spec &spec )
02386 {
02387 if ( spec.isValid() ) {
02388
02389 QString timeZone;
02390 if ( spec.timeZone() != KSystemTimeZones::local() ) {
02391 timeZone = ' ' + spec.timeZone().name();
02392 }
02393
02394 return
02395 KGlobal::locale()->formatDate( date.toTimeSpec( spec ).date(),
02396 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) +
02397 timeZone;
02398 } else {
02399 return
02400 KGlobal::locale()->formatDate( date.date(),
02401 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
02402 }
02403 }
02404
02405 QString IncidenceFormatter::dateTimeToString( const KDateTime &date,
02406 bool allDay,
02407 bool shortfmt,
02408 const KDateTime::Spec &spec )
02409 {
02410 if ( allDay ) {
02411 return dateToString( date, shortfmt, spec );
02412 }
02413
02414 if ( spec.isValid() ) {
02415 QString timeZone;
02416 if ( spec.timeZone() != KSystemTimeZones::local() ) {
02417 timeZone = ' ' + spec.timeZone().name();
02418 }
02419
02420 return KGlobal::locale()->formatDateTime(
02421 date.toTimeSpec( spec ).dateTime(),
02422 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
02423 } else {
02424 return KGlobal::locale()->formatDateTime(
02425 date.dateTime(),
02426 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
02427 }
02428 }