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