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 foreach ( const QString &c, incidence->comments() ) {
00715 if ( !c.isEmpty() ) {
00716 comments += string2HTML( c );
00717 }
00718 }
00719 if ( !incidence->description().isEmpty() ) {
00720
00721 if ( !incidence->descriptionIsRich() ) {
00722 descr = string2HTML( incidence->description() );
00723 } else {
00724 descr = incidence->richDescription();
00725 if ( noHtmlMode ) {
00726 descr = cleanHtml( descr );
00727 }
00728 }
00729 }
00730 }
00731
00732 if( !descr.isEmpty() ) {
00733 html += "<p>";
00734 html += "<table border=\"0\" style=\"margin-top:4px;\">";
00735 html += "<tr><td><center>" +
00736 eventViewerAddTag( "u", i18n( "Description:" ) ) +
00737 "</center></td></tr>";
00738 html += "<tr><td>" + descr + "</td></tr>";
00739 html += "</table>";
00740 }
00741
00742 if ( !comments.isEmpty() ) {
00743 html += "<p>";
00744 html += "<table border=\"0\" style=\"margin-top:4px;\">";
00745 html += "<tr><td><center>" +
00746 eventViewerAddTag( "u", i18n( "Comments:" ) ) +
00747 "</center></td></tr>";
00748 html += "<tr>";
00749 if ( comments.count() > 1 ) {
00750 html += "<td><ul>";
00751 for ( int i=0; i < comments.count(); ++i ) {
00752 html += "<li>" + comments[i] + "</li>";
00753 }
00754 html += "</ul></td>";
00755 } else {
00756 html += "<td>" + comments[0] + "</td>";
00757 }
00758 html += "</tr>";
00759 html += "</table>";
00760 }
00761 return html;
00762 }
00763
00764 static QString invitationDetailsEvent( Event *event, bool noHtmlMode )
00765 {
00766
00767 if ( !event ) {
00768 return QString();
00769 }
00770
00771 QString sSummary = i18n( "Summary unspecified" );
00772 if ( !event->summary().isEmpty() ) {
00773 if ( !event->summaryIsRich() ) {
00774 sSummary = string2HTML( event->summary() );
00775 } else {
00776 sSummary = event->richSummary();
00777 if ( noHtmlMode ) {
00778 sSummary = cleanHtml( sSummary );
00779 }
00780 }
00781 }
00782
00783 QString sLocation = i18n( "Location unspecified" );
00784 if ( !event->location().isEmpty() ) {
00785 if ( !event->locationIsRich() ) {
00786 sLocation = string2HTML( event->location() );
00787 } else {
00788 sLocation = event->richLocation();
00789 if ( noHtmlMode ) {
00790 sLocation = cleanHtml( sLocation );
00791 }
00792 }
00793 }
00794
00795 QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" );
00796 QString html = QString( "<div dir=\"%1\">\n" ).arg( dir );
00797
00798
00799 html += invitationRow( i18n( "What:" ), sSummary );
00800 html += invitationRow( i18n( "Where:" ), sLocation );
00801
00802
00803 html += invitationRow( i18n( "Start Time:" ), eventStartTimeStr( event ) );
00804
00805
00806 html += invitationRow( i18n( "End Time:" ), eventEndTimeStr( event ) );
00807
00808
00809 if ( !event->allDay() && event->hasEndDate() && event->dtEnd().isValid() ) {
00810 QString tmp;
00811 QTime sDuration( 0, 0, 0 ), t;
00812 int secs = event->dtStart().secsTo( event->dtEnd() );
00813 t = sDuration.addSecs( secs );
00814 if ( t.hour() > 0 ) {
00815 tmp += i18np( "1 hour ", "%1 hours ", t.hour() );
00816 }
00817 if ( t.minute() > 0 ) {
00818 tmp += i18np( "1 minute ", "%1 minutes ", t.minute() );
00819 }
00820
00821 html += invitationRow( i18n( "Duration:" ), tmp );
00822 }
00823
00824 if ( event->recurs() ) {
00825 html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) );
00826 }
00827
00828 html += "</div>";
00829 html += invitationsDetailsIncidence( event, noHtmlMode );
00830
00831 return html;
00832 }
00833
00834 static QString invitationDetailsTodo( Todo *todo, bool noHtmlMode )
00835 {
00836
00837 if ( !todo ) {
00838 return QString();
00839 }
00840
00841 QString sSummary = i18n( "Summary unspecified" );
00842 QString sDescr = i18n( "Description unspecified" );
00843 if ( ! todo->summary().isEmpty() ) {
00844 sSummary = todo->richSummary();
00845 if ( noHtmlMode ) {
00846 sSummary = cleanHtml( sSummary );
00847 }
00848 }
00849 if ( ! todo->description().isEmpty() ) {
00850 sDescr = todo->description();
00851 if ( noHtmlMode ) {
00852 sDescr = cleanHtml( sDescr );
00853 }
00854 }
00855 QString html = "<table border=\"0\" width=\"80%\" align=\"center\" "
00856 "cellpadding=\"1\" cellspacing=\"1\">";
00857 html += invitationRow( i18n( "Summary:" ), sSummary );
00858 html += invitationRow( i18n( "Description:" ), sDescr );
00859 html += "</table>\n";
00860 html += invitationsDetailsIncidence( todo, noHtmlMode );
00861
00862 return html;
00863 }
00864
00865 static QString invitationDetailsJournal( Journal *journal, bool noHtmlMode )
00866 {
00867 if ( !journal ) {
00868 return QString();
00869 }
00870
00871 QString sSummary = i18n( "Summary unspecified" );
00872 QString sDescr = i18n( "Description unspecified" );
00873 if ( ! journal->summary().isEmpty() ) {
00874 sSummary = journal->richSummary();
00875 if ( noHtmlMode ) {
00876 sSummary = cleanHtml( sSummary );
00877 }
00878 }
00879 if ( ! journal->description().isEmpty() ) {
00880 sDescr = journal->richDescription();
00881 if ( noHtmlMode ) {
00882 sDescr = cleanHtml( sDescr );
00883 }
00884 }
00885 QString html = "<table border=\"0\" width=\"80%\" align=\"center\" "
00886 "cellpadding=\"1\" cellspacing=\"1\">";
00887 html += invitationRow( i18n( "Summary:" ), sSummary );
00888 html += invitationRow( i18n( "Date:" ),
00889 IncidenceFormatter::dateToString( journal->dtStart(), false,
00890 journal->dtStart().timeSpec() ) );
00891 html += invitationRow( i18n( "Description:" ), sDescr );
00892 html += "</table>\n";
00893 html += invitationsDetailsIncidence( journal, noHtmlMode );
00894
00895 return html;
00896 }
00897
00898 static QString invitationDetailsFreeBusy( FreeBusy *fb )
00899 {
00900 if ( !fb ) {
00901 return QString();
00902 }
00903
00904 QString html = "<table border=\"0\" width=\"80%\" align=\"center\" "
00905 "cellpadding=\"1\" cellspacing=\"1\">";
00906 html += invitationRow( i18n( "Person:" ), fb->organizer().fullName() );
00907 html += invitationRow( i18n( "Start date:" ),
00908 IncidenceFormatter::dateToString( fb->dtStart(),
00909 true,
00910 fb->dtStart().timeSpec() ) );
00911 html += invitationRow( i18n( "End date:" ),
00912 KGlobal::locale()->formatDate( fb->dtEnd().date(), KLocale::ShortDate ) );
00913 html += "<tr><td colspan=2><hr></td></tr>\n";
00914 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
00915
00916 QList<Period> periods = fb->busyPeriods();
00917 QList<Period>::iterator it;
00918 for ( it = periods.begin(); it != periods.end(); ++it ) {
00919 Period per = *it;
00920 if ( per.hasDuration() ) {
00921 int dur = per.duration().asSeconds();
00922 QString cont;
00923 if ( dur >= 3600 ) {
00924 cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
00925 dur %= 3600;
00926 }
00927 if ( dur >= 60 ) {
00928 cont += i18ncp( "minutes part of duration", "1 minute", "%1 minutes ", dur / 60 );
00929 dur %= 60;
00930 }
00931 if ( dur > 0 ) {
00932 cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
00933 }
00934 html += invitationRow(
00935 QString(), i18nc( "startDate for duration", "%1 for %2",
00936 KGlobal::locale()->formatDateTime(
00937 per.start().dateTime(), KLocale::LongDate ), cont ) );
00938 } else {
00939 QString cont;
00940 if ( per.start().date() == per.end().date() ) {
00941 cont = i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
00942 KGlobal::locale()->formatDate( per.start().date() ),
00943 KGlobal::locale()->formatTime( per.start().time() ),
00944 KGlobal::locale()->formatTime( per.end().time() ) );
00945 } else {
00946 cont = i18nc( "fromDateTime - toDateTime", "%1 - %2",
00947 KGlobal::locale()->formatDateTime(
00948 per.start().dateTime(), KLocale::LongDate ),
00949 KGlobal::locale()->formatDateTime(
00950 per.end().dateTime(), KLocale::LongDate ) );
00951 }
00952
00953 html += invitationRow( QString(), cont );
00954 }
00955 }
00956
00957 html += "</table>\n";
00958 return html;
00959 }
00960
00961 static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
00962 {
00963 if ( !msg || !event ) {
00964 return QString();
00965 }
00966
00967 switch ( msg->method() ) {
00968 case iTIPPublish:
00969 return i18n( "This event has been published" );
00970 case iTIPRequest:
00971 if ( event->revision() > 0 ) {
00972
00973 return i18n( "<h3>This meeting has been updated</h3>" );
00974 } else {
00975 return i18n( "You have been invited to this meeting" );
00976 }
00977 case iTIPRefresh:
00978 return i18n( "This invitation was refreshed" );
00979 case iTIPCancel:
00980 return i18n( "This meeting has been canceled" );
00981 case iTIPAdd:
00982 return i18n( "Addition to the meeting invitation" );
00983 case iTIPReply:
00984 {
00985 Attendee::List attendees = event->attendees();
00986 if( attendees.count() == 0 ) {
00987 kDebug() << "No attendees in the iCal reply!";
00988 return QString();
00989 }
00990 if ( attendees.count() != 1 ) {
00991 kDebug() << "Warning: attendeecount in the reply should be 1"
00992 << "but is" << attendees.count();
00993 }
00994 Attendee *attendee = *attendees.begin();
00995 QString attendeeName = attendee->name();
00996 if ( attendeeName.isEmpty() ) {
00997 attendeeName = attendee->email();
00998 }
00999 if ( attendeeName.isEmpty() ) {
01000 attendeeName = i18n( "Sender" );
01001 }
01002
01003 QString delegatorName, dummy;
01004 KPIMUtils::extractEmailAddressAndName( attendee->delegator(), dummy, delegatorName );
01005 if ( delegatorName.isEmpty() ) {
01006 delegatorName = attendee->delegator();
01007 }
01008
01009 switch( attendee->status() ) {
01010 case Attendee::NeedsAction:
01011 return i18n( "%1 indicates this invitation still needs some action", attendeeName );
01012 case Attendee::Accepted:
01013 if ( delegatorName.isEmpty() ) {
01014 return i18n( "%1 accepts this meeting invitation", attendeeName );
01015 }
01016 return i18n( "%1 accepts this meeting invitation on behalf of %2",
01017 attendeeName, delegatorName );
01018 case Attendee::Tentative:
01019 if ( delegatorName.isEmpty() ) {
01020 return i18n( "%1 tentatively accepts this meeting invitation", attendeeName );
01021 }
01022 return i18n( "%1 tentatively accepts this meeting invitation on behalf of %2",
01023 attendeeName, delegatorName );
01024 case Attendee::Declined:
01025 if ( delegatorName.isEmpty() ) {
01026 return i18n( "%1 declines this meeting invitation", attendeeName );
01027 }
01028 return i18n( "%1 declines this meeting invitation on behalf of %2",
01029 attendeeName, delegatorName );
01030 case Attendee::Delegated:
01031 {
01032 QString delegate, dummy;
01033 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
01034 if ( delegate.isEmpty() ) {
01035 delegate = attendee->delegate();
01036 }
01037 if ( !delegate.isEmpty() ) {
01038 return i18n( "%1 has delegated this meeting invitation to %2", attendeeName, delegate );
01039 }
01040 return i18n( "%1 has delegated this meeting invitation", attendeeName );
01041 }
01042 case Attendee::Completed:
01043 return i18n( "This meeting invitation is now completed" );
01044 case Attendee::InProcess:
01045 return i18n( "%1 is still processing the invitation", attendeeName );
01046 default:
01047 return i18n( "Unknown response to this meeting invitation" );
01048 }
01049 break;
01050 }
01051 case iTIPCounter:
01052 return i18n( "Sender makes this counter proposal" );
01053 case iTIPDeclineCounter:
01054 return i18n( "Sender declines the counter proposal" );
01055 case iTIPNoMethod:
01056 return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() );
01057 }
01058 return QString();
01059 }
01060
01061 static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
01062 {
01063 if ( !msg || !todo ) {
01064 return QString();
01065 }
01066
01067 switch ( msg->method() ) {
01068 case iTIPPublish:
01069 return i18n( "This to-do has been published" );
01070 case iTIPRequest:
01071 if ( todo->revision() > 0 ) {
01072 return i18n( "This to-do has been updated" );
01073 } else {
01074 return i18n( "You have been assigned this to-do" );
01075 }
01076 case iTIPRefresh:
01077 return i18n( "This to-do was refreshed" );
01078 case iTIPCancel:
01079 return i18n( "This to-do was canceled" );
01080 case iTIPAdd:
01081 return i18n( "Addition to the to-do" );
01082 case iTIPReply:
01083 {
01084 Attendee::List attendees = todo->attendees();
01085 if ( attendees.count() == 0 ) {
01086 kDebug() << "No attendees in the iCal reply!";
01087 return QString();
01088 }
01089 if ( attendees.count() != 1 ) {
01090 kDebug() << "Warning: attendeecount in the reply should be 1"
01091 << "but is" << attendees.count();
01092 }
01093 Attendee *attendee = *attendees.begin();
01094 switch( attendee->status() ) {
01095 case Attendee::NeedsAction:
01096 return i18n( "Sender indicates this to-do assignment still needs some action" );
01097 case Attendee::Accepted:
01098 return i18n( "Sender accepts this to-do" );
01099 case Attendee::Tentative:
01100 return i18n( "Sender tentatively accepts this to-do" );
01101 case Attendee::Declined:
01102 return i18n( "Sender declines this to-do" );
01103 case Attendee::Delegated:
01104 {
01105 QString delegate, dummy;
01106 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
01107 if ( delegate.isEmpty() ) {
01108 delegate = attendee->delegate();
01109 }
01110 if ( !delegate.isEmpty() ) {
01111 return i18n( "Sender has delegated this request for the to-do to %1", delegate );
01112 }
01113 return i18n( "Sender has delegated this request for the to-do " );
01114 }
01115 case Attendee::Completed:
01116 return i18n( "The request for this to-do is now completed" );
01117 case Attendee::InProcess:
01118 return i18n( "Sender is still processing the invitation" );
01119 default:
01120 return i18n( "Unknown response to this to-do" );
01121 }
01122 break;
01123 }
01124 case iTIPCounter:
01125 return i18n( "Sender makes this counter proposal" );
01126 case iTIPDeclineCounter:
01127 return i18n( "Sender declines the counter proposal" );
01128 case iTIPNoMethod:
01129 return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() );
01130 }
01131 return QString();
01132 }
01133
01134 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
01135 {
01136
01137 if ( !msg || !journal ) {
01138 return QString();
01139 }
01140
01141 switch ( msg->method() ) {
01142 case iTIPPublish:
01143 return i18n( "This journal has been published" );
01144 case iTIPRequest:
01145 return i18n( "You have been assigned this journal" );
01146 case iTIPRefresh:
01147 return i18n( "This journal was refreshed" );
01148 case iTIPCancel:
01149 return i18n( "This journal was canceled" );
01150 case iTIPAdd:
01151 return i18n( "Addition to the journal" );
01152 case iTIPReply:
01153 {
01154 Attendee::List attendees = journal->attendees();
01155 if ( attendees.count() == 0 ) {
01156 kDebug() << "No attendees in the iCal reply!";
01157 return QString();
01158 }
01159
01160 if( attendees.count() != 1 ) {
01161 kDebug() << "Warning: attendeecount in the reply should be 1"
01162 << "but is" << attendees.count();
01163 }
01164
01165 Attendee *attendee = *attendees.begin();
01166 switch( attendee->status() ) {
01167 case Attendee::NeedsAction:
01168 return i18n( "Sender indicates this journal assignment still needs some action" );
01169 case Attendee::Accepted:
01170 return i18n( "Sender accepts this journal" );
01171 case Attendee::Tentative:
01172 return i18n( "Sender tentatively accepts this journal" );
01173 case Attendee::Declined:
01174 return i18n( "Sender declines this journal" );
01175 case Attendee::Delegated:
01176 return i18n( "Sender has delegated this request for the journal" );
01177 case Attendee::Completed:
01178 return i18n( "The request for this journal is now completed" );
01179 case Attendee::InProcess:
01180 return i18n( "Sender is still processing the invitation" );
01181 default:
01182 return i18n( "Unknown response to this journal" );
01183 }
01184 break;
01185 }
01186 case iTIPCounter:
01187 return i18n( "Sender makes this counter proposal" );
01188 case iTIPDeclineCounter:
01189 return i18n( "Sender declines the counter proposal" );
01190 case iTIPNoMethod:
01191 return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() );
01192 }
01193 return QString();
01194 }
01195
01196 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01197 {
01198 if ( !msg || !fb ) {
01199 return QString();
01200 }
01201
01202 switch ( msg->method() ) {
01203 case iTIPPublish:
01204 return i18n( "This free/busy list has been published" );
01205 case iTIPRequest:
01206 return i18n( "The free/busy list has been requested" );
01207 case iTIPRefresh:
01208 return i18n( "This free/busy list was refreshed" );
01209 case iTIPCancel:
01210 return i18n( "This free/busy list was canceled" );
01211 case iTIPAdd:
01212 return i18n( "Addition to the free/busy list" );
01213 case iTIPNoMethod:
01214 default:
01215 return i18n( "Error: Free/Busy iMIP message with unknown method: '%1'", msg->method() );
01216 }
01217 }
01218
01219
01220
01221 class KCal::IncidenceFormatter::ScheduleMessageVisitor
01222 : public IncidenceBase::Visitor
01223 {
01224 public:
01225 ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
01226 bool act( IncidenceBase *incidence, ScheduleMessage *msg )
01227 {
01228 mMessage = msg;
01229 return incidence->accept( *this );
01230 }
01231 QString result() const { return mResult; }
01232
01233 protected:
01234 QString mResult;
01235 ScheduleMessage *mMessage;
01236 };
01237
01238 class KCal::IncidenceFormatter::InvitationHeaderVisitor :
01239 public IncidenceFormatter::ScheduleMessageVisitor
01240 {
01241 protected:
01242 bool visit( Event *event )
01243 {
01244 mResult = invitationHeaderEvent( event, mMessage );
01245 return !mResult.isEmpty();
01246 }
01247 bool visit( Todo *todo )
01248 {
01249 mResult = invitationHeaderTodo( todo, mMessage );
01250 return !mResult.isEmpty();
01251 }
01252 bool visit( Journal *journal )
01253 {
01254 mResult = invitationHeaderJournal( journal, mMessage );
01255 return !mResult.isEmpty();
01256 }
01257 bool visit( FreeBusy *fb )
01258 {
01259 mResult = invitationHeaderFreeBusy( fb, mMessage );
01260 return !mResult.isEmpty();
01261 }
01262 };
01263
01264 class KCal::IncidenceFormatter::InvitationBodyVisitor
01265 : public IncidenceFormatter::ScheduleMessageVisitor
01266 {
01267 public:
01268 InvitationBodyVisitor( bool noHtmlMode )
01269 : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ) { }
01270
01271 protected:
01272 bool visit( Event *event )
01273 {
01274 mResult = invitationDetailsEvent( event, mNoHtmlMode );
01275 return !mResult.isEmpty();
01276 }
01277 bool visit( Todo *todo )
01278 {
01279 mResult = invitationDetailsTodo( todo, mNoHtmlMode );
01280 return !mResult.isEmpty();
01281 }
01282 bool visit( Journal *journal )
01283 {
01284 mResult = invitationDetailsJournal( journal, mNoHtmlMode );
01285 return !mResult.isEmpty();
01286 }
01287 bool visit( FreeBusy *fb )
01288 {
01289 mResult = invitationDetailsFreeBusy( fb );
01290 return !mResult.isEmpty();
01291 }
01292
01293 private:
01294 bool mNoHtmlMode;
01295 };
01296
01297
01298 QString InvitationFormatterHelper::generateLinkURL( const QString &id )
01299 {
01300 return id;
01301 }
01302
01303
01304 class IncidenceFormatter::IncidenceCompareVisitor
01305 : public IncidenceBase::Visitor
01306 {
01307 public:
01308 IncidenceCompareVisitor() : mExistingIncidence( 0 ) {}
01309 bool act( IncidenceBase *incidence, Incidence *existingIncidence )
01310 {
01311 if ( !existingIncidence ) {
01312 return false;
01313 }
01314 Incidence *inc = dynamic_cast<Incidence *>( incidence );
01315 if ( inc && inc->revision() <= existingIncidence->revision() ) {
01316 return false;
01317 }
01318 mExistingIncidence = existingIncidence;
01319 return incidence->accept( *this );
01320 }
01321
01322 QString result() const
01323 {
01324 if ( mChanges.isEmpty() ) {
01325 return QString();
01326 }
01327 QString html = "<div align=\"left\"><ul><li>";
01328 html += mChanges.join( "</li><li>" );
01329 html += "</li><ul></div>";
01330 return html;
01331 }
01332
01333 protected:
01334 bool visit( Event *event )
01335 {
01336 compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
01337 compareIncidences( event, mExistingIncidence );
01338 return !mChanges.isEmpty();
01339 }
01340 bool visit( Todo *todo )
01341 {
01342 compareIncidences( todo, mExistingIncidence );
01343 return !mChanges.isEmpty();
01344 }
01345 bool visit( Journal *journal )
01346 {
01347 compareIncidences( journal, mExistingIncidence );
01348 return !mChanges.isEmpty();
01349 }
01350 bool visit( FreeBusy *fb )
01351 {
01352 Q_UNUSED( fb );
01353 return !mChanges.isEmpty();
01354 }
01355
01356 private:
01357 void compareEvents( Event *newEvent, Event *oldEvent )
01358 {
01359 if ( !oldEvent || !newEvent ) {
01360 return;
01361 }
01362 if ( oldEvent->dtStart() != newEvent->dtStart() ||
01363 oldEvent->allDay() != newEvent->allDay() ) {
01364 mChanges += i18n( "The begin of the meeting has been changed from %1 to %2",
01365 eventStartTimeStr( oldEvent ), eventStartTimeStr( newEvent ) );
01366 }
01367 if ( oldEvent->dtEnd() != newEvent->dtEnd() ||
01368 oldEvent->allDay() != newEvent->allDay() ) {
01369 mChanges += i18n( "The end of the meeting has been changed from %1 to %2",
01370 eventEndTimeStr( oldEvent ), eventEndTimeStr( newEvent ) );
01371 }
01372 }
01373
01374 void compareIncidences( Incidence *newInc, Incidence *oldInc )
01375 {
01376 if ( !oldInc || !newInc ) {
01377 return;
01378 }
01379
01380 if ( oldInc->summary() != newInc->summary() ) {
01381 mChanges += i18n( "The summary has been changed to: \"%1\"",
01382 newInc->richSummary() );
01383 }
01384
01385 if ( oldInc->location() != newInc->location() ) {
01386 mChanges += i18n( "The location has been changed to: \"%1\"",
01387 newInc->richLocation() );
01388 }
01389
01390 if ( oldInc->description() != newInc->description() ) {
01391 mChanges += i18n( "The description has been changed to: \"%1\"",
01392 newInc->richDescription() );
01393 }
01394
01395 Attendee::List oldAttendees = oldInc->attendees();
01396 Attendee::List newAttendees = newInc->attendees();
01397 for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
01398 it != newAttendees.constEnd(); ++it ) {
01399 Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
01400 if ( !oldAtt ) {
01401 mChanges += i18n( "Attendee %1 has been added", (*it)->fullName() );
01402 } else {
01403 if ( oldAtt->status() != (*it)->status() ) {
01404 mChanges += i18n( "The status of attendee %1 has been changed to: %2",
01405 (*it)->fullName(), (*it)->statusStr() );
01406 }
01407 }
01408 }
01409
01410 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
01411 it != oldAttendees.constEnd(); ++it ) {
01412 Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
01413 if ( !newAtt ) {
01414 mChanges += i18n( "Attendee %1 has been removed", (*it)->fullName() );
01415 }
01416 }
01417 }
01418
01419 private:
01420 Incidence *mExistingIncidence;
01421 QStringList mChanges;
01422 };
01423
01424
01425 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
01426 {
01427 QString res( "<a href=\"%1\"><b>%2</b></a>" );
01428 return res.arg( generateLinkURL( id ) ).arg( text );
01429 return res;
01430 }
01431
01432 Calendar *InvitationFormatterHelper::calendar() const
01433 {
01434 return 0;
01435 }
01436
01437
01438
01439
01440 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
01441 {
01442 CalendarResources* cal = dynamic_cast<CalendarResources*>( calendar );
01443 if ( !cal || !incidence ) {
01444 return true;
01445 }
01446
01447 ResourceCalendar *res = cal->resource( incidence );
01448 if ( !res ) {
01449 return true;
01450 }
01451
01452 const QString subRes = res->subresourceIdentifier( incidence );
01453 if ( !subRes.contains( "/.INBOX.directory/" ) ) {
01454 return false;
01455 }
01456 return true;
01457 }
01458
01459 static QString formatICalInvitationHelper( QString invitation, Calendar *mCalendar,
01460 InvitationFormatterHelper *helper, bool noHtmlMode )
01461 {
01462 if ( invitation.isEmpty() ) {
01463 return QString();
01464 }
01465
01466 ICalFormat format;
01467
01468
01469 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
01470
01471 if( !msg ) {
01472 kDebug() << "Failed to parse the scheduling message";
01473 Q_ASSERT( format.exception() );
01474 kDebug() << format.exception()->message();
01475 return QString();
01476 }
01477
01478 IncidenceBase *incBase = msg->event();
01479 incBase->shiftTimes( mCalendar->timeSpec(), KDateTime::Spec::LocalZone() );
01480
01481 Incidence *existingIncidence = 0;
01482 if ( helper->calendar() ) {
01483 existingIncidence = helper->calendar()->incidence( incBase->uid() );
01484 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
01485 existingIncidence = 0;
01486 }
01487 if ( !existingIncidence ) {
01488 const Incidence::List list = helper->calendar()->incidences();
01489 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
01490 if ( (*it)->schedulingID() == incBase->uid() &&
01491 incidenceOwnedByMe( helper->calendar(), *it ) ) {
01492 existingIncidence = *it;
01493 break;
01494 }
01495 }
01496 }
01497 }
01498
01499
01500 QString html;
01501 html += "<div align=\"center\" style=\"border:solid 1px; width:80%;\">";
01502 html += "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
01503
01504 IncidenceFormatter::InvitationHeaderVisitor headerVisitor;
01505
01506 if ( !headerVisitor.act( incBase, msg ) ) {
01507 return QString();
01508 }
01509 html += "<tr>" + eventViewerAddTag( "h3", headerVisitor.result() ) + "</tr>";
01510
01511 IncidenceFormatter::InvitationBodyVisitor bodyVisitor( noHtmlMode );
01512 if ( !bodyVisitor.act( incBase, msg ) ) {
01513 return QString();
01514 }
01515 html += bodyVisitor.result();
01516
01517 if ( msg->method() == iTIPRequest ) {
01518 IncidenceFormatter::IncidenceCompareVisitor compareVisitor;
01519 if ( compareVisitor.act( incBase, existingIncidence ) ) {
01520 html +=
01521 i18n( "<p align=\"left\">The following changes have been made by the organizer:</p>" );
01522 html += compareVisitor.result();
01523 }
01524 }
01525
01526
01527
01528 html += "<p>";
01529 html += "<table border=\"0\" align=\"center\" cellspacing=\"4\"><tr>";
01530
01531
01532
01533 const QString tdOpen = "<td style=\"border-width:0px;border-style:outset\">";
01534 const QString tdClose = "</td>";
01535 Incidence *incidence = dynamic_cast<Incidence*>( incBase );
01536 switch ( msg->method() ) {
01537 case iTIPPublish:
01538 case iTIPRequest:
01539 case iTIPRefresh:
01540 case iTIPAdd:
01541 {
01542 if ( incidence && incidence->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
01543 html += tdOpen;
01544 if ( incBase ) {
01545 if ( incBase->type() == "Todo" ) {
01546
01547 html += helper->makeLink( "reply", i18n( "[Enter this into my to-do list]" ) );
01548 } else {
01549 html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
01550 }
01551 }
01552 html += tdClose;
01553 }
01554
01555 if ( incidence && !existingIncidence ) {
01556
01557 html += tdOpen;
01558
01559 html += helper->makeLink( "accept", i18nc( "accept to-do request", "[Accept]" ) );
01560 html += tdClose;
01561
01562
01563 html += tdOpen;
01564
01565 html += helper->makeLink( "accept_conditionally",
01566 i18nc( "Accept conditionally", "[Accept cond.]" ) );
01567 html += tdClose;
01568
01569
01570 html += tdOpen;
01571
01572 html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
01573 html += tdClose;
01574
01575
01576 html += tdOpen;
01577
01578 html += helper->makeLink( "decline", i18nc( "decline to-do request", "[Decline]" ) );
01579 html += tdClose;
01580
01581
01582 html += tdOpen;
01583
01584 html += helper->makeLink( "delegate", i18nc( "delegate to-do to another", "[Delegate]" ) );
01585 html += tdClose;
01586
01587
01588 html += tdOpen;
01589
01590 html += helper->makeLink( "forward", i18nc( "forward request to another", "[Forward]" ) );
01591 html += tdClose;
01592
01593
01594 if ( incBase && incBase->type() == "Event" ) {
01595 html += tdOpen;
01596
01597
01598 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01599 html += tdClose;
01600 }
01601 }
01602 break;
01603 }
01604
01605 case iTIPCancel:
01606
01607 html += tdOpen;
01608
01609 html += helper->makeLink( "cancel", i18n( "[Remove this from my calendar]" ) );
01610 html += tdClose;
01611 break;
01612
01613 case iTIPReply:
01614
01615 html += tdOpen;
01616
01617
01618 if ( incBase ) {
01619 if ( incBase->type() == "Todo" ) {
01620 html += helper->makeLink( "reply", i18n( "[Enter this into my to-do list]" ) );
01621 } else {
01622 html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
01623 }
01624 }
01625 html += tdClose;
01626 break;
01627
01628 case iTIPCounter:
01629 html += tdOpen;
01630
01631 html += helper->makeLink( "accept_counter", i18n( "[Accept]" ) );
01632 html += tdClose;
01633
01634 html += tdOpen;
01635
01636 html += helper->makeLink( "decline_counter", i18n( "[Decline]" ) );
01637 html += tdClose;
01638
01639 html += tdOpen;
01640
01641
01642 html += helper->makeLink( "check_calendar", i18n( "[Check my calendar]" ) );
01643 html += tdClose;
01644 break;
01645
01646 case iTIPDeclineCounter:
01647 case iTIPNoMethod:
01648 break;
01649 }
01650
01651
01652 html += "</tr></table>";
01653
01654
01655 html += "</table></div>";
01656 kDebug() << html;
01657 return html;
01658 }
01659
01660
01661 QString IncidenceFormatter::formatICalInvitation( QString invitation, Calendar *mCalendar,
01662 InvitationFormatterHelper *helper )
01663 {
01664 return formatICalInvitationHelper( invitation, mCalendar, helper, false );
01665 }
01666
01667 QString IncidenceFormatter::formatICalInvitationNoHtml( QString invitation, Calendar *mCalendar,
01668 InvitationFormatterHelper *helper )
01669 {
01670 return formatICalInvitationHelper( invitation, mCalendar, helper, true );
01671 }
01672
01673
01674
01675
01676
01677
01678 class KCal::IncidenceFormatter::ToolTipVisitor
01679 : public IncidenceBase::Visitor
01680 {
01681 public:
01682 ToolTipVisitor()
01683 : mRichText( true ), mSpec( KDateTime::Spec() ), mResult( "" ) {}
01684
01685 bool act( IncidenceBase *incidence, bool richText=true, KDateTime::Spec spec=KDateTime::Spec() )
01686 {
01687 mRichText = richText;
01688 mSpec = spec;
01689 mResult = "";
01690 return incidence ? incidence->accept( *this ) : false;
01691 }
01692 QString result() const { return mResult; }
01693
01694 protected:
01695 bool visit( Event *event );
01696 bool visit( Todo *todo );
01697 bool visit( Journal *journal );
01698 bool visit( FreeBusy *fb );
01699
01700 QString dateRangeText( Event *event );
01701 QString dateRangeText( Todo *todo );
01702 QString dateRangeText( Journal *journal );
01703 QString dateRangeText( FreeBusy *fb );
01704
01705 QString generateToolTip( Incidence *incidence, QString dtRangeText );
01706
01707 protected:
01708 bool mRichText;
01709 KDateTime::Spec mSpec;
01710 QString mResult;
01711 };
01712
01713 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event )
01714 {
01715
01716 QString ret;
01717 QString tmp;
01718 if ( event->isMultiDay() ) {
01719
01720 tmp = IncidenceFormatter::dateToString( event->dtStart(), true, mSpec );
01721 ret += "<br>" + i18nc( "Event start", "<i>From:</i> %1", tmp );
01722
01723 tmp = IncidenceFormatter::dateToString( event->dtEnd(), true, mSpec );
01724 ret += "<br>" + i18nc( "Event end","<i>To:</i> %1", tmp );
01725
01726 } else {
01727
01728 ret += "<br>" +
01729 i18n( "<i>Date:</i> %1", IncidenceFormatter::dateToString( event->dtStart(), true, mSpec ) );
01730 if ( !event->allDay() ) {
01731 const QString dtStartTime = IncidenceFormatter::timeToString( event->dtStart(), true, mSpec );
01732 const QString dtEndTime = IncidenceFormatter::timeToString( event->dtEnd(), true, mSpec );
01733 if ( dtStartTime == dtEndTime ) {
01734
01735 tmp = "<br>" +
01736 i18nc( "time for event", "<i>Time:</i> %1",
01737 dtStartTime );
01738 } else {
01739 tmp = "<br>" +
01740 i18nc( "time range for event",
01741 "<i>Time:</i> %1 - %2",
01742 dtStartTime, dtEndTime );
01743 }
01744 ret += tmp;
01745 }
01746 }
01747 return ret.replace( ' ', " " );
01748 }
01749
01750 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo )
01751 {
01752
01753 QString ret;
01754 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
01755
01756
01757
01758 ret += "<br>" + i18n( "<i>Start:</i> %1",
01759 IncidenceFormatter::dateToString( todo->dtStart( false ), true, mSpec ) );
01760 }
01761 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
01762 ret += "<br>" + i18n( "<i>Due:</i> %1",
01763 IncidenceFormatter::dateTimeToString( todo->dtDue(),
01764 todo->allDay(),
01765 true, mSpec ) );
01766 }
01767 if ( todo->isCompleted() ) {
01768 ret += "<br>" +
01769 i18n( "<i>Completed:</i> %1", todo->completedStr() );
01770 } else {
01771 ret += "<br>" +
01772 i18nc( "percent complete", "%1 % completed", todo->percentComplete() );
01773 }
01774
01775 return ret.replace( ' ', " " );
01776 }
01777
01778 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal *journal )
01779 {
01780
01781 QString ret;
01782 if ( journal->dtStart().isValid() ) {
01783 ret += "<br>" +
01784 i18n( "<i>Date:</i> %1",
01785 IncidenceFormatter::dateToString( journal->dtStart(), false, mSpec ) );
01786 }
01787 return ret.replace( ' ', " " );
01788 }
01789
01790 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
01791 {
01792
01793 QString ret;
01794 ret = "<br>" +
01795 i18n( "<i>Period start:</i> %1",
01796 KGlobal::locale()->formatDateTime( fb->dtStart().dateTime() ) );
01797 ret += "<br>" +
01798 i18n( "<i>Period start:</i> %1",
01799 KGlobal::locale()->formatDateTime( fb->dtEnd().dateTime() ) );
01800 return ret.replace( ' ', " " );
01801 }
01802
01803 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
01804 {
01805 mResult = generateToolTip( event, dateRangeText( event ) );
01806 return !mResult.isEmpty();
01807 }
01808
01809 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
01810 {
01811 mResult = generateToolTip( todo, dateRangeText( todo ) );
01812 return !mResult.isEmpty();
01813 }
01814
01815 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
01816 {
01817 mResult = generateToolTip( journal, dateRangeText( journal ) );
01818 return !mResult.isEmpty();
01819 }
01820
01821 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
01822 {
01823
01824 mResult = "<qt><b>" + i18n( "Free/Busy information for %1", fb->organizer().fullName() ) + "</b>";
01825 mResult += dateRangeText( fb );
01826 mResult += "</qt>";
01827 return !mResult.isEmpty();
01828 }
01829
01830 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence *incidence,
01831 QString dtRangeText )
01832 {
01833
01834 if ( !incidence ) {
01835 return QString();
01836 }
01837
01838 QString tmp = "<qt><b>"+ incidence->richSummary() + "</b>";
01839
01840 tmp += dtRangeText;
01841
01842 if ( !incidence->location().isEmpty() ) {
01843
01844 tmp += "<br>" +
01845 i18n( "<i>Location:</i> %1", incidence->richLocation() );
01846 }
01847
01848 if ( !incidence->description().isEmpty() ) {
01849 QString desc( incidence->description() );
01850 if ( !incidence->descriptionIsRich() ) {
01851 if ( desc.length() > 120 ) {
01852 desc = desc.left( 120 ) + "...";
01853 }
01854 desc = Qt::escape( desc ).replace( '\n', "<br>" );
01855 } else {
01856
01857 }
01858 tmp += "<br>----------<br>" + i18n( "<i>Description:</i>" ) + "<br>" + desc;
01859 }
01860 tmp += "</qt>";
01861 return tmp;
01862 }
01863
01864
01865 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence,
01866 bool richText )
01867 {
01868 return toolTipStr( incidence, richText, KDateTime::Spec() );
01869 }
01870
01871 QString IncidenceFormatter::toolTipStr( IncidenceBase *incidence,
01872 bool richText, KDateTime::Spec spec )
01873 {
01874 ToolTipVisitor v;
01875 if ( v.act( incidence, richText, spec ) ) {
01876 return v.result();
01877 } else {
01878 return QString();
01879 }
01880 }
01881
01882
01883
01884
01885
01886
01887 static QString mailBodyIncidence( Incidence *incidence )
01888 {
01889 QString body;
01890 if ( !incidence->summary().isEmpty() ) {
01891 body += i18n( "Summary: %1\n", incidence->richSummary() );
01892 }
01893 if ( !incidence->organizer().isEmpty() ) {
01894 body += i18n( "Organizer: %1\n", incidence->organizer().fullName() );
01895 }
01896 if ( !incidence->location().isEmpty() ) {
01897 body += i18n( "Location: %1\n", incidence->richLocation() );
01898 }
01899 return body;
01900 }
01901
01902
01903
01904 class KCal::IncidenceFormatter::MailBodyVisitor
01905 : public IncidenceBase::Visitor
01906 {
01907 public:
01908 MailBodyVisitor()
01909 : mSpec( KDateTime::Spec() ), mResult( "" ) {}
01910
01911 bool act( IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec() )
01912 {
01913 mSpec = spec;
01914 mResult = "";
01915 return incidence ? incidence->accept( *this ) : false;
01916 }
01917 QString result() const
01918 {
01919 return mResult;
01920 }
01921
01922 protected:
01923 bool visit( Event *event );
01924 bool visit( Todo *todo );
01925 bool visit( Journal *journal );
01926 bool visit( FreeBusy * )
01927 {
01928 mResult = i18n( "This is a Free Busy Object" );
01929 return !mResult.isEmpty();
01930 }
01931 protected:
01932 KDateTime::Spec mSpec;
01933 QString mResult;
01934 };
01935
01936 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
01937 {
01938 QString recurrence[]= {
01939 i18nc( "no recurrence", "None" ),
01940 i18nc( "event recurs by minutes", "Minutely" ),
01941 i18nc( "event recurs by hours", "Hourly" ),
01942 i18nc( "event recurs by days", "Daily" ),
01943 i18nc( "event recurs by weeks", "Weekly" ),
01944 i18nc( "event recurs same position (e.g. first monday) each month", "Monthly Same Position" ),
01945 i18nc( "event recurs same day each month", "Monthly Same Day" ),
01946 i18nc( "event recurs same month each year", "Yearly Same Month" ),
01947 i18nc( "event recurs same day each year", "Yearly Same Day" ),
01948 i18nc( "event recurs same position (e.g. first monday) each year", "Yearly Same Position" )
01949 };
01950
01951 mResult = mailBodyIncidence( event );
01952 mResult += i18n( "Start Date: %1\n", IncidenceFormatter::dateToString( event->dtStart(), true,
01953 mSpec ) );
01954 if ( !event->allDay() ) {
01955 mResult += i18n( "Start Time: %1\n", IncidenceFormatter::timeToString( event->dtStart(),
01956 true, mSpec ) );
01957 }
01958 if ( event->dtStart() != event->dtEnd() ) {
01959 mResult += i18n( "End Date: %1\n", IncidenceFormatter::dateToString( event->dtEnd(), true,
01960 mSpec ) );
01961 }
01962 if ( !event->allDay() ) {
01963 mResult += i18n( "End Time: %1\n", IncidenceFormatter::timeToString( event->dtEnd(), true,
01964 mSpec ) );
01965 }
01966 if ( event->recurs() ) {
01967 Recurrence *recur = event->recurrence();
01968
01969 mResult += i18n( "Recurs: %1\n", recurrence[ recur->recurrenceType() ] );
01970 mResult += i18n( "Frequency: %1\n", event->recurrence()->frequency() );
01971
01972 if ( recur->duration() > 0 ) {
01973 mResult += i18np( "Repeats once", "Repeats %1 times", recur->duration() );
01974 mResult += '\n';
01975 } else {
01976 if ( recur->duration() != -1 ) {
01977
01978 QString endstr;
01979 if ( event->allDay() ) {
01980 endstr = KGlobal::locale()->formatDate( recur->endDate() );
01981 } else {
01982 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime().dateTime() );
01983 }
01984 mResult += i18n( "Repeat until: %1\n", endstr );
01985 } else {
01986 mResult += i18n( "Repeats forever\n" );
01987 }
01988 }
01989 }
01990
01991 QString details = event->richDescription();
01992 if ( !details.isEmpty() ) {
01993 mResult += i18n( "Details:\n%1\n", details );
01994 }
01995 return !mResult.isEmpty();
01996 }
01997
01998 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
01999 {
02000 mResult = mailBodyIncidence( todo );
02001
02002 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
02003 mResult += i18n( "Start Date: %1\n",
02004 IncidenceFormatter::dateToString( todo->dtStart(false), true, mSpec ) );
02005 if ( !todo->allDay() ) {
02006 mResult += i18n( "Start Time: %1\n",
02007 IncidenceFormatter::timeToString( todo->dtStart(false), true, mSpec ) );
02008 }
02009 }
02010 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
02011 mResult += i18n( "Due Date: %1\n",
02012 IncidenceFormatter::dateToString( todo->dtDue(), true, mSpec ) );
02013 if ( !todo->allDay() ) {
02014 mResult += i18n( "Due Time: %1\n",
02015 IncidenceFormatter::timeToString( todo->dtDue(), true, mSpec ) );
02016 }
02017 }
02018 QString details = todo->richDescription();
02019 if ( !details.isEmpty() ) {
02020 mResult += i18n( "Details:\n%1\n", details );
02021 }
02022 return !mResult.isEmpty();
02023 }
02024
02025 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
02026 {
02027 mResult = mailBodyIncidence( journal );
02028 mResult += i18n( "Date: %1\n", IncidenceFormatter::dateToString( journal->dtStart(), true,
02029 mSpec ) );
02030
02031 if ( !journal->allDay() ) {
02032 mResult += i18n( "Time: %1\n",
02033 IncidenceFormatter::timeToString( journal->dtStart(), true, mSpec ) );
02034
02035 }
02036 if ( !journal->description().isEmpty() ) {
02037 mResult += i18n( "Text of the journal:\n%1\n", journal->richDescription() );
02038 }
02039 return !mResult.isEmpty();
02040 }
02041
02042
02043 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
02044 {
02045 return mailBodyStr( incidence, KDateTime::Spec() );
02046 }
02047
02048 QString IncidenceFormatter::mailBodyStr( IncidenceBase *incidence,
02049 KDateTime::Spec spec )
02050 {
02051 if ( !incidence ) {
02052 return QString();
02053 }
02054
02055 MailBodyVisitor v;
02056 if ( v.act( incidence, spec ) ) {
02057 return v.result();
02058 }
02059 return QString();
02060 }
02061
02062
02063 static QString recurEnd( Incidence *incidence )
02064 {
02065 QString endstr;
02066 if ( incidence->allDay() ) {
02067 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
02068 } else {
02069 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
02070 }
02071 return endstr;
02072 }
02073
02074
02075 QString IncidenceFormatter::recurrenceString( Incidence *incidence )
02076 {
02077 if ( !incidence->recurs() ) {
02078 return i18n( "No recurrence" );
02079 }
02080 QStringList dayList;
02081 dayList.append( i18n( "31st Last" ) );
02082 dayList.append( i18n( "30th Last" ) );
02083 dayList.append( i18n( "29th Last" ) );
02084 dayList.append( i18n( "28th Last" ) );
02085 dayList.append( i18n( "27th Last" ) );
02086 dayList.append( i18n( "26th Last" ) );
02087 dayList.append( i18n( "25th Last" ) );
02088 dayList.append( i18n( "24th Last" ) );
02089 dayList.append( i18n( "23rd Last" ) );
02090 dayList.append( i18n( "22nd Last" ) );
02091 dayList.append( i18n( "21st Last" ) );
02092 dayList.append( i18n( "20th Last" ) );
02093 dayList.append( i18n( "19th Last" ) );
02094 dayList.append( i18n( "18th Last" ) );
02095 dayList.append( i18n( "17th Last" ) );
02096 dayList.append( i18n( "16th Last" ) );
02097 dayList.append( i18n( "15th Last" ) );
02098 dayList.append( i18n( "14th Last" ) );
02099 dayList.append( i18n( "13th Last" ) );
02100 dayList.append( i18n( "12th Last" ) );
02101 dayList.append( i18n( "11th Last" ) );
02102 dayList.append( i18n( "10th Last" ) );
02103 dayList.append( i18n( "9th Last" ) );
02104 dayList.append( i18n( "8th Last" ) );
02105 dayList.append( i18n( "7th Last" ) );
02106 dayList.append( i18n( "6th Last" ) );
02107 dayList.append( i18n( "5th Last" ) );
02108 dayList.append( i18n( "4th Last" ) );
02109 dayList.append( i18n( "3rd Last" ) );
02110 dayList.append( i18n( "2nd Last" ) );
02111 dayList.append( i18nc( "last day of the month", "Last" ) );
02112 dayList.append( i18nc( "unknown day of the month", "unknown" ) );
02113 dayList.append( i18n( "1st" ) );
02114 dayList.append( i18n( "2nd" ) );
02115 dayList.append( i18n( "3rd" ) );
02116 dayList.append( i18n( "4th" ) );
02117 dayList.append( i18n( "5th" ) );
02118 dayList.append( i18n( "6th" ) );
02119 dayList.append( i18n( "7th" ) );
02120 dayList.append( i18n( "8th" ) );
02121 dayList.append( i18n( "9th" ) );
02122 dayList.append( i18n( "10th" ) );
02123 dayList.append( i18n( "11th" ) );
02124 dayList.append( i18n( "12th" ) );
02125 dayList.append( i18n( "13th" ) );
02126 dayList.append( i18n( "14th" ) );
02127 dayList.append( i18n( "15th" ) );
02128 dayList.append( i18n( "16th" ) );
02129 dayList.append( i18n( "17th" ) );
02130 dayList.append( i18n( "18th" ) );
02131 dayList.append( i18n( "19th" ) );
02132 dayList.append( i18n( "20th" ) );
02133 dayList.append( i18n( "21st" ) );
02134 dayList.append( i18n( "22nd" ) );
02135 dayList.append( i18n( "23rd" ) );
02136 dayList.append( i18n( "24th" ) );
02137 dayList.append( i18n( "25th" ) );
02138 dayList.append( i18n( "26th" ) );
02139 dayList.append( i18n( "27th" ) );
02140 dayList.append( i18n( "28th" ) );
02141 dayList.append( i18n( "29th" ) );
02142 dayList.append( i18n( "30th" ) );
02143 dayList.append( i18n( "31st" ) );
02144 int weekStart = KGlobal::locale()->weekStartDay();
02145 QString dayNames;
02146 QString txt;
02147 const KCalendarSystem *calSys = KGlobal::locale()->calendar();
02148 Recurrence *recur = incidence->recurrence();
02149 switch ( recur->recurrenceType() ) {
02150 case Recurrence::rNone:
02151 return i18n( "No recurrence" );
02152 case Recurrence::rMinutely:
02153 if ( recur->duration() != -1 ) {
02154 txt = i18np( "Recurs every minute until %2",
02155 "Recurs every %1 minutes until %2",
02156 recur->frequency(), recurEnd( incidence ) );
02157 if ( recur->duration() > 0 ) {
02158 txt += i18nc( "number of occurrences",
02159 " (<numid>%1</numid> occurrences)",
02160 recur->duration() );
02161 }
02162 return txt;
02163 }
02164 return i18np( "Recurs every minute",
02165 "Recurs every %1 minutes", recur->frequency() );
02166 case Recurrence::rHourly:
02167 if ( recur->duration() != -1 ) {
02168 txt = i18np( "Recurs hourly until %2",
02169 "Recurs every %1 hours until %2",
02170 recur->frequency(), recurEnd( incidence ) );
02171 if ( recur->duration() > 0 ) {
02172 txt += i18nc( "number of occurrences",
02173 " (<numid>%1</numid> occurrences)",
02174 recur->duration() );
02175 }
02176 return txt;
02177 }
02178 return i18np( "Recurs hourly", "Recurs every %1 hours", recur->frequency() );
02179 case Recurrence::rDaily:
02180 if ( recur->duration() != -1 ) {
02181 txt = i18np( "Recurs daily until %2",
02182 "Recurs every %1 days until %2",
02183 recur->frequency(), recurEnd( incidence ) );
02184 if ( recur->duration() > 0 ) {
02185 txt += i18nc( "number of occurrences",
02186 " (<numid>%1</numid> occurrences)",
02187 recur->duration() );
02188 }
02189 return txt;
02190 }
02191 return i18np( "Recurs daily", "Recurs every %1 days", recur->frequency() );
02192 case Recurrence::rWeekly:
02193 {
02194 bool addSpace = false;
02195 for ( int i = 0; i < 7; ++i ) {
02196 if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
02197 if ( addSpace ) {
02198 dayNames.append( i18nc( "separator for list of days", ", " ) );
02199 }
02200 dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1,
02201 KCalendarSystem::ShortDayName ) );
02202 addSpace = true;
02203 }
02204 }
02205 if ( dayNames.isEmpty() ) {
02206 dayNames = i18nc( "Recurs weekly on no days", "no days" );
02207 }
02208 if ( recur->duration() != -1 ) {
02209 txt = i18ncp( "Recurs weekly on [list of days] until end-date",
02210 "Recurs weekly on %2 until %3",
02211 "Recurs every <numid>%1</numid> weeks on %2 until %3",
02212 recur->frequency(), dayNames, recurEnd( incidence ) );
02213 if ( recur->duration() > 0 ) {
02214 txt += i18nc( "number of occurrences",
02215 " (<numid>%1</numid> occurrences)",
02216 recur->duration() );
02217 }
02218 return txt;
02219 }
02220 return i18ncp( "Recurs weekly on [list of days]",
02221 "Recurs weekly on %2",
02222 "Recurs every <numid>%1</numid> weeks on %2",
02223 recur->frequency(), dayNames );
02224 }
02225 case Recurrence::rMonthlyPos:
02226 {
02227 KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0];
02228 if ( recur->duration() != -1 ) {
02229 txt = i18ncp( "Recurs every N months on the [2nd|3rd|...]"
02230 " weekdayname until end-date",
02231 "Recurs every month on the %2 %3 until %4",
02232 "Recurs every <numid>%1</numid> months on the %2 %3 until %4",
02233 recur->frequency(),
02234 dayList[rule.pos() + 31],
02235 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
02236 recurEnd( incidence ) );
02237 if ( recur->duration() > 0 ) {
02238 txt += i18nc( "number of occurrences",
02239 " (<numid>%1</numid> occurrences)",
02240 recur->duration() );
02241 }
02242 return txt;
02243 }
02244 return i18ncp( "Recurs every N months on the [2nd|3rd|...] weekdayname",
02245 "Recurs every month on the %2 %3",
02246 "Recurs every %1 months on the %2 %3",
02247 recur->frequency(),
02248 dayList[rule.pos() + 31],
02249 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ) );
02250 }
02251 case Recurrence::rMonthlyDay:
02252 {
02253 int days = recur->monthDays()[0];
02254 if ( recur->duration() != -1 ) {
02255 txt = i18ncp( "Recurs monthly on the [1st|2nd|...] day until end-date",
02256 "Recurs monthly on the %2 day until %3",
02257 "Recurs every %1 months on the %2 day until %3",
02258 recur->frequency(),
02259 dayList[days + 31],
02260 recurEnd( incidence ) );
02261 if ( recur->duration() > 0 ) {
02262 txt += i18nc( "number of occurrences",
02263 " (<numid>%1</numid> occurrences)",
02264 recur->duration() );
02265 }
02266 return txt;
02267 }
02268 return i18ncp( "Recurs monthly on the [1st|2nd|...] day",
02269 "Recurs monthly on the %2 day",
02270 "Recurs every <numid>%1</numid> month on the %2 day",
02271 recur->frequency(),
02272 dayList[days + 31] );
02273 }
02274 case Recurrence::rYearlyMonth:
02275 {
02276 if ( recur->duration() != -1 ) {
02277 if ( !recur->yearDates().isEmpty() ) {
02278 txt = i18ncp( "Recurs Every N years on month-name [1st|2nd|...]"
02279 " until end-date",
02280 "Recurs yearly on %2 %3 until %4",
02281 "Recurs every %1 years on %2 %3 until %4",
02282 recur->frequency(),
02283 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
02284 dayList[ recur->yearDates()[0] + 31 ],
02285 recurEnd( incidence ) );
02286 if ( recur->duration() > 0 ) {
02287 txt += i18nc( "number of occurrences",
02288 " (<numid>%1</numid> occurrences)",
02289 recur->duration() );
02290 }
02291 return txt;
02292 }
02293 }
02294 if ( !recur->yearDates().isEmpty() ) {
02295 return i18ncp( "Recurs Every N years on month-name [1st|2nd|...]",
02296 "Recurs yearly on %2 %3",
02297 "Recurs every %1 years on %2 %3",
02298 recur->frequency(),
02299 calSys->monthName( recur->yearMonths()[0],
02300 recur->startDate().year() ),
02301 dayList[ recur->yearDates()[0] + 31 ] );
02302 } else {
02303 if (!recur->yearMonths().isEmpty() ) {
02304 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
02305 "Recurs yearly on %1 %2",
02306 calSys->monthName( recur->yearMonths()[0],
02307 recur->startDate().year() ),
02308 dayList[ recur->startDate().day() + 31 ] );
02309 } else {
02310 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
02311 "Recurs yearly on %1 %2",
02312 calSys->monthName( recur->startDate().month(),
02313 recur->startDate().year() ),
02314 dayList[ recur->startDate().day() + 31 ] );
02315 }
02316 }
02317 }
02318 case Recurrence::rYearlyDay:
02319 if ( recur->duration() != -1 ) {
02320 txt = i18ncp( "Recurs every N years on day N until end-date",
02321 "Recurs every year on day <numid>%2</numid> until %3",
02322 "Recurs every <numid>%1</numid> years"
02323 " on day <numid>%2</numid> until %3",
02324 recur->frequency(),
02325 recur->yearDays()[0],
02326 recurEnd( incidence ) );
02327 if ( recur->duration() > 0 ) {
02328 txt += i18nc( "number of occurrences",
02329 " (<numid>%1</numid> occurrences)",
02330 recur->duration() );
02331 }
02332 return txt;
02333 }
02334 return i18ncp( "Recurs every N YEAR[S] on day N",
02335 "Recurs every year on day <numid>%2</numid>",
02336 "Recurs every <numid>%1</numid> years"
02337 " on day <numid>%2</numid>",
02338 recur->frequency(), recur->yearDays()[0] );
02339 case Recurrence::rYearlyPos:
02340 {
02341 KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0];
02342 if ( recur->duration() != -1 ) {
02343 txt = i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
02344 "of monthname until end-date",
02345 "Every year on the %2 %3 of %4 until %5",
02346 "Every <numid>%1</numid> years on the %2 %3 of %4"
02347 " until %5",
02348 recur->frequency(),
02349 dayList[rule.pos() + 31],
02350 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
02351 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
02352 recurEnd( incidence ) );
02353 if ( recur->duration() > 0 ) {
02354 txt += i18nc( "number of occurrences",
02355 " (<numid>%1</numid> occurrences)",
02356 recur->duration() );
02357 }
02358 return txt;
02359 }
02360 return i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
02361 "of monthname",
02362 "Every year on the %2 %3 of %4",
02363 "Every <numid>%1</numid> years on the %2 %3 of %4",
02364 recur->frequency(),
02365 dayList[rule.pos() + 31],
02366 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
02367 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) );
02368 }
02369 default:
02370 return i18n( "Incidence recurs" );
02371 }
02372 }
02373
02374 QString IncidenceFormatter::timeToString( const KDateTime &date,
02375 bool shortfmt,
02376 const KDateTime::Spec &spec )
02377 {
02378 if ( spec.isValid() ) {
02379
02380 QString timeZone;
02381 if ( spec.timeZone() != KSystemTimeZones::local() ) {
02382 timeZone = ' ' + spec.timeZone().name();
02383 }
02384
02385 return KGlobal::locale()->formatTime( date.toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
02386 } else {
02387 return KGlobal::locale()->formatTime( date.time(), !shortfmt );
02388 }
02389 }
02390
02391 QString IncidenceFormatter::dateToString( const KDateTime &date,
02392 bool shortfmt,
02393 const KDateTime::Spec &spec )
02394 {
02395 if ( spec.isValid() ) {
02396
02397 QString timeZone;
02398 if ( spec.timeZone() != KSystemTimeZones::local() ) {
02399 timeZone = ' ' + spec.timeZone().name();
02400 }
02401
02402 return
02403 KGlobal::locale()->formatDate( date.toTimeSpec( spec ).date(),
02404 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) +
02405 timeZone;
02406 } else {
02407 return
02408 KGlobal::locale()->formatDate( date.date(),
02409 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
02410 }
02411 }
02412
02413 QString IncidenceFormatter::dateTimeToString( const KDateTime &date,
02414 bool allDay,
02415 bool shortfmt,
02416 const KDateTime::Spec &spec )
02417 {
02418 if ( allDay ) {
02419 return dateToString( date, shortfmt, spec );
02420 }
02421
02422 if ( spec.isValid() ) {
02423 QString timeZone;
02424 if ( spec.timeZone() != KSystemTimeZones::local() ) {
02425 timeZone = ' ' + spec.timeZone().name();
02426 }
02427
02428 return KGlobal::locale()->formatDateTime(
02429 date.toTimeSpec( spec ).dateTime(),
02430 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
02431 } else {
02432 return KGlobal::locale()->formatDateTime(
02433 date.dateTime(),
02434 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
02435 }
02436 }