• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KCal Library

incidenceformatter.cpp

00001 /*
00002   This file is part of the kcal library.
00003 
00004   Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00005   Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006   Copyright (c) 2005 Rafal Rzepecki <divide@users.sourceforge.net>
00007 
00008   This library is free software; you can redistribute it and/or
00009   modify it under the terms of the GNU Library General Public
00010   License as published by the Free Software Foundation; either
00011   version 2 of the License, or (at your option) any later version.
00012 
00013   This library is distributed in the hope that it will be useful,
00014   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016   Library General Public License for more details.
00017 
00018   You should have received a copy of the GNU Library General Public License
00019   along with this library; see the file COPYING.LIB.  If not, write to
00020   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021   Boston, MA 02110-1301, USA.
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  *  Helper functions for the extensive display (event viewer)
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   // Make the search, if there is an email address to search on,
00109   // and either name or uid is missing
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         // No name set, so use the one from the addressbook
00117         name = o.formattedName();
00118       }
00119       uid = o.uid();
00120     } else {
00121       // Email not found in the addressbook. Don't make a link
00122       uid.clear();
00123     }
00124   }
00125   kDebug(5800) << "formatAttendees: uid =" << uid;
00126 
00127   // Show the attendee
00128   QString tmpString = "<li>";
00129   if ( !uid.isEmpty() ) {
00130     // There is a UID, so make a link to the addressbook
00131     if ( name.isEmpty() ) {
00132       // Use the email address for text
00133       tmpString += eventViewerAddLink( "uid:" + uid, email );
00134     } else {
00135       tmpString += eventViewerAddLink( "uid:" + uid, name );
00136     }
00137   } else {
00138     // No UID, just show some text
00139     tmpString += ( name.isEmpty() ? email : name );
00140   }
00141   tmpString += '\n';
00142 
00143   // Make the mailto link
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     // Add organizer link
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     // Add attendees links
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   FIXME:This function depends of kaddressbook. Is necessary a new
00208   type of event?
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   //TODO: add a tart icon
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   // show icons
00245   /* all of those icons currently don't exist, makes no sense currently
00246      to load a set of "unknown" icons. re-enable when those are available.
00247   {
00248     KIconLoader *iconLoader = KIconLoader::global();
00249     tmpStr += "<td>";
00250 
00251     if ( incidence->type() == "Todo" ) {
00252       tmpStr += "<img src=\"" + iconLoader->iconPath( "todo", KIconLoader::Small ) + "\">";
00253     }
00254     if ( incidence->isAlarmEnabled() ) {
00255       tmpStr += "<img src=\"" + iconLoader->iconPath( "bell", KIconLoader::Small ) + "\">";
00256     }
00257     if ( incidence->recurs() ) {
00258       tmpStr += "<img src=\"" + iconLoader->iconPath( "recur", KIconLoader::Small ) + "\">";
00259     }
00260     if ( incidence->isReadOnly() ) {
00261       tmpStr += "<img src=\"" + iconLoader->iconPath( "readonlyevent", KIconLoader::Small ) + "\">";
00262     }
00263 
00264     tmpStr += "</td>";
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&nbsp;category", "%1&nbsp;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&nbsp;attachment", "%1&nbsp;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 //@cond PRIVATE
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 //@endcond
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  *  Helper functions for the body part formatter of kmail
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   // Meeting details are formatted into an HTML table
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   // Meeting summary & location rows
00594   html += invitationRow( i18n( "What:" ), sSummary );
00595   html += invitationRow( i18n( "Where:" ), sLocation );
00596 
00597   // Meeting Start Time Row
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   // Meeting End Time Row
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   // Meeting Duration Row
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   // To-do details are formatted into an HTML table
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   // TODO: Several of the methods are not allowed for journals, so remove them.
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 //@cond PRIVATE
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 //@endcond
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   // parseScheduleMessage takes the tz from the calendar,
01099   // no need to set it manually here for the format!
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   // First make the text of the message
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   // The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled
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>&nbsp;<br>&nbsp;<br>";
01136   html += "<table border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td></tr><tr>";
01137 
01138 #if 0
01139   // TODO: implement this
01140   html += helper->makeLinkURL( "accept", i18n( "[Enter this into my calendar]" ) );
01141   html += "</td><td> &nbsp; </td><td>";
01142 #endif
01143 
01144   // Add groupware links
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     // Accept
01166     html += helper->makeLink( "accept", i18nc( "accept to-do request", "[Accept]" ) );
01167     html += "</td><td> &nbsp; </td><td>";
01168     html += helper->makeLink( "accept_conditionally",
01169                               i18nc( "Accept conditionally", "[Accept cond.]" ) );
01170     html += "</td><td> &nbsp; </td><td>";
01171     // Decline
01172     html += helper->makeLink( "decline", i18nc( "decline to-do request", "[Decline]" ) );
01173     html += "</td><td> &nbsp; </td><td>";
01174 
01175     // Delegate
01176     html += helper->makeLink( "delegate", i18nc( "delegate to-do to another", "[Delegate]" ) );
01177     html += "</td><td> &nbsp; </td><td>";
01178 
01179     // Forward
01180     html += helper->makeLink( "forward", i18nc( "forward request to another", "[Forward]" ) );
01181 
01182     if ( incBase->type() == "Event" ) {
01183       html += "</b></a></td><td> &nbsp; </td><td>";
01184       html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01185     }
01186     break;
01187   }
01188 
01189   case iTIPCancel:
01190     // Cancel event from my calendar
01191     html += helper->makeLink( "cancel", i18n( "[Remove this from my calendar]" ) );
01192     break;
01193 
01194   case iTIPReply:
01195     // Enter this into my calendar
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>&nbsp;<br>&nbsp;<br><u>" + i18n( "Description:" ) +
01215               "</u><br><table border=\"0\"><tr><td>&nbsp;</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>&nbsp;</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  *  Helper functions for the Incidence tooltips
01237  *******************************************************************/
01238 
01239 //@cond PRIVATE
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( " ", "&nbsp;" );
01279     } else {
01280       tmp = event->dtStartStr( true, event->dtStart().timeSpec() ).replace( " ", "&nbsp;" );
01281     }
01282     ret += "<br>" + i18nc( "Event start", "<i>From:</i>&nbsp;%1", tmp );
01283 
01284     if ( event->allDay() ) {
01285       tmp = event->dtEndDateStr( true, event->dtStart().timeSpec() ).replace( " ", "&nbsp;" );
01286     } else {
01287       tmp = event->dtEndStr( true, event->dtEnd().timeSpec() ).replace( " ", "&nbsp;" );
01288     }
01289     ret += "<br>" + i18nc( "Event end","<i>To:</i>&nbsp;%1", tmp );
01290 
01291   } else {
01292 
01293     ret += "<br>" +
01294            i18n( "<i>Date:</i>&nbsp;%1",
01295                  event->dtStartDateStr(
01296                    true, event->dtStart().timeSpec() ).replace( " ", "&nbsp;" ) );
01297     if ( !event->allDay() ) {
01298       if ( event->dtStartTimeStr( true, event->dtStart().timeSpec() ) ==
01299            event->dtEndTimeStr( true, event->dtEnd().timeSpec() ) ) {
01300         // to prevent 'Time: 17:00 - 17:00'
01301         tmp = "<br>" +
01302               i18nc( "time for event, &nbsp; to prevent ugly line breaks", "<i>Time:</i>&nbsp;%1",
01303                      event->dtStartTimeStr(
01304                        true, event->dtStart().timeSpec() ).replace( " ", "&nbsp;" ) );
01305       } else {
01306         tmp = "<br>" +
01307               i18nc( "time range for event, &nbsp; to prevent ugly line breaks",
01308                      "<i>Time:</i>&nbsp;%1&nbsp;-&nbsp;%2",
01309                      event->dtStartTimeStr(
01310                        true, event->dtStart().timeSpec() ).replace( " ", "&nbsp;" ),
01311                      event->dtEndTimeStr(
01312                        true, event->dtEnd().timeSpec() ).replace( " ", "&nbsp;" ) );
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     // No need to add <i> here. This is separated issue and each line
01326     // is very visible on its own. On the other hand... Yes, I like it
01327     // italics here :)
01328     ret += "<br>" + i18n( "<i>Start:</i>&nbsp;%1",
01329                           ( allDay ) ?
01330                           ( todo->dtStartDateStr(
01331                             true, false, todo->dtStart().timeSpec() ).replace( " ", "&nbsp;" ) ) :
01332                           ( todo->dtStartStr(
01333                             true, false, todo->dtStart().timeSpec() ).replace( " ", "&nbsp;" ) ) ) ;
01334   }
01335   if ( todo->hasDueDate() ) {
01336     ret += "<br>" + i18n( "<i>Due:</i>&nbsp;%1",
01337                           ( allDay ) ?
01338                           ( todo->dtDueDateStr(
01339                             true, todo->dtStart().timeSpec() ).replace( " ", "&nbsp;" ) ) :
01340                           ( todo->dtDueStr(
01341                             true, todo->dtDue().timeSpec() ).replace( " ", "&nbsp;" ) ) );
01342   }
01343   if ( todo->isCompleted() ) {
01344     ret += "<br>" +
01345            i18n( "<i>Completed:</i>&nbsp;%1", todo->completedStr().replace( " ", "&nbsp;" ) );
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>&nbsp;%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>&nbsp;%1",
01369               KGlobal::locale()->formatDateTime( fb->dtStart().dateTime() ) );
01370   ret += "<br>" +
01371          i18n( "<i>Period start:</i>&nbsp;%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     // Put Location: in italics
01415     tmp += "<br>" +
01416            i18n( "<i>Location:</i>&nbsp;%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 //@endcond
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  *  Helper functions for the Incidence tooltips
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 //@cond PRIVATE
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     // TODO: Merge these two to one of the form "Recurs every 3 days"
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 // TODO_Recurrence: What to do with all-day
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 //@endcond
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 }

KCal Library

Skip menu "KCal Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal