00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "incidenceformatter.h"
00025 #include "attachment.h"
00026 #include "event.h"
00027 #include "todo.h"
00028 #include "journal.h"
00029 #include "calendar.h"
00030 #include "calendarlocal.h"
00031 #include "icalformat.h"
00032 #include "freebusy.h"
00033
00034 #include "kpimutils/email.h"
00035 #include "kabc/phonenumber.h"
00036 #include "kabc/vcardconverter.h"
00037 #include "kabc/stdaddressbook.h"
00038
00039 #include <kdatetime.h>
00040 #include <kglobal.h>
00041 #include <kiconloader.h>
00042 #include <klocale.h>
00043
00044 #include <QtCore/QBuffer>
00045 #include <QtCore/QList>
00046 #include <QtGui/QTextDocument>
00047 #include <QtGui/QApplication>
00048
00049 #include <time.h>
00050
00051 using namespace KCal;
00052
00053
00054
00055
00056
00057 static QString eventViewerAddLink( const QString &ref, const QString &text,
00058 bool newline = true )
00059 {
00060 QString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
00061 if ( newline ) {
00062 tmpStr += '\n';
00063 }
00064 return tmpStr;
00065 }
00066
00067 static QString eventViewerAddTag( const QString &tag, const QString &text )
00068 {
00069 int numLineBreaks = text.count( "\n" );
00070 QString str = '<' + tag + '>';
00071 QString tmpText = text;
00072 QString tmpStr = str;
00073 if( numLineBreaks >= 0 ) {
00074 if ( numLineBreaks > 0 ) {
00075 int pos = 0;
00076 QString tmp;
00077 for ( int i = 0; i <= numLineBreaks; i++ ) {
00078 pos = tmpText.indexOf( "\n" );
00079 tmp = tmpText.left( pos );
00080 tmpText = tmpText.right( tmpText.length() - pos - 1 );
00081 tmpStr += tmp + "<br>";
00082 }
00083 } else {
00084 tmpStr += tmpText;
00085 }
00086 }
00087 tmpStr += "</" + tag + '>';
00088 return tmpStr;
00089 }
00090
00091 static QString eventViewerFormatCategories( Incidence *event )
00092 {
00093 QString tmpStr;
00094 if ( !event->categoriesStr().isEmpty() ) {
00095 if ( event->categories().count() == 1 ) {
00096 tmpStr = eventViewerAddTag( "h3", i18n( "Category" ) );
00097 } else {
00098 tmpStr = eventViewerAddTag( "h3", i18n( "Categories" ) );
00099 }
00100 tmpStr += eventViewerAddTag( "p", event->categoriesStr() );
00101 }
00102 return tmpStr;
00103 }
00104
00105 static QString linkPerson( const QString &email, QString name, QString uid,
00106 const QString &iconPath )
00107 {
00108
00109
00110 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00111 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00112 KABC::Addressee::List addressList = add_book->findByEmail( email );
00113 KABC::Addressee o = addressList.first();
00114 if ( !o.isEmpty() && addressList.size() < 2 ) {
00115 if ( name.isEmpty() ) {
00116
00117 name = o.formattedName();
00118 }
00119 uid = o.uid();
00120 } else {
00121
00122 uid.clear();
00123 }
00124 }
00125 kDebug(5800) << "formatAttendees: uid =" << uid;
00126
00127
00128 QString tmpString = "<li>";
00129 if ( !uid.isEmpty() ) {
00130
00131 if ( name.isEmpty() ) {
00132
00133 tmpString += eventViewerAddLink( "uid:" + uid, email );
00134 } else {
00135 tmpString += eventViewerAddLink( "uid:" + uid, name );
00136 }
00137 } else {
00138
00139 tmpString += ( name.isEmpty() ? email : name );
00140 }
00141 tmpString += '\n';
00142
00143
00144 if ( !email.isEmpty() && !iconPath.isNull() ) {
00145 KCal::Person person( name, email );
00146 KUrl mailto;
00147 mailto.setProtocol( "mailto" );
00148 mailto.setPath( person.fullName() );
00149 tmpString += eventViewerAddLink( mailto.url(), "<img src=\"" + iconPath + "\">" );
00150 }
00151 tmpString += "</li>\n";
00152
00153 return tmpString;
00154 }
00155
00156 static QString eventViewerFormatAttendees( Incidence *event )
00157 {
00158 QString tmpStr;
00159 Attendee::List attendees = event->attendees();
00160 if ( attendees.count() ) {
00161 KIconLoader *iconLoader = KIconLoader::global();
00162 const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
00163
00164
00165 tmpStr += eventViewerAddTag( "h4", i18n( "Organizer" ) );
00166 tmpStr += "<ul>";
00167 tmpStr += linkPerson( event->organizer().email(), event->organizer().name(),
00168 QString(), iconPath );
00169 tmpStr += "</ul>";
00170
00171
00172 tmpStr += eventViewerAddTag( "h4", i18n( "Attendees" ) );
00173 tmpStr += "<ul>";
00174 Attendee::List::ConstIterator it;
00175 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00176 Attendee *a = *it;
00177 tmpStr += linkPerson( a->email(), a->name(), a->uid(), iconPath );
00178 if ( !a->delegator().isEmpty() ) {
00179 tmpStr += i18n( " (delegated by %1)", a->delegator() );
00180 }
00181 if ( !a->delegate().isEmpty() ) {
00182 tmpStr += i18n( " (delegated to %1)", a->delegate() );
00183 }
00184 }
00185 tmpStr += "</ul>";
00186 }
00187 return tmpStr;
00188 }
00189
00190 static QString eventViewerFormatAttachments( Incidence *i )
00191 {
00192 QString tmpStr;
00193 Attachment::List as = i->attachments();
00194 if ( as.count() > 0 ) {
00195 Attachment::List::ConstIterator it;
00196 for ( it = as.begin(); it != as.end(); ++it ) {
00197 if ( (*it)->isUri() ) {
00198 tmpStr += eventViewerAddLink( (*it)->uri(), (*it)->label() );
00199 tmpStr += "<br>";
00200 }
00201 }
00202 }
00203 return tmpStr;
00204 }
00205
00206
00207
00208
00209
00210 static QString eventViewerFormatBirthday( Event *event )
00211 {
00212 if ( !event ) {
00213 return QString();
00214 }
00215 if ( event->customProperty( "KABC", "BIRTHDAY" ) != "YES" ) {
00216 return QString();
00217 }
00218
00219 QString uid_1 = event->customProperty( "KABC", "UID-1" );
00220 QString name_1 = event->customProperty( "KABC", "NAME-1" );
00221 QString email_1= event->customProperty( "KABC", "EMAIL-1" );
00222
00223 KIconLoader *iconLoader = KIconLoader::global();
00224 const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
00225
00226 QString tmpString = "<ul>";
00227 tmpString += linkPerson( email_1, name_1, uid_1, iconPath );
00228
00229 if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00230 QString uid_2 = event->customProperty( "KABC", "UID-2" );
00231 QString name_2 = event->customProperty( "KABC", "NAME-2" );
00232 QString email_2= event->customProperty( "KABC", "EMAIL-2" );
00233 tmpString += linkPerson( email_2, name_2, uid_2, iconPath );
00234 }
00235
00236 tmpString += "</ul>";
00237 return tmpString;
00238 }
00239
00240 static QString eventViewerFormatHeader( Incidence *incidence )
00241 {
00242 QString tmpStr = "<table><tr>";
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268 tmpStr += "<td>" + eventViewerAddTag( "h2", incidence->summary() ) + "</td>";
00269 tmpStr += "</tr></table><br>";
00270
00271 return tmpStr;
00272 }
00273
00274 static QString eventViewerFormatEvent( Event *event )
00275 {
00276 if ( !event ) {
00277 return QString();
00278 }
00279
00280 QString tmpStr = eventViewerFormatHeader( event );
00281
00282 tmpStr += "<table>";
00283 if ( !event->location().isEmpty() ) {
00284 tmpStr += "<tr>";
00285 tmpStr += "<td align=\"right\"><b>" + i18n( "Location" ) + "</b></td>";
00286 tmpStr += "<td>" + event->location() + "</td>";
00287 tmpStr += "</tr>";
00288 }
00289
00290 tmpStr += "<tr>";
00291 if ( event->allDay() ) {
00292 if ( event->isMultiDay() ) {
00293 tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00294 tmpStr += "<td>" + i18nc("<beginTime> - <endTime>","%1 - %2",
00295 event->dtStartDateStr( true, event->dtStart().timeSpec() ),
00296 event->dtEndDateStr( true, event->dtEnd().timeSpec() ) ) + "</td>";
00297 } else {
00298 tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
00299 tmpStr += "<td>" +
00300 i18nc( "date as string","%1",
00301 event->dtStartDateStr( true, event->dtStart().timeSpec() ) ) + "</td>";
00302 }
00303 } else {
00304 if ( event->isMultiDay() ) {
00305 tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00306 tmpStr += "<td>" +
00307 i18nc( "<beginTime> - <endTime>","%1 - %2",
00308 event->dtStartStr( true, event->dtStart().timeSpec() ),
00309 event->dtEndStr( true, event->dtEnd().timeSpec() ) ) + "</td>";
00310 } else {
00311 tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00312 if ( event->hasEndDate() && event->dtStart() != event->dtEnd() ) {
00313 tmpStr += "<td>" +
00314 i18nc( "<beginTime> - <endTime>","%1 - %2",
00315 event->dtStartTimeStr( true, event->dtStart().timeSpec() ),
00316 event->dtEndTimeStr( true, event->dtEnd().timeSpec() ) ) + "</td>";
00317 } else {
00318 tmpStr += "<td>" + event->dtStartTimeStr( true, event->dtStart().timeSpec() ) + "</td>";
00319 }
00320 tmpStr += "</tr><tr>";
00321 tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
00322 tmpStr += "<td>" +
00323 i18nc( "date as string","%1",
00324 event->dtStartDateStr( true, event->dtStart().timeSpec() ) ) + "</td>";
00325 }
00326 }
00327 tmpStr += "</tr>";
00328
00329 if ( event->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00330 tmpStr += "<tr>";
00331 tmpStr += "<td align=\"right\"><b>" + i18n( "Birthday" ) + "</b></td>";
00332 tmpStr += "<td>" + eventViewerFormatBirthday( event ) + "</td>";
00333 tmpStr += "</tr>";
00334 tmpStr += "</table>";
00335 return tmpStr;
00336 }
00337
00338 if ( !event->description().isEmpty() ) {
00339 tmpStr += "<tr>";
00340 tmpStr += "<td></td>";
00341 tmpStr += "<td>" + eventViewerAddTag( "p", event->description() ) + "</td>";
00342 tmpStr += "</tr>";
00343 }
00344
00345 if ( event->categories().count() > 0 ) {
00346 tmpStr += "<tr>";
00347 tmpStr += "<td align=\"right\"><b>";
00348 tmpStr += i18np( "1 category", "%1 categories", event->categories().count() ) +
00349 "</b></td>";
00350 tmpStr += "<td>" + event->categoriesStr() + "</td>";
00351 tmpStr += "</tr>";
00352 }
00353
00354 if ( event->recurs() ) {
00355 KDateTime dt = event->recurrence()->getNextDateTime( KDateTime::currentUtcDateTime() );
00356 tmpStr += "<tr>";
00357 tmpStr += "<td align=\"right\"><b>" + i18n( "Next Occurrence" )+ "</b></td>";
00358 tmpStr += "<td>" +
00359 KGlobal::locale()->formatDateTime( dt.dateTime(), KLocale::ShortDate ) + "</td>";
00360 tmpStr += "</tr>";
00361 }
00362
00363 tmpStr += "<tr><td colspan=\"2\">";
00364 tmpStr += eventViewerFormatAttendees( event );
00365 tmpStr += "</td></tr>";
00366
00367 int attachmentCount = event->attachments().count();
00368 if ( attachmentCount > 0 ) {
00369 tmpStr += "<tr>";
00370 tmpStr += "<td align=\"right\"><b>";
00371 tmpStr += i18np( "1 attachment", "%1 attachments", attachmentCount )+ "</b></td>";
00372 tmpStr += "<td>" + eventViewerFormatAttachments( event ) + "</td>";
00373 tmpStr += "</tr>";
00374 }
00375
00376 tmpStr += "</table>";
00377 tmpStr += "<p><em>" +
00378 i18n( "Creation date: %1", KGlobal::locale()->formatDateTime(
00379 event->created().dateTime(), KLocale::ShortDate ) ) + "</em>";
00380 return tmpStr;
00381 }
00382
00383 static QString eventViewerFormatTodo( Todo *todo )
00384 {
00385 if ( !todo ) {
00386 return QString();
00387 }
00388
00389 QString tmpStr = eventViewerFormatHeader( todo );
00390
00391 if ( !todo->location().isEmpty() ) {
00392 tmpStr += eventViewerAddTag( "b", i18n(" Location: %1", todo->location() ) );
00393 tmpStr += "<br>";
00394 }
00395
00396 if ( todo->hasDueDate() ) {
00397 tmpStr += i18n( "<b>Due on:</b> %1", todo->dtDueStr( true, todo->dtStart().timeSpec() ) );
00398 }
00399
00400 if ( !todo->description().isEmpty() ) {
00401 tmpStr += eventViewerAddTag( "p", todo->description() );
00402 }
00403
00404 tmpStr += eventViewerFormatCategories( todo );
00405
00406 if ( todo->priority() > 0 ) {
00407 tmpStr += i18n( "<p><b>Priority:</b> %1</p>", todo->priority() );
00408 } else {
00409 tmpStr += i18n( "<p><b>Priority:</b> %1</p>", i18n( "Unspecified" ) );
00410 }
00411
00412 tmpStr += i18n( "<p><i>%1 % completed</i></p>", todo->percentComplete() );
00413
00414 if ( todo->recurs() ) {
00415 KDateTime dt = todo->recurrence()->getNextDateTime( KDateTime::currentUtcDateTime() );
00416 tmpStr += eventViewerAddTag( "p", "<em>" +
00417 i18n( "This is a recurring to-do. The next occurrence will be on %1.",
00418 KGlobal::locale()->formatDateTime( dt.dateTime(), KLocale::ShortDate ) ) + "</em>" );
00419 }
00420 tmpStr += eventViewerFormatAttendees( todo );
00421 tmpStr += eventViewerFormatAttachments( todo );
00422 tmpStr += "<p><em>" + i18n( "Creation date: %1",
00423 KGlobal::locale()->formatDateTime( todo->created().dateTime(), KLocale::ShortDate ) ) + "</em>";
00424 return tmpStr;
00425 }
00426
00427 static QString eventViewerFormatJournal( Journal *journal )
00428 {
00429 if ( !journal ) {
00430 return QString();
00431 }
00432
00433 QString tmpStr;
00434 if ( !journal->summary().isEmpty() ) {
00435 tmpStr+= eventViewerAddTag( "h2", journal->summary() );
00436 }
00437 tmpStr += eventViewerAddTag(
00438 "h3", i18n( "Journal for %1",
00439 journal->dtStartDateStr( false, journal->dtStart().timeSpec() ) ) );
00440 if ( !journal->description().isEmpty() ) {
00441 tmpStr += eventViewerAddTag( "p", journal->description() );
00442 }
00443 return tmpStr;
00444 }
00445
00446 static QString eventViewerFormatFreeBusy( FreeBusy *fb )
00447 {
00448 if ( !fb ) {
00449 return QString();
00450 }
00451
00452 QString tmpStr(
00453 eventViewerAddTag(
00454 "h2", i18n( "Free/Busy information for %1", fb->organizer().fullName() ) ) );
00455 tmpStr += eventViewerAddTag(
00456 "h4", i18n( "Busy times in date range %1 - %2:",
00457 KGlobal::locale()->formatDate( fb->dtStart().date(), KLocale::ShortDate ),
00458 KGlobal::locale()->formatDate( fb->dtEnd().date(), KLocale::ShortDate ) ) );
00459
00460 QList<Period> periods = fb->busyPeriods();
00461
00462 QString text =
00463 eventViewerAddTag( "em",
00464 eventViewerAddTag( "b", i18nc( "tag for busy periods list", "Busy:" ) ) );
00465
00466 QList<Period>::iterator it;
00467 for ( it = periods.begin(); it != periods.end(); ++it ) {
00468 Period per = *it;
00469 if ( per.hasDuration() ) {
00470 int dur = per.duration().asSeconds();
00471 QString cont;
00472 if ( dur >= 3600 ) {
00473 cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
00474 dur %= 3600;
00475 }
00476 if ( dur >= 60 ) {
00477 cont += i18ncp( "minutes part duration", "1 minute ", "%1 minutes ", dur / 60 );
00478 dur %= 60;
00479 }
00480 if ( dur > 0 ) {
00481 cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
00482 }
00483 text += i18nc( "startDate for duration", "%1 for %2",
00484 KGlobal::locale()->formatDateTime(
00485 per.start().dateTime(), KLocale::LongDate ), cont );
00486 text += "<br>";
00487 } else {
00488 if ( per.start().date() == per.end().date() ) {
00489 text += i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
00490 KGlobal::locale()->formatDate( per.start().date() ),
00491 KGlobal::locale()->formatTime( per.start().time() ),
00492 KGlobal::locale()->formatTime( per.end().time() ) );
00493 } else {
00494 text += i18nc( "fromDateTime - toDateTime", "%1 - %2",
00495 KGlobal::locale()->formatDateTime(
00496 per.start().dateTime(), KLocale::LongDate ),
00497 KGlobal::locale()->formatDateTime(
00498 per.end().dateTime(), KLocale::LongDate ) );
00499 }
00500 text += "<br>";
00501 }
00502 }
00503 tmpStr += eventViewerAddTag( "p", text );
00504 return tmpStr;
00505 }
00506
00507
00508 class KCal::IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor
00509 {
00510 public:
00511 EventViewerVisitor() { mResult = ""; }
00512 bool act( IncidenceBase *incidence ) { return incidence->accept( *this ); }
00513 QString result() const { return mResult; }
00514 protected:
00515 bool visit( Event *event )
00516 {
00517 mResult = eventViewerFormatEvent( event );
00518 return !mResult.isEmpty();
00519 }
00520 bool visit( Todo *todo )
00521 {
00522 mResult = eventViewerFormatTodo( todo );
00523 return !mResult.isEmpty();
00524 }
00525 bool visit( Journal *journal )
00526 {
00527 mResult = eventViewerFormatJournal( journal );
00528 return !mResult.isEmpty();
00529 }
00530 bool visit( FreeBusy *fb )
00531 {
00532 mResult = eventViewerFormatFreeBusy( fb );
00533 return !mResult.isEmpty();
00534 }
00535
00536 protected:
00537 QString mResult;
00538 };
00539
00540
00541 QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
00542 {
00543 if ( !incidence ) {
00544 return QString();
00545 }
00546
00547 EventViewerVisitor v;
00548 if ( v.act( incidence ) ) {
00549 return v.result();
00550 } else {
00551 return QString();
00552 }
00553 }
00554
00555
00556
00557
00558
00559 static QString string2HTML( const QString &str )
00560 {
00561 return Qt::convertFromPlainText( str, Qt::WhiteSpaceNormal );
00562 }
00563
00564 static QString invitationRow( const QString &cell1, const QString &cell2 )
00565 {
00566 return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
00567 }
00568
00569 static QString invitationDetailsEvent( Event *event )
00570 {
00571
00572 if ( !event ) {
00573 return QString();
00574 }
00575
00576 QString html;
00577 QString tmp;
00578
00579 QString sSummary = i18n( "Summary unspecified" );
00580 if ( ! event->summary().isEmpty() ) {
00581 sSummary = string2HTML( event->summary() );
00582 }
00583
00584 QString sLocation = i18n( "Location unspecified" );
00585 if ( ! event->location().isEmpty() ) {
00586 sLocation = string2HTML( event->location() );
00587 }
00588
00589 QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" );
00590 html = QString( "<div dir=\"%1\">\n" ).arg( dir );
00591 html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
00592
00593
00594 html += invitationRow( i18n( "What:" ), sSummary );
00595 html += invitationRow( i18n( "Where:" ), sLocation );
00596
00597
00598 if ( ! event->allDay() ) {
00599 tmp = i18nc( "%1: Start Date, %2: Start Time", "%1 %2",
00600 event->dtStartDateStr(), event->dtStartTimeStr() );
00601 } else {
00602 tmp = i18nc( "%1: Start Date", "%1 (time unspecified)",
00603 event->dtStartDateStr( true, event->dtStart().timeSpec() ) );
00604 }
00605 html += invitationRow( i18n( "Start Time:" ), tmp );
00606
00607
00608 if ( event->hasEndDate() ) {
00609 if ( ! event->allDay() ) {
00610 tmp = i18nc( "%1: End Date, %2: End Time", "%1 %2",
00611 event->dtEndDateStr( true, event->dtEnd().timeSpec() ),
00612 event->dtEndTimeStr( true, event->dtEnd().timeSpec() ) );
00613 } else {
00614 tmp = i18nc( "%1: End Date", "%1 (time unspecified)",
00615 event->dtEndDateStr( true, event->dtEnd().timeSpec() ) );
00616 }
00617 } else {
00618 tmp = i18n( "Unspecified" );
00619 }
00620 html += invitationRow( i18n( "End Time:" ), tmp );
00621
00622
00623 if ( !event->allDay() && event->hasEndDate() ) {
00624 tmp.clear();
00625 QTime sDuration( 0, 0, 0 ), t;
00626 int secs = event->dtStart().secsTo( event->dtEnd() );
00627 t = sDuration.addSecs( secs );
00628 if ( t.hour() > 0 ) {
00629 tmp += i18np( "1 hour ", "%1 hours ", t.hour() );
00630 }
00631 if ( t.minute() > 0 ) {
00632 tmp += i18np( "1 minute ", "%1 minutes ", t.minute() );
00633 }
00634
00635 html += invitationRow( i18n( "Duration:" ), tmp );
00636 }
00637
00638 html += "</table>\n";
00639 html += "</div>\n";
00640
00641 return html;
00642 }
00643
00644 static QString invitationDetailsTodo( Todo *todo )
00645 {
00646
00647 if ( !todo ) {
00648 return QString();
00649 }
00650
00651 QString sSummary = i18n( "Summary unspecified" );
00652 QString sDescr = i18n( "Description unspecified" );
00653 if ( ! todo->summary().isEmpty() ) {
00654 sSummary = todo->summary();
00655 }
00656 if ( ! todo->description().isEmpty() ) {
00657 sDescr = todo->description();
00658 }
00659 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00660 html += invitationRow( i18n( "Summary:" ), sSummary );
00661 html += invitationRow( i18n( "Description:" ), sDescr );
00662 html += "</table>\n";
00663
00664 return html;
00665 }
00666
00667 static QString invitationDetailsJournal( Journal *journal )
00668 {
00669 if ( !journal ) {
00670 return QString();
00671 }
00672
00673 QString sSummary = i18n( "Summary unspecified" );
00674 QString sDescr = i18n( "Description unspecified" );
00675 if ( ! journal->summary().isEmpty() ) {
00676 sSummary = journal->summary();
00677 }
00678 if ( ! journal->description().isEmpty() ) {
00679 sDescr = journal->description();
00680 }
00681 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00682 html += invitationRow( i18n( "Summary:" ), sSummary );
00683 html += invitationRow( i18n( "Date:" ),
00684 journal->dtStartDateStr( false, journal->dtStart().timeSpec() ) );
00685 html += invitationRow( i18n( "Description:" ), sDescr );
00686 html += "</table>\n";
00687
00688 return html;
00689 }
00690
00691 static QString invitationDetailsFreeBusy( FreeBusy *fb )
00692 {
00693 if ( !fb ) {
00694 return QString();
00695 }
00696
00697 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00698 html += invitationRow( i18n( "Person:" ), fb->organizer().fullName() );
00699 html += invitationRow( i18n( "Start date:" ),
00700 fb->dtStartDateStr( true, fb->dtStart().timeSpec() ) );
00701 html += invitationRow( i18n( "End date:" ),
00702 KGlobal::locale()->formatDate( fb->dtEnd().date(), KLocale::ShortDate ) );
00703 html += "<tr><td colspan=2><hr></td></tr>\n";
00704 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
00705
00706 QList<Period> periods = fb->busyPeriods();
00707 QList<Period>::iterator it;
00708 for ( it = periods.begin(); it != periods.end(); ++it ) {
00709 Period per = *it;
00710 if ( per.hasDuration() ) {
00711 int dur = per.duration().asSeconds();
00712 QString cont;
00713 if ( dur >= 3600 ) {
00714 cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
00715 dur %= 3600;
00716 }
00717 if ( dur >= 60 ) {
00718 cont += i18ncp( "minutes part of duration", "1 minute", "%1 minutes ", dur / 60 );
00719 dur %= 60;
00720 }
00721 if ( dur > 0 ) {
00722 cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
00723 }
00724 html += invitationRow(
00725 QString(), i18nc( "startDate for duration", "%1 for %2",
00726 KGlobal::locale()->formatDateTime(
00727 per.start().dateTime(), KLocale::LongDate ), cont ) );
00728 } else {
00729 QString cont;
00730 if ( per.start().date() == per.end().date() ) {
00731 cont = i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
00732 KGlobal::locale()->formatDate( per.start().date() ),
00733 KGlobal::locale()->formatTime( per.start().time() ),
00734 KGlobal::locale()->formatTime( per.end().time() ) );
00735 } else {
00736 cont = i18nc( "fromDateTime - toDateTime", "%1 - %2",
00737 KGlobal::locale()->formatDateTime(
00738 per.start().dateTime(), KLocale::LongDate ),
00739 KGlobal::locale()->formatDateTime(
00740 per.end().dateTime(), KLocale::LongDate ) );
00741 }
00742
00743 html += invitationRow( QString(), cont );
00744 }
00745 }
00746
00747 html += "</table>\n";
00748 return html;
00749 }
00750
00751 static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
00752 {
00753 if ( !msg || !event ) {
00754 return QString();
00755 }
00756
00757 switch ( msg->method() ) {
00758 case iTIPPublish:
00759 return i18n( "This event has been published" );
00760 case iTIPRequest:
00761 if ( event->revision() > 0 ) {
00762 return i18n( "<h3>This meeting has been updated</h3>" );
00763 } else {
00764 return i18n( "You have been invited to this meeting" );
00765 }
00766 case iTIPRefresh:
00767 return i18n( "This invitation was refreshed" );
00768 case iTIPCancel:
00769 return i18n( "This meeting has been canceled" );
00770 case iTIPAdd:
00771 return i18n( "Addition to the meeting invitation" );
00772 case iTIPReply:
00773 {
00774 Attendee::List attendees = event->attendees();
00775 if( attendees.count() == 0 ) {
00776 kDebug(5800) << "No attendees in the iCal reply!";
00777 return QString();
00778 }
00779 if ( attendees.count() != 1 ) {
00780 kDebug(5800) << "Warning: attendeecount in the reply should be 1"
00781 << "but is" << attendees.count();
00782 }
00783 Attendee *attendee = *attendees.begin();
00784 QString attendeeName = attendee->name();
00785 if ( attendeeName.isEmpty() ) {
00786 attendeeName = attendee->email();
00787 }
00788 if ( attendeeName.isEmpty() ) {
00789 attendeeName = i18n( "Sender" );
00790 }
00791
00792 QString delegatorName, dummy;
00793 KPIMUtils::extractEmailAddressAndName( attendee->delegator(), dummy, delegatorName );
00794 if ( delegatorName.isEmpty() ) {
00795 delegatorName = attendee->delegator();
00796 }
00797
00798 switch( attendee->status() ) {
00799 case Attendee::NeedsAction:
00800 return i18n( "%1 indicates this invitation still needs some action", attendeeName );
00801 case Attendee::Accepted:
00802 if ( delegatorName.isEmpty() ) {
00803 return i18n( "%1 accepts this meeting invitation", attendeeName );
00804 }
00805 return i18n( "%1 accepts this meeting invitation on behalf of %2",
00806 attendeeName, delegatorName );
00807 case Attendee::Tentative:
00808 if ( delegatorName.isEmpty() ) {
00809 return i18n( "%1 tentatively accepts this meeting invitation", attendeeName );
00810 }
00811 return i18n( "%1 tentatively accepts this meeting invitation on behalf of %2",
00812 attendeeName, delegatorName );
00813 case Attendee::Declined:
00814 if ( delegatorName.isEmpty() ) {
00815 return i18n( "%1 declines this meeting invitation", attendeeName );
00816 }
00817 return i18n( "%1 declines this meeting invitation on behalf of %2",
00818 attendeeName, delegatorName );
00819 case Attendee::Delegated:
00820 {
00821 QString delegate, dummy;
00822 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
00823 if ( delegate.isEmpty() ) {
00824 delegate = attendee->delegate();
00825 }
00826 if ( !delegate.isEmpty() ) {
00827 return i18n( "%1 has delegated this meeting invitation to %2", attendeeName, delegate );
00828 }
00829 return i18n( "%1 has delegated this meeting invitation", attendeeName );
00830 }
00831 case Attendee::Completed:
00832 return i18n( "This meeting invitation is now completed" );
00833 case Attendee::InProcess:
00834 return i18n( "%1 is still processing the invitation", attendeeName );
00835 default:
00836 return i18n( "Unknown response to this meeting invitation" );
00837 }
00838 break;
00839 }
00840 case iTIPCounter:
00841 return i18n( "Sender makes this counter proposal" );
00842 case iTIPDeclineCounter:
00843 return i18n( "Sender declines the counter proposal" );
00844 case iTIPNoMethod:
00845 return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() );
00846 }
00847 return QString();
00848 }
00849
00850 static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
00851 {
00852 if ( !msg || !todo ) {
00853 return QString();
00854 }
00855
00856 switch ( msg->method() ) {
00857 case iTIPPublish:
00858 return i18n( "This to-do has been published" );
00859 case iTIPRequest:
00860 if ( todo->revision() > 0 ) {
00861 return i18n( "This to-do has been updated" );
00862 } else {
00863 return i18n( "You have been assigned this to-do" );
00864 }
00865 case iTIPRefresh:
00866 return i18n( "This to-do was refreshed" );
00867 case iTIPCancel:
00868 return i18n( "This to-do was canceled" );
00869 case iTIPAdd:
00870 return i18n( "Addition to the to-do" );
00871 case iTIPReply:
00872 {
00873 Attendee::List attendees = todo->attendees();
00874 if ( attendees.count() == 0 ) {
00875 kDebug(5800) << "No attendees in the iCal reply!";
00876 return QString();
00877 }
00878 if ( attendees.count() != 1 ) {
00879 kDebug(5800) << "Warning: attendeecount in the reply should be 1"
00880 << "but is" << attendees.count();
00881 }
00882 Attendee *attendee = *attendees.begin();
00883 switch( attendee->status() ) {
00884 case Attendee::NeedsAction:
00885 return i18n( "Sender indicates this to-do assignment still needs some action" );
00886 case Attendee::Accepted:
00887 return i18n( "Sender accepts this to-do" );
00888 case Attendee::Tentative:
00889 return i18n( "Sender tentatively accepts this to-do" );
00890 case Attendee::Declined:
00891 return i18n( "Sender declines this to-do" );
00892 case Attendee::Delegated:
00893 {
00894 QString delegate, dummy;
00895 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
00896 if ( delegate.isEmpty() ) {
00897 delegate = attendee->delegate();
00898 }
00899 if ( !delegate.isEmpty() ) {
00900 return i18n( "Sender has delegated this request for the to-do to %1", delegate );
00901 }
00902 return i18n( "Sender has delegated this request for the to-do " );
00903 }
00904 case Attendee::Completed:
00905 return i18n( "The request for this to-do is now completed" );
00906 case Attendee::InProcess:
00907 return i18n( "Sender is still processing the invitation" );
00908 default:
00909 return i18n( "Unknown response to this to-do" );
00910 }
00911 break;
00912 }
00913 case iTIPCounter:
00914 return i18n( "Sender makes this counter proposal" );
00915 case iTIPDeclineCounter:
00916 return i18n( "Sender declines the counter proposal" );
00917 case iTIPNoMethod:
00918 return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() );
00919 }
00920 return QString();
00921 }
00922
00923 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
00924 {
00925
00926 if ( !msg || !journal ) {
00927 return QString();
00928 }
00929
00930 switch ( msg->method() ) {
00931 case iTIPPublish:
00932 return i18n( "This journal has been published" );
00933 case iTIPRequest:
00934 return i18n( "You have been assigned this journal" );
00935 case iTIPRefresh:
00936 return i18n( "This journal was refreshed" );
00937 case iTIPCancel:
00938 return i18n( "This journal was canceled" );
00939 case iTIPAdd:
00940 return i18n( "Addition to the journal" );
00941 case iTIPReply:
00942 {
00943 Attendee::List attendees = journal->attendees();
00944 if ( attendees.count() == 0 ) {
00945 kDebug(5800) << "No attendees in the iCal reply!";
00946 return QString();
00947 }
00948
00949 if( attendees.count() != 1 ) {
00950 kDebug(5800) << "Warning: attendeecount in the reply should be 1"
00951 << "but is" << attendees.count();
00952 }
00953
00954 Attendee *attendee = *attendees.begin();
00955 switch( attendee->status() ) {
00956 case Attendee::NeedsAction:
00957 return i18n( "Sender indicates this journal assignment still needs some action" );
00958 case Attendee::Accepted:
00959 return i18n( "Sender accepts this journal" );
00960 case Attendee::Tentative:
00961 return i18n( "Sender tentatively accepts this journal" );
00962 case Attendee::Declined:
00963 return i18n( "Sender declines this journal" );
00964 case Attendee::Delegated:
00965 return i18n( "Sender has delegated this request for the journal" );
00966 case Attendee::Completed:
00967 return i18n( "The request for this journal is now completed" );
00968 case Attendee::InProcess:
00969 return i18n( "Sender is still processing the invitation" );
00970 default:
00971 return i18n( "Unknown response to this journal" );
00972 }
00973 break;
00974 }
00975 case iTIPCounter:
00976 return i18n( "Sender makes this counter proposal" );
00977 case iTIPDeclineCounter:
00978 return i18n( "Sender declines the counter proposal" );
00979 case iTIPNoMethod:
00980 return i18n( "Error: iMIP message with unknown method: '%1'", msg->method() );
00981 }
00982 return QString();
00983 }
00984
00985 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
00986 {
00987 if ( !msg || !fb ) {
00988 return QString();
00989 }
00990
00991 switch ( msg->method() ) {
00992 case iTIPPublish:
00993 return i18n( "This free/busy list has been published" );
00994 case iTIPRequest:
00995 return i18n( "The free/busy list has been requested" );
00996 case iTIPRefresh:
00997 return i18n( "This free/busy list was refreshed" );
00998 case iTIPCancel:
00999 return i18n( "This free/busy list was canceled" );
01000 case iTIPAdd:
01001 return i18n( "Addition to the free/busy list" );
01002 case iTIPNoMethod:
01003 default:
01004 return i18n( "Error: Free/Busy iMIP message with unknown method: '%1'", msg->method() );
01005 }
01006 }
01007
01008
01009 class KCal::IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor
01010 {
01011 public:
01012 ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
01013 bool act( IncidenceBase *incidence, ScheduleMessage *msg )
01014 {
01015 mMessage = msg;
01016 return incidence->accept( *this );
01017 }
01018 QString result() const { return mResult; }
01019
01020 protected:
01021 QString mResult;
01022 ScheduleMessage *mMessage;
01023 };
01024
01025 class KCal::IncidenceFormatter::InvitationHeaderVisitor :
01026 public IncidenceFormatter::ScheduleMessageVisitor
01027 {
01028 protected:
01029 bool visit( Event *event )
01030 {
01031 mResult = invitationHeaderEvent( event, mMessage );
01032 return !mResult.isEmpty();
01033 }
01034 bool visit( Todo *todo )
01035 {
01036 mResult = invitationHeaderTodo( todo, mMessage );
01037 return !mResult.isEmpty();
01038 }
01039 bool visit( Journal *journal )
01040 {
01041 mResult = invitationHeaderJournal( journal, mMessage );
01042 return !mResult.isEmpty();
01043 }
01044 bool visit( FreeBusy *fb )
01045 {
01046 mResult = invitationHeaderFreeBusy( fb, mMessage );
01047 return !mResult.isEmpty();
01048 }
01049 };
01050
01051 class KCal::IncidenceFormatter::InvitationBodyVisitor
01052 : public IncidenceFormatter::ScheduleMessageVisitor
01053 {
01054 protected:
01055 bool visit( Event *event )
01056 {
01057 mResult = invitationDetailsEvent( event );
01058 return !mResult.isEmpty();
01059 }
01060 bool visit( Todo *todo )
01061 {
01062 mResult = invitationDetailsTodo( todo );
01063 return !mResult.isEmpty();
01064 }
01065 bool visit( Journal *journal )
01066 {
01067 mResult = invitationDetailsJournal( journal );
01068 return !mResult.isEmpty();
01069 }
01070 bool visit( FreeBusy *fb )
01071 {
01072 mResult = invitationDetailsFreeBusy( fb );
01073 return !mResult.isEmpty();
01074 }
01075 };
01076
01077
01078 QString InvitationFormatterHelper::generateLinkURL( const QString &id )
01079 {
01080 return id;
01081 }
01082
01083 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
01084 {
01085 QString res( "<a href=\"%1\"><b>%2</b></a>" );
01086 return res.arg( generateLinkURL( id ) ).arg( text );
01087 return res;
01088 }
01089
01090 QString IncidenceFormatter::formatICalInvitation( QString invitation, Calendar *mCalendar,
01091 InvitationFormatterHelper *helper )
01092 {
01093 if ( invitation.isEmpty() ) {
01094 return QString();
01095 }
01096
01097 ICalFormat format;
01098
01099
01100 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
01101
01102 if( !msg ) {
01103 kDebug(5800) << "Failed to parse the scheduling message";
01104 Q_ASSERT( format.exception() );
01105 kDebug(5800) << format.exception()->message();
01106 return QString();
01107 }
01108
01109 IncidenceBase *incBase = msg->event();
01110
01111
01112 QString html;
01113
01114 QString tableStyle = QString::fromLatin1(
01115 "style=\"border: solid 1px; margin: 0em;\"" );
01116 QString tableHead = QString::fromLatin1(
01117 "<div align=\"center\">"
01118 "<table width=\"80%\" cellpadding=\"1\" cellspacing=\"0\" %1>"
01119 "<tr><td>" ).arg( tableStyle );
01120
01121 html += tableHead;
01122 InvitationHeaderVisitor headerVisitor;
01123
01124 if ( !headerVisitor.act( incBase, msg ) ) {
01125 return QString();
01126 }
01127 html += "<h3>" + headerVisitor.result() + "</h3>";
01128
01129 InvitationBodyVisitor bodyVisitor;
01130 if ( !bodyVisitor.act( incBase, msg ) ) {
01131 return QString();
01132 }
01133 html += bodyVisitor.result();
01134
01135 html += "<br> <br> <br>";
01136 html += "<table border=\"0\" cellspacing=\"0\"><tr><td> </td></tr><tr>";
01137
01138 #if 0
01139
01140 html += helper->makeLinkURL( "accept", i18n( "[Enter this into my calendar]" ) );
01141 html += "</td><td> </td><td>";
01142 #endif
01143
01144
01145
01146 Incidence *incidence = dynamic_cast<Incidence*>( incBase );
01147 switch ( msg->method() ) {
01148 case iTIPPublish:
01149 case iTIPRequest:
01150 case iTIPRefresh:
01151 case iTIPAdd:
01152 {
01153 if ( incidence && incidence->revision() > 0 ) {
01154 if ( incBase->type() == "Todo" ) {
01155 html += "<td colspan=\"11\">";
01156 html += helper->makeLink( "reply", i18n( "[Enter this into my to-do list]" ) );
01157 } else {
01158 html += "<td colspan=\"9\">";
01159 html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
01160 }
01161 html += "</td></tr><tr>";
01162 }
01163 html += "<td>";
01164
01165
01166 html += helper->makeLink( "accept", i18nc( "accept to-do request", "[Accept]" ) );
01167 html += "</td><td> </td><td>";
01168 html += helper->makeLink( "accept_conditionally",
01169 i18nc( "Accept conditionally", "[Accept cond.]" ) );
01170 html += "</td><td> </td><td>";
01171
01172 html += helper->makeLink( "decline", i18nc( "decline to-do request", "[Decline]" ) );
01173 html += "</td><td> </td><td>";
01174
01175
01176 html += helper->makeLink( "delegate", i18nc( "delegate to-do to another", "[Delegate]" ) );
01177 html += "</td><td> </td><td>";
01178
01179
01180 html += helper->makeLink( "forward", i18nc( "forward request to another", "[Forward]" ) );
01181
01182 if ( incBase->type() == "Event" ) {
01183 html += "</b></a></td><td> </td><td>";
01184 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01185 }
01186 break;
01187 }
01188
01189 case iTIPCancel:
01190
01191 html += helper->makeLink( "cancel", i18n( "[Remove this from my calendar]" ) );
01192 break;
01193
01194 case iTIPReply:
01195
01196 if ( incBase->type() == "Todo" ) {
01197 html += helper->makeLink( "reply", i18n( "[Enter this into my to-do list]" ) );
01198 } else {
01199 html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
01200 }
01201 break;
01202
01203 case iTIPCounter:
01204 case iTIPDeclineCounter:
01205 case iTIPNoMethod:
01206 break;
01207 }
01208
01209 html += "</td></tr></table>";
01210
01211 if ( incidence ) {
01212 QString sDescr = incidence->description();
01213 if( !sDescr.isEmpty() ) {
01214 html += "<br> <br> <br><u>" + i18n( "Description:" ) +
01215 "</u><br><table border=\"0\"><tr><td> </td><td>";
01216 html += string2HTML( sDescr ) + "</td></tr></table>";
01217 }
01218 QStringList comments = incidence->comments();
01219 if ( ( msg->method() == iTIPRequest ||
01220 msg->method() == iTIPCancel ) && !comments.isEmpty() ) {
01221 html += "<br><u>" + i18n( "Comments:" ) +
01222 "</u><br><table border=\"0\"><tr><td> </td><td><ul>";
01223 for ( int i = 0; i < comments.count(); ++i ) {
01224 html += "<li>" + string2HTML( comments[i] ) + "</li>";
01225 }
01226 html += "</ul></td></tr></table>";
01227 }
01228 }
01229
01230 html += "</td></tr></table><br></div>";
01231
01232 return html;
01233 }
01234
01235
01236
01237
01238
01239
01240 class KCal::IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
01241 {
01242 public:
01243 ToolTipVisitor() : mRichText( true ), mResult( "" ) {}
01244
01245 bool act( IncidenceBase *incidence, bool richText=true )
01246 {
01247 mRichText = richText;
01248 mResult = "";
01249 return incidence ? incidence->accept( *this ) : false;
01250 }
01251 QString result() const { return mResult; }
01252
01253 protected:
01254 bool visit( Event *event );
01255 bool visit( Todo *todo );
01256 bool visit( Journal *journal );
01257 bool visit( FreeBusy *fb );
01258
01259 QString dateRangeText( Event *event );
01260 QString dateRangeText( Todo *todo );
01261 QString dateRangeText( Journal *journal );
01262 QString dateRangeText( FreeBusy *fb );
01263
01264 QString generateToolTip( Incidence *incidence, QString dtRangeText );
01265
01266 protected:
01267 bool mRichText;
01268 QString mResult;
01269 };
01270
01271 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event )
01272 {
01273 QString ret;
01274 QString tmp;
01275 if ( event->isMultiDay() ) {
01276
01277 if ( event->allDay() ) {
01278 tmp = event->dtStartDateStr( true, event->dtStart().timeSpec() ).replace( " ", " " );
01279 } else {
01280 tmp = event->dtStartStr( true, event->dtStart().timeSpec() ).replace( " ", " " );
01281 }
01282 ret += "<br>" + i18nc( "Event start", "<i>From:</i> %1", tmp );
01283
01284 if ( event->allDay() ) {
01285 tmp = event->dtEndDateStr( true, event->dtStart().timeSpec() ).replace( " ", " " );
01286 } else {
01287 tmp = event->dtEndStr( true, event->dtEnd().timeSpec() ).replace( " ", " " );
01288 }
01289 ret += "<br>" + i18nc( "Event end","<i>To:</i> %1", tmp );
01290
01291 } else {
01292
01293 ret += "<br>" +
01294 i18n( "<i>Date:</i> %1",
01295 event->dtStartDateStr(
01296 true, event->dtStart().timeSpec() ).replace( " ", " " ) );
01297 if ( !event->allDay() ) {
01298 if ( event->dtStartTimeStr( true, event->dtStart().timeSpec() ) ==
01299 event->dtEndTimeStr( true, event->dtEnd().timeSpec() ) ) {
01300
01301 tmp = "<br>" +
01302 i18nc( "time for event, to prevent ugly line breaks", "<i>Time:</i> %1",
01303 event->dtStartTimeStr(
01304 true, event->dtStart().timeSpec() ).replace( " ", " " ) );
01305 } else {
01306 tmp = "<br>" +
01307 i18nc( "time range for event, to prevent ugly line breaks",
01308 "<i>Time:</i> %1 - %2",
01309 event->dtStartTimeStr(
01310 true, event->dtStart().timeSpec() ).replace( " ", " " ),
01311 event->dtEndTimeStr(
01312 true, event->dtEnd().timeSpec() ).replace( " ", " " ) );
01313 }
01314 ret += tmp;
01315 }
01316 }
01317 return ret;
01318 }
01319
01320 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo )
01321 {
01322 QString ret;
01323 bool allDay( todo->allDay() );
01324 if ( todo->hasStartDate() ) {
01325
01326
01327
01328 ret += "<br>" + i18n( "<i>Start:</i> %1",
01329 ( allDay ) ?
01330 ( todo->dtStartDateStr(
01331 true, false, todo->dtStart().timeSpec() ).replace( " ", " " ) ) :
01332 ( todo->dtStartStr(
01333 true, false, todo->dtStart().timeSpec() ).replace( " ", " " ) ) ) ;
01334 }
01335 if ( todo->hasDueDate() ) {
01336 ret += "<br>" + i18n( "<i>Due:</i> %1",
01337 ( allDay ) ?
01338 ( todo->dtDueDateStr(
01339 true, todo->dtStart().timeSpec() ).replace( " ", " " ) ) :
01340 ( todo->dtDueStr(
01341 true, todo->dtDue().timeSpec() ).replace( " ", " " ) ) );
01342 }
01343 if ( todo->isCompleted() ) {
01344 ret += "<br>" +
01345 i18n( "<i>Completed:</i> %1", todo->completedStr().replace( " ", " " ) );
01346 } else {
01347 ret += "<br>" + i18nc( "percent complete", "%1 % completed", todo->percentComplete() );
01348 }
01349
01350 return ret;
01351 }
01352
01353 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal *journal )
01354 {
01355 QString ret;
01356 if ( journal->dtStart().isValid() ) {
01357 ret += "<br>" +
01358 i18n( "<i>Date:</i> %1",
01359 journal->dtStartDateStr( false, journal->dtStart().timeSpec() ) );
01360 }
01361 return ret;
01362 }
01363
01364 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
01365 {
01366 QString ret;
01367 ret = "<br>" +
01368 i18n( "<i>Period start:</i> %1",
01369 KGlobal::locale()->formatDateTime( fb->dtStart().dateTime() ) );
01370 ret += "<br>" +
01371 i18n( "<i>Period start:</i> %1",
01372 KGlobal::locale()->formatDateTime( fb->dtEnd().dateTime() ) );
01373 return ret;
01374 }
01375
01376 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
01377 {
01378 mResult = generateToolTip( event, dateRangeText( event ) );
01379 return !mResult.isEmpty();
01380 }
01381
01382 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
01383 {
01384 mResult = generateToolTip( todo, dateRangeText( todo ) );
01385 return !mResult.isEmpty();
01386 }
01387
01388 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
01389 {
01390 mResult = generateToolTip( journal, dateRangeText( journal ) );
01391 return !mResult.isEmpty();
01392 }
01393
01394 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
01395 {
01396 mResult = "<qt><b>" + i18n( "Free/Busy information for %1", fb->organizer().fullName() ) + "</b>";
01397 mResult += dateRangeText( fb );
01398 mResult += "</qt>";
01399 return !mResult.isEmpty();
01400 }
01401
01402 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence *incidence,
01403 QString dtRangeText )
01404 {
01405 if ( !incidence ) {
01406 return QString();
01407 }
01408
01409 QString tmp = "<qt><b>"+ incidence->summary().replace( "\n", "<br>" ) + "</b>";
01410
01411 tmp += dtRangeText;
01412
01413 if ( !incidence->location().isEmpty() ) {
01414
01415 tmp += "<br>" +
01416 i18n( "<i>Location:</i> %1", incidence->location().replace( "\n", "<br>" ) );
01417 }
01418
01419 if ( !incidence->description().isEmpty() ) {
01420 QString desc( incidence->description() );
01421 if ( desc.length() > 120 ) {
01422 desc = desc.left( 120 ) + "...";
01423 }
01424 tmp += "<br>----------<br>" + i18n( "<i>Description:</i>" ) + "<br>" +
01425 desc.replace( "\n", "<br>" );
01426 }
01427 tmp += "</qt>";
01428 return tmp;
01429 }
01430
01431
01432 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText )
01433 {
01434 ToolTipVisitor v;
01435 if ( v.act( incidence, richText ) ) {
01436 return v.result();
01437 } else {
01438 return QString();
01439 }
01440 }
01441
01442
01443
01444
01445
01446 static QString mailBodyIncidence( Incidence *incidence )
01447 {
01448 QString body;
01449 if ( !incidence->summary().isEmpty() ) {
01450 body += i18n( "Summary: %1\n", incidence->summary() );
01451 }
01452 if ( !incidence->organizer().isEmpty() ) {
01453 body += i18n( "Organizer: %1\n", incidence->organizer().fullName() );
01454 }
01455 if ( !incidence->location().isEmpty() ) {
01456 body += i18n( "Location: %1\n", incidence->location() );
01457 }
01458 return body;
01459 }
01460
01461
01462 class KCal::IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor
01463 {
01464 public:
01465 MailBodyVisitor() : mResult( "" ) {}
01466
01467 bool act( IncidenceBase *incidence )
01468 {
01469 mResult = "";
01470 return incidence ? incidence->accept( *this ) : false;
01471 }
01472 QString result() const
01473 {
01474 return mResult;
01475 }
01476
01477 protected:
01478 bool visit( Event *event );
01479 bool visit( Todo *todo );
01480 bool visit( Journal *journal );
01481 bool visit( FreeBusy * )
01482 {
01483 mResult = i18n( "This is a Free Busy Object" );
01484 return !mResult.isEmpty();
01485 }
01486 protected:
01487 QString mResult;
01488 };
01489
01490 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
01491 {
01492 QString recurrence[]= {
01493 i18nc( "no recurrence", "None" ),
01494 i18nc( "event recurs by minutes", "Minutely" ),
01495 i18nc( "event recurs by hours", "Hourly" ),
01496 i18nc( "event recurs by days", "Daily" ),
01497 i18nc( "event recurs by weeks", "Weekly" ),
01498 i18nc( "event recurs same position (e.g. first monday) each month", "Monthly Same Position" ),
01499 i18nc( "event recurs same day each month", "Monthly Same Day" ),
01500 i18nc( "event recurs same month each year", "Yearly Same Month" ),
01501 i18nc( "event recurs same day each year", "Yearly Same Day" ),
01502 i18nc( "event recurs same position (e.g. first monday) each year", "Yearly Same Position" )
01503 };
01504
01505 mResult = mailBodyIncidence( event );
01506 mResult += i18n( "Start Date: %1\n",
01507 event->dtStartDateStr( true, event->dtStart().timeSpec() ) );
01508 if ( !event->allDay() ) {
01509 mResult += i18n( "Start Time: %1\n",
01510 event->dtStartTimeStr( true, event->dtStart().timeSpec() ) );
01511 }
01512 if ( event->dtStart() != event->dtEnd() ) {
01513 mResult += i18n( "End Date: %1\n",
01514 event->dtEndDateStr( true, event->dtStart().timeSpec() ) );
01515 }
01516 if ( !event->allDay() ) {
01517 mResult += i18n( "End Time: %1\n",
01518 event->dtEndTimeStr( true, event->dtStart().timeSpec() ) );
01519 }
01520 if ( event->recurs() ) {
01521 Recurrence *recur = event->recurrence();
01522
01523 mResult += i18n( "Recurs: %1\n", recurrence[ recur->recurrenceType() ] );
01524 mResult += i18n( "Frequency: %1\n", event->recurrence()->frequency() );
01525
01526 if ( recur->duration() > 0 ) {
01527 mResult += i18np( "Repeats once", "Repeats %1 times", recur->duration() );
01528 mResult += '\n';
01529 } else {
01530 if ( recur->duration() != -1 ) {
01531
01532 QString endstr;
01533 if ( event->allDay() ) {
01534 endstr = KGlobal::locale()->formatDate( recur->endDate() );
01535 } else {
01536 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime().dateTime() );
01537 }
01538 mResult += i18n( "Repeat until: %1\n", endstr );
01539 } else {
01540 mResult += i18n( "Repeats forever\n" );
01541 }
01542 }
01543 }
01544
01545 QString details = event->description();
01546 if ( !details.isEmpty() ) {
01547 mResult += i18n( "Details:\n%1\n", details );
01548 }
01549 return !mResult.isEmpty();
01550 }
01551
01552 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
01553 {
01554 mResult = mailBodyIncidence( todo );
01555
01556 if ( todo->hasStartDate() ) {
01557 mResult += i18n( "Start Date: %1\n",
01558 todo->dtStartDateStr( true, false, todo->dtStart().timeSpec() ) );
01559 if ( !todo->allDay() ) {
01560 mResult += i18n( "Start Time: %1\n",
01561 todo->dtStartTimeStr( true, false, todo->dtStart().timeSpec() ) );
01562 }
01563 }
01564 if ( todo->hasDueDate() ) {
01565 mResult += i18n( "Due Date: %1\n",
01566 todo->dtDueDateStr( true, todo->dtStart().timeSpec() ) );
01567 if ( !todo->allDay() ) {
01568 mResult += i18n( "Due Time: %1\n",
01569 todo->dtDueTimeStr( true, todo->dtStart().timeSpec() ) );
01570 }
01571 }
01572 QString details = todo->description();
01573 if ( !details.isEmpty() ) {
01574 mResult += i18n( "Details:\n%1\n", details );
01575 }
01576 return !mResult.isEmpty();
01577 }
01578
01579 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
01580 {
01581 mResult = mailBodyIncidence( journal );
01582 mResult += i18n( "Date: %1\n", journal->dtStartDateStr( true, journal->dtStart().timeSpec() ) );
01583 if ( !journal->allDay() ) {
01584 mResult += i18n( "Time: %1\n", journal->dtStartTimeStr( true, journal->dtStart().timeSpec() ) );
01585 }
01586 if ( !journal->description().isEmpty() ) {
01587 mResult += i18n( "Text of the journal:\n%1\n", journal->description() );
01588 }
01589 return !mResult.isEmpty();
01590 }
01591
01592
01593 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
01594 {
01595 if ( !incidence ) {
01596 return QString();
01597 }
01598
01599 MailBodyVisitor v;
01600 if ( v.act( incidence ) ) {
01601 return v.result();
01602 }
01603 return QString();
01604 }