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