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