libkcal

incidenceformatter.cpp

00001 /*
00002     This file is part of libkcal.
00003 
00004     Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to
00019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020     Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include "incidenceformatter.h"
00024 
00025 #include <libkcal/attachment.h>
00026 #include <libkcal/event.h>
00027 #include <libkcal/todo.h>
00028 #include <libkcal/journal.h>
00029 #include <libkcal/calendar.h>
00030 #include <libkcal/calendarlocal.h>
00031 #include <libkcal/icalformat.h>
00032 #include <libkcal/freebusy.h>
00033 
00034 #include <libemailfunctions/email.h>
00035 
00036 #include <ktnef/ktnefparser.h>
00037 #include <ktnef/ktnefmessage.h>
00038 #include <ktnef/ktnefdefs.h>
00039 #include <kabc/phonenumber.h>
00040 #include <kabc/vcardconverter.h>
00041 #include <kabc/stdaddressbook.h>
00042 
00043 #include <kapplication.h>
00044 // #include <kdebug.h>
00045 
00046 #include <klocale.h>
00047 #include <kglobal.h>
00048 #include <kiconloader.h>
00049 
00050 #include <qbuffer.h>
00051 #include <qstylesheet.h>
00052 #include <qdatetime.h>
00053 
00054 #include <time.h>
00055 
00056 
00057 using namespace KCal;
00058 
00059 
00060 /*******************************************************************
00061  *  Helper functions for the extensive display (event viewer)
00062  *******************************************************************/
00063 
00064 static QString eventViewerAddLink( const QString &ref, const QString &text,
00065                              bool newline = true )
00066 {
00067   QString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
00068   if ( newline ) tmpStr += "\n";
00069   return tmpStr;
00070 }
00071 
00072 static QString eventViewerAddTag( const QString & tag, const QString & text )
00073 {
00074   int numLineBreaks = text.contains( "\n" );
00075   QString str = "<" + tag + ">";
00076   QString tmpText = text;
00077   QString tmpStr = str;
00078   if( numLineBreaks >= 0 ) {
00079     if ( numLineBreaks > 0) {
00080       int pos = 0;
00081       QString tmp;
00082       for( int i = 0; i <= numLineBreaks; i++ ) {
00083         pos = tmpText.find( "\n" );
00084         tmp = tmpText.left( pos );
00085         tmpText = tmpText.right( tmpText.length() - pos - 1 );
00086         tmpStr += tmp + "<br>";
00087       }
00088     } else {
00089       tmpStr += tmpText;
00090     }
00091   }
00092   tmpStr += "</" + tag + ">";
00093   return tmpStr;
00094 }
00095 
00096 
00097 static QString eventViewerFormatCategories( Incidence *event )
00098 {
00099   QString tmpStr;
00100   if ( !event->categoriesStr().isEmpty() ) {
00101     if ( event->categories().count() == 1 ) {
00102       tmpStr = eventViewerAddTag( "h2", i18n("Category") );
00103     } else {
00104       tmpStr = eventViewerAddTag( "h2", i18n("Categories") );
00105     }
00106     tmpStr += eventViewerAddTag( "p", event->categoriesStr() );
00107   }
00108   return tmpStr;
00109 }
00110 
00111 static QString linkPerson( const QString& email, QString name,
00112                            QString uid, const QString& iconPath )
00113 {
00114   // Make the search, if there is an email address to search on,
00115   // and either name or uid is missing
00116   if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00117     KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00118     KABC::Addressee::List addressList = add_book->findByEmail( email );
00119     KABC::Addressee o = addressList.first();
00120     if ( !o.isEmpty() && addressList.size() < 2 ) {
00121       if ( name.isEmpty() )
00122         // No name set, so use the one from the addressbook
00123         name = o.formattedName();
00124       uid = o.uid();
00125     } else
00126       // Email not found in the addressbook. Don't make a link
00127       uid = QString::null;
00128   }
00129   kdDebug(5850) << "formatAttendees: uid = " << uid << endl;
00130 
00131   // Show the attendee
00132   QString tmpString = "<li>";
00133   if ( !uid.isEmpty() ) {
00134     // There is a UID, so make a link to the addressbook
00135     if ( name.isEmpty() )
00136       // Use the email address for text
00137       tmpString += eventViewerAddLink( "uid:" + uid, email );
00138     else
00139       tmpString += eventViewerAddLink( "uid:" + uid, name );
00140   } else {
00141     // No UID, just show some text
00142     tmpString += ( name.isEmpty() ? email : name );
00143   }
00144   tmpString += '\n';
00145 
00146   // Make the mailto link
00147   if ( !email.isEmpty() && !iconPath.isNull() ) {
00148     KCal::Person person( name, email );
00149     KURL mailto;
00150     mailto.setProtocol( "mailto" );
00151     mailto.setPath( person.fullName() );
00152     tmpString += eventViewerAddLink( mailto.url(),
00153                                      "<img src=\"" + iconPath + "\">" );
00154   }
00155   tmpString += "</li>\n";
00156 
00157   return tmpString;
00158 }
00159 
00160 static QString eventViewerFormatAttendees( Incidence *event )
00161 {
00162   QString tmpStr;
00163   Attendee::List attendees = event->attendees();
00164   if ( attendees.count() ) {
00165     KIconLoader iconLoader;
00166     const QString iconPath = iconLoader.iconPath( "mail_generic",
00167                                                   KIcon::Small );
00168 
00169     // Add organizer link
00170     tmpStr += eventViewerAddTag( "h3", i18n("Organizer") );
00171     tmpStr += "<ul>";
00172     tmpStr += linkPerson( event->organizer().email(),
00173                           event->organizer().name(), QString::null, iconPath );
00174     tmpStr += "</ul>";
00175 
00176     // Add attendees links
00177     tmpStr += eventViewerAddTag( "h3", i18n("Attendees") );
00178     tmpStr += "<ul>";
00179     Attendee::List::ConstIterator it;
00180     for( it = attendees.begin(); it != attendees.end(); ++it ) {
00181       Attendee *a = *it;
00182       tmpStr += linkPerson( a->email(), a->name(), a->uid(), iconPath );
00183     }
00184     tmpStr += "</ul>";
00185   }
00186   return tmpStr;
00187 }
00188 
00189 static QString eventViewerFormatAttachments( Incidence *i )
00190 {
00191   QString tmpStr;
00192   Attachment::List as = i->attachments();
00193   if ( as.count() > 0 ) {
00194     Attachment::List::ConstIterator it;
00195     for( it = as.begin(); it != as.end(); ++it ) {
00196       if ( (*it)->isUri() ) {
00197         QString name;
00198         if ( (*it)->uri().startsWith( "kmail:" ) )
00199           name = i18n( "Show mail" );
00200         else
00201           name = (*it)->uri();
00202         tmpStr += eventViewerAddLink( (*it)->uri(), name );
00203         tmpStr += "<br>";
00204       }
00205     }
00206   }
00207   return tmpStr;
00208 }
00209 
00210 /*
00211   FIXME:This function depends of kaddressbook. Is necessary a new
00212   type of event?
00213 */
00214 static QString eventViewerFormatBirthday( Event *event )
00215 {
00216   if ( !event) return  QString::null;
00217   if ( event->customProperty("KABC","BIRTHDAY")!= "YES" ) return QString::null;
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;
00224   const QString iconPath = iconLoader.iconPath( "mail_generic",
00225                                                   KIcon::Small );
00226   //TODO: add a tart icon
00227   QString tmpString = "<ul>";
00228   tmpString += linkPerson( email_1, name_1, uid_1, iconPath );
00229 
00230   if ( event->customProperty( "KABC", "ANNIVERSARY") == "YES" ) {
00231     QString uid_2 = event->customProperty("KABC","UID-2");
00232     QString name_2 = event->customProperty("KABC","NAME-2");
00233     QString email_2= event->customProperty("KABC","EMAIL-2");
00234     tmpString += linkPerson( email_2, name_2, uid_2, iconPath );
00235   }
00236 
00237   tmpString += "</ul>";
00238   return tmpString;
00239 }
00240 
00241 static QString eventViewerFormatHeader( Incidence *incidence )
00242 {
00243   QString tmpStr = "<table><tr>";
00244 
00245   // show icons
00246   {
00247     KIconLoader iconLoader;
00248 
00249     tmpStr += "<td>";
00250 
00251     if ( incidence->type() == "Todo" ) {
00252       tmpStr += "<img src=\"" +
00253                 iconLoader.iconPath( "todo", KIcon::Small ) +
00254                 "\">";
00255     }
00256     if ( incidence->isAlarmEnabled() ) {
00257       tmpStr += "<img src=\"" +
00258                 iconLoader.iconPath( "bell", KIcon::Small ) +
00259                 "\">";
00260     }
00261     if ( incidence->doesRecur() ) {
00262       tmpStr += "<img src=\"" +
00263                 iconLoader.iconPath( "recur", KIcon::Small ) +
00264                 "\">";
00265     }
00266     if ( incidence->isReadOnly() ) {
00267       tmpStr += "<img src=\"" +
00268                 iconLoader.iconPath( "readonlyevent", KIcon::Small ) +
00269                 "\">";
00270     }
00271 
00272     tmpStr += "</td>";
00273   }
00274 
00275   tmpStr += "<td>" + eventViewerAddTag( "h1", incidence->summary() ) + "</td>";
00276   tmpStr += "</tr></table><br>";
00277 
00278   return tmpStr;
00279 }
00280 
00281 static QString eventViewerFormatEvent( Event *event )
00282 {
00283   if ( !event ) return QString::null;
00284   QString tmpStr = eventViewerFormatHeader( event );
00285 
00286   tmpStr += "<table>";
00287   if ( !event->location().isEmpty() ) {
00288     tmpStr += "<tr>";
00289     tmpStr += "<td align=\"right\"><b>" + i18n( "Location" ) + "</b></td>";
00290     tmpStr += "<td>" + event->location() + "</td>";
00291     tmpStr += "</tr>";
00292   }
00293 
00294   tmpStr += "<tr>";
00295   if ( event->doesFloat() ) {
00296     if ( event->isMultiDay() ) {
00297       tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00298       tmpStr += "<td>" + i18n("<beginTime> - <endTime>","%1 - %2")
00299                     .arg( event->dtStartDateStr() )
00300                     .arg( event->dtEndDateStr() ) + "</td>";
00301     } else {
00302       tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
00303       tmpStr += "<td>" + i18n("date as string","%1").arg( event->dtStartDateStr() ) + "</td>";
00304     }
00305   } else {
00306     if ( event->isMultiDay() ) {
00307       tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00308       tmpStr += "<td>" + i18n("<beginTime> - <endTime>","%1 - %2")
00309                     .arg( event->dtStartStr() )
00310                     .arg( event->dtEndStr() ) + "</td>";
00311     } else {
00312       tmpStr += "<td align=\"right\"><b>" + i18n( "Time" ) + "</b></td>";
00313       if ( event->hasEndDate() && event->dtStart() != event->dtEnd()) {
00314         tmpStr += "<td>" + i18n("<beginTime> - <endTime>","%1 - %2")
00315                       .arg( event->dtStartTimeStr() )
00316                       .arg( event->dtEndTimeStr() ) + "</td>";
00317       } else {
00318         tmpStr += "<td>" + event->dtStartTimeStr() + "</td>";
00319       }
00320       tmpStr += "</tr><tr>";
00321       tmpStr += "<td align=\"right\"><b>" + i18n( "Date" ) + "</b></td>";
00322       tmpStr += "<td>" + i18n("date as string","%1")
00323                     .arg( event->dtStartDateStr() ) + "</td>";
00324     }
00325   }
00326   tmpStr += "</tr>";
00327 
00328   if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) {
00329     tmpStr += "<tr>";
00330     tmpStr += "<td align=\"right\"><b>" + i18n( "Birthday" ) + "</b></td>";
00331     tmpStr += "<td>" + eventViewerFormatBirthday( event ) + "</td>";
00332     tmpStr += "</tr>";
00333     tmpStr += "</table>";
00334     return tmpStr;
00335   }
00336 
00337   if ( !event->description().isEmpty() ) {
00338     tmpStr += "<tr>";
00339     tmpStr += "<td></td>";
00340     tmpStr += "<td>" + eventViewerAddTag( "p", event->description() ) + "</td>";
00341     tmpStr += "</tr>";
00342   }
00343 
00344   if ( event->categories().count() > 0 ) {
00345     tmpStr += "<tr>";
00346     tmpStr += "<td align=\"right\"><b>" + i18n( "1 category", "%n categories", event->categories().count() )+ "</b></td>";
00347     tmpStr += "<td>" + event->categoriesStr() + "</td>";
00348     tmpStr += "</tr>";
00349   }
00350 
00351   if ( event->doesRecur() ) {
00352     QDateTime dt = event->recurrence()->getNextDateTime(
00353                                           QDateTime::currentDateTime() );
00354     tmpStr += "<tr>";
00355     tmpStr += "<td align=\"right\"><b>" + i18n( "Next Occurrence" )+ "</b></td>";
00356     tmpStr += "<td>" + KGlobal::locale()->formatDateTime( dt, true ) + "</td>";
00357     tmpStr += "</tr>";
00358   }
00359 
00360   tmpStr += "<tr><td colspan=\"2\">";
00361   tmpStr += eventViewerFormatAttendees( event );
00362   tmpStr += "</td></tr>";
00363 
00364   int attachmentCount = event->attachments().count();
00365   if ( attachmentCount > 0 ) {
00366     tmpStr += "<tr>";
00367     tmpStr += "<td align=\"right\"><b>" + i18n( "1 attachment", "%n attachments", attachmentCount )+ "</b></td>";
00368     tmpStr += "<td>" + eventViewerFormatAttachments( event ) + "</td>";
00369     tmpStr += "</tr>";
00370   }
00371 
00372   tmpStr += "</table>";
00373   tmpStr += "<p><em>" + i18n( "Creation date: %1.").arg(
00374     KGlobal::locale()->formatDateTime( event->created() , true ) ) + "</em>";
00375   return tmpStr;
00376 }
00377 
00378 static QString eventViewerFormatTodo( Todo *todo )
00379 {
00380   if ( !todo ) return QString::null;
00381   QString tmpStr = eventViewerFormatHeader( todo );
00382 
00383   if ( !todo->location().isEmpty() ) {
00384     tmpStr += eventViewerAddTag( "b", i18n("Location:") );
00385     tmpStr += todo->location();
00386     tmpStr += "<br>";
00387   }
00388   if ( todo->hasDueDate() ) {
00389     tmpStr += i18n("<b>Due on:</b> %1").arg( todo->dtDueStr() );
00390   }
00391 
00392   if ( !todo->description().isEmpty() )
00393     tmpStr += eventViewerAddTag( "p", todo->description() );
00394 
00395   tmpStr += eventViewerFormatCategories( todo );
00396 
00397   tmpStr += i18n("<p><b>Priority:</b> %2</p>")
00398                 .arg( (todo->priority()>0)?(QString::number( todo->priority() )):i18n("Unspecified") );
00399 
00400   tmpStr += i18n("<p><i>%1 % completed</i></p>")
00401                 .arg( todo->percentComplete() );
00402 
00403   if ( todo->doesRecur() ) {
00404     QDateTime dt = todo->recurrence()->getNextDateTime(
00405                                          QDateTime::currentDateTime() );
00406     tmpStr += eventViewerAddTag( "p", "<em>" +
00407       i18n("This is a recurring to-do. The next occurrence will be on %1.").arg(
00408       KGlobal::locale()->formatDateTime( dt, true ) ) + "</em>" );
00409   }
00410   tmpStr += eventViewerFormatAttendees( todo );
00411   tmpStr += eventViewerFormatAttachments( todo );
00412   tmpStr += "<p><em>" + i18n( "Creation date: %1.").arg(
00413     KGlobal::locale()->formatDateTime( todo->created() , true ) ) + "</em>";
00414   return tmpStr;
00415 }
00416 
00417 static QString eventViewerFormatJournal( Journal *journal )
00418 {
00419   if ( !journal ) return QString::null;
00420   QString tmpStr;
00421   if ( !journal->summary().isEmpty() )
00422     tmpStr+= eventViewerAddTag( "h1", journal->summary() );
00423   tmpStr += eventViewerAddTag( "h2", i18n("Journal for %1").arg( journal->dtStartDateStr( false ) ) );
00424   if ( !journal->description().isEmpty() )
00425     tmpStr += eventViewerAddTag( "p", journal->description() );
00426   return tmpStr;
00427 }
00428 
00429 static QString eventViewerFormatFreeBusy( FreeBusy *fb )
00430 {
00431   if ( !fb ) return QString::null;
00432   QString tmpStr( eventViewerAddTag( "h1", i18n("Free/Busy information for %1").arg( fb->organizer().fullName() ) ) );
00433   tmpStr += eventViewerAddTag( "h3", i18n("Busy times in date range %1 - %2:")
00434       .arg( KGlobal::locale()->formatDate( fb->dtStart().date(), true ) )
00435       .arg( KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) ) );
00436 
00437   QValueList<Period> periods = fb->busyPeriods();
00438 
00439   QString text = eventViewerAddTag( "em", eventViewerAddTag( "b", i18n("Busy:") ) );
00440   QValueList<Period>::iterator it;
00441   for ( it = periods.begin(); it != periods.end(); ++it ) {
00442     Period per = *it;
00443     if ( per.hasDuration() ) {
00444       int dur = per.duration().asSeconds();
00445       QString cont;
00446       if ( dur >= 3600 ) {
00447         cont += i18n("1 hour ", "%n hours ", dur / 3600 );
00448         dur %= 3600;
00449       }
00450       if ( dur >= 60 ) {
00451         cont += i18n("1 minute ", "%n minutes ", dur / 60);
00452         dur %= 60;
00453       }
00454       if ( dur > 0 ) {
00455         cont += i18n("1 second", "%n seconds", dur);
00456       }
00457       text += i18n("startDate for duration", "%1 for %2")
00458           .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
00459           .arg( cont );
00460       text += "<br>";
00461     } else {
00462       if ( per.start().date() == per.end().date() ) {
00463         text += i18n("date, fromTime - toTime ", "%1, %2 - %3")
00464             .arg( KGlobal::locale()->formatDate( per.start().date() ) )
00465             .arg( KGlobal::locale()->formatTime( per.start().time() ) )
00466             .arg( KGlobal::locale()->formatTime( per.end().time() ) );
00467       } else {
00468         text += i18n("fromDateTime - toDateTime", "%1 - %2")
00469           .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
00470           .arg( KGlobal::locale()->formatDateTime( per.end(), false ) );
00471       }
00472       text += "<br>";
00473     }
00474   }
00475   tmpStr += eventViewerAddTag( "p", text );
00476   return tmpStr;
00477 }
00478 
00479 class IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor
00480 {
00481   public:
00482     EventViewerVisitor() { mResult = ""; }
00483     bool act( IncidenceBase *incidence ) { return incidence->accept( *this ); }
00484     QString result() const { return mResult; }
00485   protected:
00486     bool visit( Event *event )
00487     {
00488       mResult = eventViewerFormatEvent( event );
00489       return !mResult.isEmpty();
00490     }
00491     bool visit( Todo *todo )
00492     {
00493       mResult = eventViewerFormatTodo( todo );
00494       return !mResult.isEmpty();
00495     }
00496     bool visit( Journal *journal )
00497     {
00498       mResult = eventViewerFormatJournal( journal );
00499       return !mResult.isEmpty();
00500     }
00501     bool visit( FreeBusy *fb )
00502     {
00503       mResult = eventViewerFormatFreeBusy( fb );
00504       return !mResult.isEmpty();
00505     }
00506 
00507   protected:
00508     QString mResult;
00509 };
00510 
00511 QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
00512 {
00513   if ( !incidence ) return QString::null;
00514   EventViewerVisitor v;
00515   if ( v.act( incidence ) ) {
00516     return v.result();
00517   } else
00518     return QString::null;
00519 }
00520 
00521 
00522 
00523 
00524 /*******************************************************************
00525  *  Helper functions for the body part formatter of kmail
00526  *******************************************************************/
00527 
00528 static QString string2HTML( const QString& str )
00529 {
00530   return QStyleSheet::convertFromPlainText(str);
00531 }
00532 
00533 static QString invitationRow( const QString &cell1, const QString &cell2 )
00534 {
00535   return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
00536 }
00537 
00538 static QString invitationDetailsEvent( Event* event )
00539 {
00540   // Meeting details are formatted into an HTML table
00541   if ( !event )
00542     return QString::null;
00543 
00544   QString html;
00545   QString tmp;
00546 
00547   QString sSummary = i18n( "Summary unspecified" );
00548   if ( ! event->summary().isEmpty() ) {
00549     sSummary = string2HTML( event->summary() );
00550   }
00551 
00552   QString sLocation = i18n( "Location unspecified" );
00553   if ( ! event->location().isEmpty() ) {
00554     sLocation = string2HTML( event->location() );
00555   }
00556 
00557   QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
00558   html = QString("<div dir=\"%1\">\n").arg(dir);
00559 
00560   html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
00561 
00562   // Meeting summary & location rows
00563   html += invitationRow( i18n( "What:" ), sSummary );
00564   html += invitationRow( i18n( "Where:" ), sLocation );
00565 
00566   // Meeting Start Time Row
00567   if ( ! event->doesFloat() ) {
00568     tmp =  i18n("%1: Start Date, %2: Start Time", "%1 %2")
00569              .arg( event->dtStartDateStr(), event->dtStartTimeStr() );
00570   } else {
00571     tmp = i18n("%1: Start Date", "%1 (time unspecified)")
00572             .arg( event->dtStartDateStr() );
00573   }
00574   html += invitationRow( i18n( "Start Time:" ), tmp );
00575 
00576   // Meeting End Time Row
00577   if ( event->hasEndDate() ) {
00578     if ( ! event->doesFloat() ) {
00579       tmp =  i18n("%1: End Date, %2: End Time", "%1 %2")
00580                .arg( event->dtEndDateStr(), event->dtEndTimeStr() );
00581     } else {
00582       tmp = i18n("%1: End Date", "%1 (time unspecified)")
00583               .arg( event->dtEndDateStr() );
00584     }
00585   } else {
00586     tmp = i18n( "Unspecified" );
00587   }
00588   html += invitationRow( i18n( "End Time:" ), tmp );
00589 
00590   // Meeting Duration Row
00591   if ( !event->doesFloat() && event->hasEndDate() ) {
00592     tmp = QString::null;
00593     QTime sDuration(0,0,0), t;
00594     int secs = event->dtStart().secsTo( event->dtEnd() );
00595     t = sDuration.addSecs( secs );
00596     if ( t.hour() > 0 ) {
00597       tmp += i18n( "1 hour ", "%n hours ", t.hour() );
00598     }
00599     if ( t.minute() > 0 ) {
00600       tmp += i18n( "1 minute ", "%n minutes ",  t.minute() );
00601     }
00602 
00603     html += invitationRow( i18n( "Duration:" ), tmp );
00604   }
00605 
00606   html += "</table>\n";
00607   html += "</div>\n";
00608 
00609   return html;
00610 }
00611 
00612 static QString invitationDetailsTodo( Todo *todo )
00613 {
00614   // Task details are formatted into an HTML table
00615   if ( !todo )
00616     return QString::null;
00617 
00618   QString sSummary = i18n( "Summary unspecified" );
00619   QString sDescr = i18n( "Description unspecified" );
00620   if ( ! todo->summary().isEmpty() ) {
00621     sSummary = todo->summary();
00622   }
00623   if ( ! todo->description().isEmpty() ) {
00624     sDescr = todo->description();
00625   }
00626   QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00627   html += invitationRow( i18n( "Summary:" ), sSummary );
00628   html += invitationRow( i18n( "Description:" ), sDescr );
00629   html += "</table>\n";
00630 
00631   return html;
00632 }
00633 
00634 static QString invitationDetailsJournal( Journal *journal )
00635 {
00636   if ( !journal )
00637     return QString::null;
00638 
00639   QString sSummary = i18n( "Summary unspecified" );
00640   QString sDescr = i18n( "Description unspecified" );
00641   if ( ! journal->summary().isEmpty() ) {
00642     sSummary = journal->summary();
00643   }
00644   if ( ! journal->description().isEmpty() ) {
00645     sDescr = journal->description();
00646   }
00647   QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00648   html += invitationRow( i18n( "Summary:" ), sSummary );
00649   html += invitationRow( i18n( "Date:" ), journal->dtStartDateStr( false ) );
00650   html += invitationRow( i18n( "Description:" ), sDescr );
00651   html += "</table>\n";
00652 
00653   return html;
00654 }
00655 
00656 static QString invitationDetailsFreeBusy( FreeBusy *fb )
00657 {
00658   if ( !fb )
00659     return QString::null;
00660   QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
00661 
00662   html += invitationRow( i18n("Person:"), fb->organizer().fullName() );
00663   html += invitationRow( i18n("Start date:"), fb->dtStartDateStr() );
00664   html += invitationRow( i18n("End date:"),
00665       KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) );
00666   html += "<tr><td colspan=2><hr></td></tr>\n";
00667   html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
00668 
00669   QValueList<Period> periods = fb->busyPeriods();
00670 
00671   QValueList<Period>::iterator it;
00672   for ( it = periods.begin(); it != periods.end(); ++it ) {
00673     Period per = *it;
00674     if ( per.hasDuration() ) {
00675       int dur = per.duration().asSeconds();
00676       QString cont;
00677       if ( dur >= 3600 ) {
00678         cont += i18n("1 hour ", "%n hours ", dur / 3600);
00679         dur %= 3600;
00680       }
00681       if ( dur >= 60 ) {
00682         cont += i18n("1 minute", "%n minutes ", dur / 60);
00683         dur %= 60;
00684       }
00685       if ( dur > 0 ) {
00686         cont += i18n("1 second", "%n seconds", dur);
00687       }
00688       html += invitationRow( QString::null, i18n("startDate for duration", "%1 for %2")
00689           .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
00690           .arg(cont) );
00691     } else {
00692       QString cont;
00693       if ( per.start().date() == per.end().date() ) {
00694         cont = i18n("date, fromTime - toTime ", "%1, %2 - %3")
00695             .arg( KGlobal::locale()->formatDate( per.start().date() ) )
00696             .arg( KGlobal::locale()->formatTime( per.start().time() ) )
00697             .arg( KGlobal::locale()->formatTime( per.end().time() ) );
00698       } else {
00699         cont = i18n("fromDateTime - toDateTime", "%1 - %2")
00700           .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
00701           .arg( KGlobal::locale()->formatDateTime( per.end(), false ) );
00702       }
00703 
00704       html += invitationRow( QString::null, cont );
00705     }
00706   }
00707 
00708   html += "</table>\n";
00709   return html;
00710 }
00711 
00712 static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
00713 {
00714   if ( !msg || !event )
00715     return QString::null;
00716   switch ( msg->method() ) {
00717     case Scheduler::Publish:
00718         return i18n("This event has been published");
00719     case Scheduler::Request:
00720         return i18n( "You have been invited to this meeting" );
00721     case Scheduler::Refresh:
00722         return i18n( "This invitation was refreshed" );
00723     case Scheduler::Cancel:
00724         return i18n( "This meeting has been canceled" );
00725     case Scheduler::Add:
00726         return i18n( "Addition to the meeting invitation" );
00727     case Scheduler::Reply: {
00728         Attendee::List attendees = event->attendees();
00729         if( attendees.count() == 0 ) {
00730           kdDebug(5850) << "No attendees in the iCal reply!\n";
00731           return QString::null;
00732         }
00733         if( attendees.count() != 1 )
00734           kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
00735                         << "but is " << attendees.count() << endl;
00736         Attendee* attendee = *attendees.begin();
00737 
00738         switch( attendee->status() ) {
00739           case Attendee::NeedsAction:
00740               return i18n( "Sender indicates this invitation still needs some action" );
00741           case Attendee::Accepted:
00742               return i18n( "Sender accepts this meeting invitation" );
00743           case Attendee::Tentative:
00744               return i18n( "Sender tentatively accepts this meeting invitation" );
00745           case Attendee::Declined:
00746               return i18n( "Sender declines this meeting invitation" );
00747           case Attendee::Delegated:
00748               return i18n( "Sender has delegated this meeting invitation" );
00749           case Attendee::Completed:
00750               return i18n( "This meeting invitation is now completed" );
00751           case Attendee::InProcess:
00752               return i18n( "Sender is still processing the invitation" );
00753           default:
00754               return i18n( "Unknown response to this meeting invitation" );
00755         }
00756         break; }
00757     case Scheduler::Counter:
00758         return i18n( "Sender makes this counter proposal" );
00759     case Scheduler::Declinecounter:
00760         return i18n( "Sender declines the counter proposal" );
00761     case Scheduler::NoMethod:
00762         return i18n("Error: iMIP message with unknown method: '%1'")
00763             .arg( msg->method() );
00764   }
00765   return QString::null;
00766 }
00767 
00768 static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
00769 {
00770   if ( !msg || !todo )
00771     return QString::null;
00772   switch ( msg->method() ) {
00773     case Scheduler::Publish:
00774         return i18n("This task has been published");
00775     case Scheduler::Request:
00776         return i18n( "You have been assigned this task" );
00777     case Scheduler::Refresh:
00778         return i18n( "This task was refreshed" );
00779     case Scheduler::Cancel:
00780         return i18n( "This task was canceled" );
00781     case Scheduler::Add:
00782         return i18n( "Addition to the task" );
00783     case Scheduler::Reply: {
00784         Attendee::List attendees = todo->attendees();
00785         if( attendees.count() == 0 ) {
00786           kdDebug(5850) << "No attendees in the iCal reply!\n";
00787           return QString::null;
00788         }
00789         if( attendees.count() != 1 )
00790           kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
00791                         << "but is " << attendees.count() << endl;
00792         Attendee* attendee = *attendees.begin();
00793 
00794         switch( attendee->status() ) {
00795           case Attendee::NeedsAction:
00796               return i18n( "Sender indicates this task assignment still needs some action" );
00797           case Attendee::Accepted:
00798               return i18n( "Sender accepts this task" );
00799           case Attendee::Tentative:
00800               return i18n( "Sender tentatively accepts this task" );
00801           case Attendee::Declined:
00802               return i18n( "Sender declines this task" );
00803           case Attendee::Delegated:
00804               return i18n( "Sender has delegated this request for the task " );
00805           case Attendee::Completed:
00806               return i18n( "The request for this task is now completed" );
00807           case Attendee::InProcess:
00808               return i18n( "Sender is still processing the invitation" );
00809           default:
00810               return i18n( "Unknown response to this task" );
00811           }
00812         break; }
00813     case Scheduler::Counter:
00814         return i18n( "Sender makes this counter proposal" );
00815     case Scheduler::Declinecounter:
00816         return i18n( "Sender declines the counter proposal" );
00817     case Scheduler::NoMethod:
00818         return i18n("Error: iMIP message with unknown method: '%1'")
00819             .arg( msg->method() );
00820   }
00821   return QString::null;
00822 }
00823 
00824 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
00825 {
00826   // TODO: Several of the methods are not allowed for journals, so remove them.
00827   if ( !msg || !journal )
00828     return QString::null;
00829   switch ( msg->method() ) {
00830     case Scheduler::Publish:
00831         return i18n("This journal has been published");
00832     case Scheduler::Request:
00833         return i18n( "You have been assigned this journal" );
00834     case Scheduler::Refresh:
00835         return i18n( "This journal was refreshed" );
00836     case Scheduler::Cancel:
00837         return i18n( "This journal was canceled" );
00838     case Scheduler::Add:
00839         return i18n( "Addition to the journal" );
00840     case Scheduler::Reply: {
00841         Attendee::List attendees = journal->attendees();
00842         if( attendees.count() == 0 ) {
00843           kdDebug(5850) << "No attendees in the iCal reply!\n";
00844           return QString::null;
00845         }
00846         if( attendees.count() != 1 )
00847           kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
00848                         << "but is " << attendees.count() << endl;
00849         Attendee* attendee = *attendees.begin();
00850 
00851         switch( attendee->status() ) {
00852           case Attendee::NeedsAction:
00853               return i18n( "Sender indicates this journal assignment still needs some action" );
00854           case Attendee::Accepted:
00855               return i18n( "Sender accepts this journal" );
00856           case Attendee::Tentative:
00857               return i18n( "Sender tentatively accepts this journal" );
00858           case Attendee::Declined:
00859               return i18n( "Sender declines this journal" );
00860           case Attendee::Delegated:
00861               return i18n( "Sender has delegated this request for the journal" );
00862           case Attendee::Completed:
00863               return i18n( "The request for this journal is now completed" );
00864           case Attendee::InProcess:
00865               return i18n( "Sender is still processing the invitation" );
00866           default:
00867               return i18n( "Unknown response to this journal" );
00868           }
00869         break; }
00870     case Scheduler::Counter:
00871         return i18n( "Sender makes this counter proposal" );
00872     case Scheduler::Declinecounter:
00873         return i18n( "Sender declines the counter proposal" );
00874     case Scheduler::NoMethod:
00875         return i18n("Error: iMIP message with unknown method: '%1'")
00876             .arg( msg->method() );
00877   }
00878   return QString::null;
00879 }
00880 
00881 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
00882 {
00883   if ( !msg || !fb )
00884     return QString::null;
00885   switch ( msg->method() ) {
00886     case Scheduler::Publish:
00887         return i18n("This free/busy list has been published");
00888     case Scheduler::Request:
00889         return i18n( "The free/busy list has been requested" );
00890     case Scheduler::Refresh:
00891         return i18n( "This free/busy list was refreshed" );
00892     case Scheduler::Cancel:
00893         return i18n( "This free/busy list was canceled" );
00894     case Scheduler::Add:
00895         return i18n( "Addition to the free/busy list" );
00896     case Scheduler::NoMethod:
00897     default:
00898         return i18n("Error: Free/Busy iMIP message with unknown method: '%1'")
00899             .arg( msg->method() );
00900   }
00901 }
00902 
00903 class IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor
00904 {
00905   public:
00906     ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
00907     bool act( IncidenceBase *incidence, ScheduleMessage *msg ) { mMessage = msg; return incidence->accept( *this ); }
00908     QString result() const { return mResult; }
00909 
00910   protected:
00911     QString mResult;
00912     ScheduleMessage *mMessage;
00913 };
00914 
00915 class IncidenceFormatter::InvitationHeaderVisitor :
00916       public IncidenceFormatter::ScheduleMessageVisitor
00917 {
00918   protected:
00919     bool visit( Event *event )
00920     {
00921       mResult = invitationHeaderEvent( event, mMessage );
00922       return !mResult.isEmpty();
00923     }
00924     bool visit( Todo *todo )
00925     {
00926       mResult = invitationHeaderTodo( todo, mMessage );
00927       return !mResult.isEmpty();
00928     }
00929     bool visit( Journal *journal )
00930     {
00931       mResult = invitationHeaderJournal( journal, mMessage );
00932       return !mResult.isEmpty();
00933     }
00934     bool visit( FreeBusy *fb )
00935     {
00936       mResult = invitationHeaderFreeBusy( fb, mMessage );
00937       return !mResult.isEmpty();
00938     }
00939 };
00940 
00941 class IncidenceFormatter::InvitationBodyVisitor :
00942       public IncidenceFormatter::ScheduleMessageVisitor
00943 {
00944   protected:
00945     bool visit( Event *event )
00946     {
00947       mResult = invitationDetailsEvent( event );
00948       return !mResult.isEmpty();
00949     }
00950     bool visit( Todo *todo )
00951     {
00952       mResult = invitationDetailsTodo( todo );
00953       return !mResult.isEmpty();
00954     }
00955     bool visit( Journal *journal )
00956     {
00957       mResult = invitationDetailsJournal( journal );
00958       return !mResult.isEmpty();
00959     }
00960     bool visit( FreeBusy *fb )
00961     {
00962       mResult = invitationDetailsFreeBusy( fb );
00963       return !mResult.isEmpty();
00964     }
00965 };
00966 
00967 
00968 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
00969 {
00970   QString res( "<a href=\"%1\"><b>%2</b></a>" );
00971   return res.arg( generateLinkURL( id ) ).arg( text );
00972   return res;
00973 }
00974 
00975 
00976 QString IncidenceFormatter::formatICalInvitation( QString invitation, Calendar *mCalendar,
00977     InvitationFormatterHelper *helper )
00978 {
00979   if ( invitation.isEmpty() ) return QString::null;
00980 
00981   ICalFormat format;
00982   // parseScheduleMessage takes the tz from the calendar, no need to set it manually here for the format!
00983   ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
00984 
00985   if( !msg ) {
00986     kdDebug( 5850 ) << "Failed to parse the scheduling message" << endl;
00987     Q_ASSERT( format.exception() );
00988     kdDebug( 5850 ) << format.exception()->message() << endl;
00989     return QString::null;
00990   }
00991 
00992   IncidenceBase *incBase = msg->event();
00993 
00994   // First make the text of the message
00995   QString html;
00996 
00997   QString tableStyle = QString::fromLatin1(
00998     "style=\"border: solid 1px; margin: 0em;\"" );
00999   QString tableHead = QString::fromLatin1(
01000     "<div align=\"center\">"
01001     "<table width=\"80%\" cellpadding=\"1\" cellspacing=\"0\" %1>"
01002     "<tr><td>").arg(tableStyle);
01003 
01004   html += tableHead;
01005   InvitationHeaderVisitor headerVisitor;
01006   // The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled
01007   if ( !headerVisitor.act( incBase, msg ) )
01008     return QString::null;
01009   html += "<h2>" + headerVisitor.result() + "</h2>";
01010 
01011   InvitationBodyVisitor bodyVisitor;
01012   if ( !bodyVisitor.act( incBase, msg ) )
01013     return QString::null;
01014   html += bodyVisitor.result();
01015 
01016   html += "<br>&nbsp;<br>&nbsp;<br>";
01017   html += "<table border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td><td>";
01018 
01019 #if 0
01020   html += helper->makeLinkURL( "accept", i18n("[Enter this into my calendar]") );
01021   html += "</td><td> &nbsp; </td><td>";
01022 #endif
01023 
01024   // Add groupware links
01025 
01026   switch ( msg->method() ) {
01027     case Scheduler::Publish:
01028     case Scheduler::Request:
01029     case Scheduler::Refresh:
01030     case Scheduler::Add:
01031         // Accept
01032         html += helper->makeLink( "accept", i18n( "[Accept]" ) );
01033         html += "</td><td> &nbsp; </td><td>";
01034         html += helper->makeLink( "accept_conditionally",
01035                           i18n( "Accept conditionally", "[Accept cond.]" ) );
01036         html += "</td><td> &nbsp; </td><td>";
01037         // Decline
01038         html += helper->makeLink( "decline", i18n( "[Decline]" ) );
01039 #if 0
01040         // TODO: implement this
01041         html += "</b></a></td><td> &nbsp; </td><td>";
01042         html += helper->makeLink( "check_calendar", i18n("[Check my calendar...]" ) );
01043 #endif
01044         break;
01045 
01046     case Scheduler::Cancel:
01047         // Cancel event from my calendar
01048         html += helper->makeLink( "cancel", i18n( "[Remove this from my calendar]" ) );
01049         break;
01050 
01051     case Scheduler::Reply:
01052         // Enter this into my calendar
01053         if ( incBase->type() == "Todo" ) {
01054           html += helper->makeLink( "reply", i18n( "[Enter this into my task list]" ) );
01055         } else {
01056           html += helper->makeLink( "reply", i18n( "[Enter this into my calendar]" ) );
01057         }
01058         break;
01059 
01060     case Scheduler::Counter:
01061     case Scheduler::Declinecounter:
01062     case Scheduler::NoMethod:
01063         break;
01064   }
01065 
01066   html += "</td></tr></table>";
01067 
01068   Incidence* incidence = dynamic_cast<Incidence*>( incBase );
01069   if ( incidence ) {
01070     QString sDescr = incidence->description();
01071     if( ( msg->method() == Scheduler::Request || msg->method() == Scheduler::Cancel ) &&
01072         !sDescr.isEmpty() ) {
01073       html += "<br>&nbsp;<br>&nbsp;<br><u>" + i18n("Description:")
01074         + "</u><br><table border=\"0\"><tr><td>&nbsp;</td><td>";
01075       html += string2HTML(sDescr) + "</td></tr></table>";
01076     }
01077   }
01078 
01079   html += "</td></tr></table><br></div>";
01080 
01081   return html;
01082 }
01083 
01084 
01085 
01086 
01087 /*******************************************************************
01088  *  Helper functions for the msTNEF -> VPart converter
01089  *******************************************************************/
01090 
01091 
01092 //-----------------------------------------------------------------------------
01093 
01094 static QString stringProp( KTNEFMessage* tnefMsg, const Q_UINT32& key,
01095                            const QString& fallback = QString::null)
01096 {
01097   return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16,
01098                             fallback );
01099 }
01100 
01101 static QString sNamedProp( KTNEFMessage* tnefMsg, const QString& name,
01102                            const QString& fallback = QString::null )
01103 {
01104   return tnefMsg->findNamedProp( name, fallback );
01105 }
01106 
01107 struct save_tz { char* old_tz; char* tz_env_str; };
01108 
01109 /* temporarily go to a different timezone */
01110 static struct save_tz set_tz( const char* _tc )
01111 {
01112   const char *tc = _tc?_tc:"UTC";
01113 
01114   struct save_tz rv;
01115 
01116   rv.old_tz = 0;
01117   rv.tz_env_str = 0;
01118 
01119   //kdDebug(5006) << "set_tz(), timezone before = " << timezone << endl;
01120 
01121   char* tz_env = 0;
01122   if( getenv( "TZ" ) ) {
01123     tz_env = strdup( getenv( "TZ" ) );
01124     rv.old_tz = tz_env;
01125   }
01126   char* tmp_env = (char*)malloc( strlen( tc ) + 4 );
01127   strcpy( tmp_env, "TZ=" );
01128   strcpy( tmp_env+3, tc );
01129   putenv( tmp_env );
01130 
01131   rv.tz_env_str = tmp_env;
01132 
01133   /* tmp_env is not free'ed -- it is part of the environment */
01134 
01135   tzset();
01136   //kdDebug(5006) << "set_tz(), timezone after = " << timezone << endl;
01137 
01138   return rv;
01139 }
01140 
01141 /* restore previous timezone */
01142 static void unset_tz( struct save_tz old_tz )
01143 {
01144   if( old_tz.old_tz ) {
01145     char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 );
01146     strcpy( tmp_env, "TZ=" );
01147     strcpy( tmp_env+3, old_tz.old_tz );
01148     putenv( tmp_env );
01149     /* tmp_env is not free'ed -- it is part of the environment */
01150     free( old_tz.old_tz );
01151   } else {
01152     /* clear TZ from env */
01153     putenv( strdup("TZ") );
01154   }
01155   tzset();
01156 
01157   /* is this OK? */
01158   if( old_tz.tz_env_str ) free( old_tz.tz_env_str );
01159 }
01160 
01161 static QDateTime utc2Local( const QDateTime& utcdt )
01162 {
01163   struct tm tmL;
01164 
01165   save_tz tmp_tz = set_tz("UTC");
01166   time_t utc = utcdt.toTime_t();
01167   unset_tz( tmp_tz );
01168 
01169   localtime_r( &utc, &tmL );
01170   return QDateTime( QDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ),
01171                     QTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) );
01172 }
01173 
01174 
01175 static QDateTime pureISOToLocalQDateTime( const QString& dtStr,
01176                                           bool bDateOnly = false )
01177 {
01178   QDate tmpDate;
01179   QTime tmpTime;
01180   int year, month, day, hour, minute, second;
01181 
01182   if( bDateOnly ) {
01183     year = dtStr.left( 4 ).toInt();
01184     month = dtStr.mid( 4, 2 ).toInt();
01185     day = dtStr.mid( 6, 2 ).toInt();
01186     hour = 0;
01187     minute = 0;
01188     second = 0;
01189   } else {
01190     year = dtStr.left( 4 ).toInt();
01191     month = dtStr.mid( 4, 2 ).toInt();
01192     day = dtStr.mid( 6, 2 ).toInt();
01193     hour = dtStr.mid( 9, 2 ).toInt();
01194     minute = dtStr.mid( 11, 2 ).toInt();
01195     second = dtStr.mid( 13, 2 ).toInt();
01196   }
01197   tmpDate.setYMD( year, month, day );
01198   tmpTime.setHMS( hour, minute, second );
01199 
01200   if( tmpDate.isValid() && tmpTime.isValid() ) {
01201     QDateTime dT = QDateTime( tmpDate, tmpTime );
01202 
01203     if( !bDateOnly ) {
01204       // correct for GMT ( == Zulu time == UTC )
01205       if (dtStr.at(dtStr.length()-1) == 'Z') {
01206         //dT = dT.addSecs( 60 * KRFCDate::localUTCOffset() );
01207         //localUTCOffset( dT ) );
01208         dT = utc2Local( dT );
01209       }
01210     }
01211     return dT;
01212   } else
01213     return QDateTime();
01214 }
01215 
01216 
01217 
01218 QString IncidenceFormatter::msTNEFToVPart( const QByteArray& tnef )
01219 {
01220   bool bOk = false;
01221 
01222   KTNEFParser parser;
01223   QBuffer buf( tnef );
01224   CalendarLocal cal ( QString::fromLatin1( "UTC" ) );
01225   KABC::Addressee addressee;
01226   KABC::VCardConverter cardConv;
01227   ICalFormat calFormat;
01228   Event* event = new Event();
01229 
01230   if( parser.openDevice( &buf ) ) {
01231     KTNEFMessage* tnefMsg = parser.message();
01232     //QMap<int,KTNEFProperty*> props = parser.message()->properties();
01233 
01234     // Everything depends from property PR_MESSAGE_CLASS
01235     // (this is added by KTNEFParser):
01236     QString msgClass = tnefMsg->findProp( 0x001A, QString::null, true )
01237       .upper();
01238     if( !msgClass.isEmpty() ) {
01239       // Match the old class names that might be used by Outlook for
01240       // compatibility with Microsoft Mail for Windows for Workgroups 3.1.
01241       bool bCompatClassAppointment = false;
01242       bool bCompatMethodRequest = false;
01243       bool bCompatMethodCancled = false;
01244       bool bCompatMethodAccepted = false;
01245       bool bCompatMethodAcceptedCond = false;
01246       bool bCompatMethodDeclined = false;
01247       if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) {
01248         bCompatClassAppointment = true;
01249         if( msgClass.endsWith( ".MTGREQ" ) )
01250           bCompatMethodRequest = true;
01251         if( msgClass.endsWith( ".MTGCNCL" ) )
01252           bCompatMethodCancled = true;
01253         if( msgClass.endsWith( ".MTGRESPP" ) )
01254           bCompatMethodAccepted = true;
01255         if( msgClass.endsWith( ".MTGRESPA" ) )
01256           bCompatMethodAcceptedCond = true;
01257         if( msgClass.endsWith( ".MTGRESPN" ) )
01258           bCompatMethodDeclined = true;
01259       }
01260       bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" );
01261 
01262       if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) {
01263         // Compose a vCal
01264         bool bIsReply = false;
01265         QString prodID = "-//Microsoft Corporation//Outlook ";
01266         prodID += tnefMsg->findNamedProp( "0x8554", "9.0" );
01267         prodID += "MIMEDIR/EN\n";
01268         prodID += "VERSION:2.0\n";
01269         calFormat.setApplication( "Outlook", prodID );
01270 
01271         Scheduler::Method method;
01272         if( bCompatMethodRequest )
01273           method = Scheduler::Request;
01274         else if( bCompatMethodCancled )
01275           method = Scheduler::Cancel;
01276         else if( bCompatMethodAccepted || bCompatMethodAcceptedCond ||
01277                  bCompatMethodDeclined ) {
01278           method = Scheduler::Reply;
01279           bIsReply = true;
01280         } else {
01281           // pending(khz): verify whether "0x0c17" is the right tag ???
01282           //
01283           // at the moment we think there are REQUESTS and UPDATES
01284           //
01285           // but WHAT ABOUT REPLIES ???
01286           //
01287           //
01288 
01289           if( tnefMsg->findProp(0x0c17) == "1" )
01290             bIsReply = true;
01291           method = Scheduler::Request;
01292         }
01293 
01295         ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown );
01296 
01297         QString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) );
01298 
01299         if( !sSenderSearchKeyEmail.isEmpty() ) {
01300           int colon = sSenderSearchKeyEmail.find( ':' );
01301           // May be e.g. "SMTP:KHZ@KDE.ORG"
01302           if( sSenderSearchKeyEmail.find( ':' ) == -1 )
01303             sSenderSearchKeyEmail.remove( 0, colon+1 );
01304         }
01305 
01306         QString s( tnefMsg->findProp( 0x0e04 ) );
01307         QStringList attendees = QStringList::split( ';', s );
01308         if( attendees.count() ) {
01309           for( QStringList::Iterator it = attendees.begin();
01310                it != attendees.end(); ++it ) {
01311             // Skip all entries that have no '@' since these are
01312             // no mail addresses
01313             if( (*it).find('@') == -1 ) {
01314               s = (*it).stripWhiteSpace();
01315 
01316               Attendee *attendee = new Attendee( s, s, true );
01317               if( bIsReply ) {
01318                 if( bCompatMethodAccepted )
01319                   attendee->setStatus( Attendee::Accepted );
01320                 if( bCompatMethodDeclined )
01321                   attendee->setStatus( Attendee::Declined );
01322                 if( bCompatMethodAcceptedCond )
01323                   attendee->setStatus(Attendee::Tentative);
01324               } else {
01325                 attendee->setStatus( Attendee::NeedsAction );
01326                 attendee->setRole( Attendee::ReqParticipant );
01327               }
01328               event->addAttendee(attendee);
01329             }
01330           }
01331         } else {
01332           // Oops, no attendees?
01333           // This must be old style, let us use the PR_SENDER_SEARCH_KEY.
01334           s = sSenderSearchKeyEmail;
01335           if( !s.isEmpty() ) {
01336             Attendee *attendee = new Attendee( QString::null, QString::null,
01337                                                true );
01338             if( bIsReply ) {
01339               if( bCompatMethodAccepted )
01340                 attendee->setStatus( Attendee::Accepted );
01341               if( bCompatMethodAcceptedCond )
01342                 attendee->setStatus( Attendee::Declined );
01343               if( bCompatMethodDeclined )
01344                 attendee->setStatus( Attendee::Tentative );
01345             } else {
01346               attendee->setStatus(Attendee::NeedsAction);
01347               attendee->setRole(Attendee::ReqParticipant);
01348             }
01349             event->addAttendee(attendee);
01350           }
01351         }
01352         s = tnefMsg->findProp( 0x0c1f ); // look for organizer property
01353         if( s.isEmpty() && !bIsReply )
01354           s = sSenderSearchKeyEmail;
01355         // TODO: Use the common name?
01356         if( !s.isEmpty() )
01357           event->setOrganizer( s );
01358 
01359         s = tnefMsg->findProp( 0x8516 ).replace( QChar( '-' ), QString::null )
01360           .replace( QChar( ':' ), QString::null );
01361         event->setDtStart( QDateTime::fromString( s ) ); // ## Format??
01362 
01363         s = tnefMsg->findProp( 0x8517 ).replace( QChar( '-' ), QString::null )
01364           .replace( QChar( ':' ), QString::null );
01365         event->setDtEnd( QDateTime::fromString( s ) );
01366 
01367         s = tnefMsg->findProp( 0x8208 );
01368         event->setLocation( s );
01369 
01370         // is it OK to set this to OPAQUE always ??
01371         //vPart += "TRANSP:OPAQUE\n"; ###FIXME, portme!
01372         //vPart += "SEQUENCE:0\n";
01373 
01374         // is "0x0023" OK  -  or should we look for "0x0003" ??
01375         s = tnefMsg->findProp( 0x0023 );
01376         event->setUid( s );
01377 
01378         // PENDING(khz): is this value in local timezone? Must it be
01379         // adjusted? Most likely this is a bug in the server or in
01380         // Outlook - we ignore it for now.
01381         s = tnefMsg->findProp( 0x8202 ).replace( QChar( '-' ), QString::null )
01382           .replace( QChar( ':' ), QString::null );
01383         // ### libkcal always uses currentDateTime()
01384         // event->setDtStamp(QDateTime::fromString(s));
01385 
01386         s = tnefMsg->findNamedProp( "Keywords" );
01387         event->setCategories( s );
01388 
01389         s = tnefMsg->findProp( 0x1000 );
01390         event->setDescription( s );
01391 
01392         s = tnefMsg->findProp( 0x0070 );
01393         event->setSummary( s );
01394 
01395         s = tnefMsg->findProp( 0x0026 );
01396         event->setPriority( s.toInt() );
01397 
01398         // is reminder flag set ?
01399         if(!tnefMsg->findProp(0x8503).isEmpty()) {
01400           Alarm *alarm = new Alarm(event);
01401           QDateTime highNoonTime =
01402             pureISOToLocalQDateTime( tnefMsg->findProp( 0x8502 )
01403                                      .replace( QChar( '-' ), "" )
01404                                      .replace( QChar( ':' ), "" ) );
01405           QDateTime wakeMeUpTime =
01406             pureISOToLocalQDateTime( tnefMsg->findProp( 0x8560, "" )
01407                                      .replace( QChar( '-' ), "" )
01408                                      .replace( QChar( ':' ), "" ) );
01409           alarm->setTime(wakeMeUpTime);
01410 
01411           if( highNoonTime.isValid() && wakeMeUpTime.isValid() )
01412             alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) );
01413           else
01414             // default: wake them up 15 minutes before the appointment
01415             alarm->setStartOffset( Duration( 15*60 ) );
01416           alarm->setDisplayAlarm( i18n( "Reminder" ) );
01417 
01418           // Sorry: the different action types are not known (yet)
01419           //        so we always set 'DISPLAY' (no sounds, no images...)
01420           event->addAlarm( alarm );
01421         }
01422         cal.addEvent( event );
01423         bOk = true;
01424         // we finished composing a vCal
01425       } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) {
01426         addressee.setUid( stringProp( tnefMsg, attMSGID ) );
01427         addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) );
01428         addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true );
01429         addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false );
01430         addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false );
01431         addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) );
01432         addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) );
01433         addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) );
01434         addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) );
01435         addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) );
01436         addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) );
01437         addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) );
01438 
01439         QString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY )
01440           .replace( QChar( '-' ), QString::null )
01441           .replace( QChar( ':' ), QString::null );
01442         if( !s.isEmpty() )
01443           addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s );
01444 
01445         addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE )  ) );
01446 
01447         // collect parts of Name entry
01448         addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) );
01449         addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) );
01450         addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) );
01451         addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) );
01452         addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) );
01453 
01454         addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) );
01455         addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) );
01456         addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) );
01457         /*
01458         the MAPI property ID of this (multiline) )field is unknown:
01459         vPart += stringProp(tnefMsg, "\n","NOTE", ... , "" );
01460         */
01461 
01462         KABC::Address adr;
01463         adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) );
01464         adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) );
01465         adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) );
01466         adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) );
01467         adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) );
01468         adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) );
01469         adr.setType(KABC::Address::Home);
01470         addressee.insertAddress(adr);
01471 
01472         adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) );
01473         adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) );
01474         adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) );
01475         adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) );
01476         adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) );
01477         adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) );
01478         adr.setType( KABC::Address::Work );
01479         addressee.insertAddress( adr );
01480 
01481         adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) );
01482         adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) );
01483         adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) );
01484         adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) );
01485         adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) );
01486         adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) );
01487         adr.setType( KABC::Address::Dom );
01488         addressee.insertAddress(adr);
01489 
01490         // problem: the 'other' address was stored by KOrganizer in
01491         //          a line looking like the following one:
01492         // vPart += "\nADR;TYPE=dom;TYPE=intl;TYPE=parcel;TYPE=postal;TYPE=work;TYPE=home:other_pobox;;other_str1\nother_str2;other_loc;other_region;other_pocode;other_country
01493 
01494         QString nr;
01495         nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER );
01496         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) );
01497         nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER );
01498         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) );
01499         nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER );
01500         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) );
01501         nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER );
01502         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) );
01503         nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER );
01504         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) );
01505 
01506         s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY )
01507           .replace( QChar( '-' ), QString::null )
01508           .replace( QChar( ':' ), QString::null );
01509         if( !s.isEmpty() )
01510           addressee.setBirthday( QDateTime::fromString( s ) );
01511 
01512         bOk = ( !addressee.isEmpty() );
01513       } else if( "IPM.NOTE" == msgClass ) {
01514 
01515       } // else if ... and so on ...
01516     }
01517   }
01518 
01519   // Compose return string
01520   QString iCal = calFormat.toString( &cal );
01521   if( !iCal.isEmpty() )
01522     // This was an iCal
01523     return iCal;
01524 
01525   // Not an iCal - try a vCard
01526   KABC::VCardConverter converter;
01527   return converter.createVCard( addressee );
01528 }
01529 
01530 
01531 QString IncidenceFormatter::formatTNEFInvitation( const QByteArray& tnef,
01532         Calendar *mCalendar, InvitationFormatterHelper *helper )
01533 {
01534   QString vPart = IncidenceFormatter::msTNEFToVPart( tnef );
01535   QString iCal = IncidenceFormatter::formatICalInvitation( vPart, mCalendar, helper );
01536   if( !iCal.isEmpty() )
01537     return iCal;
01538   return vPart;
01539 }
01540 
01541 
01542 
01543 
01544 /*******************************************************************
01545  *  Helper functions for the Incidence tooltips
01546  *******************************************************************/
01547 
01548 class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
01549 {
01550   public:
01551     ToolTipVisitor() : mRichText( true ), mResult( "" ) {}
01552 
01553     bool act( IncidenceBase *incidence, bool richText=true)
01554     {
01555       mRichText = richText;
01556       mResult = "";
01557       return incidence ? incidence->accept( *this ) : false;
01558     }
01559     QString result() const { return mResult; }
01560 
01561   protected:
01562     bool visit( Event *event );
01563     bool visit( Todo *todo );
01564     bool visit( Journal *journal );
01565     bool visit( FreeBusy *fb );
01566 
01567     QString dateRangeText( Event*event );
01568     QString dateRangeText( Todo *todo );
01569     QString dateRangeText( Journal *journal );
01570     QString dateRangeText( FreeBusy *fb );
01571 
01572     QString generateToolTip( Incidence* incidence, QString dtRangeText );
01573 
01574   protected:
01575     bool mRichText;
01576     QString mResult;
01577 };
01578 
01579 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event*event )
01580 {
01581   QString ret;
01582   QString tmp;
01583   if ( event->isMultiDay() ) {
01584 
01585     tmp = "<br>" + i18n("Event start", "<i>From:</i>&nbsp;%1");
01586     if (event->doesFloat())
01587       ret += tmp.arg( event->dtStartDateStr().replace(" ", "&nbsp;") );
01588     else
01589       ret += tmp.arg( event->dtStartStr().replace(" ", "&nbsp;") );
01590 
01591     tmp = "<br>" + i18n("Event end","<i>To:</i>&nbsp;%1");
01592     if (event->doesFloat())
01593       ret += tmp.arg( event->dtEndDateStr().replace(" ", "&nbsp;") );
01594     else
01595       ret += tmp.arg( event->dtEndStr().replace(" ", "&nbsp;") );
01596 
01597   } else {
01598 
01599     ret += "<br>"+i18n("<i>Date:</i>&nbsp;%1").
01600         arg( event->dtStartDateStr().replace(" ", "&nbsp;") );
01601     if ( !event->doesFloat() ) {
01602       if ( event->dtStartTimeStr() == event->dtEndTimeStr() ) { // to prevent 'Time: 17:00 - 17:00'
01603         tmp = "<br>" + i18n("time for event, &nbsp; to prevent ugly line breaks",
01604         "<i>Time:</i>&nbsp;%1").
01605         arg( event->dtStartTimeStr().replace(" ", "&nbsp;") );
01606       } else {
01607         tmp = "<br>" + i18n("time range for event, &nbsp; to prevent ugly line breaks",
01608         "<i>Time:</i>&nbsp;%1&nbsp;-&nbsp;%2").
01609         arg( event->dtStartTimeStr().replace(" ", "&nbsp;") ).
01610         arg( event->dtEndTimeStr().replace(" ", "&nbsp;") );
01611       }
01612       ret += tmp;
01613     }
01614 
01615   }
01616   return ret;
01617 }
01618 
01619 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo*todo )
01620 {
01621   QString ret;
01622   bool floats( todo->doesFloat() );
01623   if (todo->hasStartDate())
01624     // No need to add <i> here. This is separated issue and each line
01625     // is very visible on its own. On the other hand... Yes, I like it
01626     // italics here :)
01627     ret += "<br>" + i18n("<i>Start:</i>&nbsp;%1").arg(
01628       (floats)
01629         ?(todo->dtStartDateStr().replace(" ", "&nbsp;"))
01630         :(todo->dtStartStr().replace(" ", "&nbsp;")) ) ;
01631   if (todo->hasDueDate())
01632     ret += "<br>" + i18n("<i>Due:</i>&nbsp;%1").arg(
01633       (floats)
01634         ?(todo->dtDueDateStr().replace(" ", "&nbsp;"))
01635         :(todo->dtDueStr().replace(" ", "&nbsp;")) );
01636   if (todo->isCompleted())
01637     ret += "<br>" + i18n("<i>Completed:</i>&nbsp;%1").arg( todo->completedStr().replace(" ", "&nbsp;") );
01638   else
01639     ret += "<br>" + i18n("%1 % completed").arg(todo->percentComplete());
01640 
01641   return ret;
01642 }
01643 
01644 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal )
01645 {
01646   QString ret;
01647   if (journal->dtStart().isValid() ) {
01648     ret += "<br>" + i18n("<i>Date:</i>&nbsp;%1").arg( journal->dtStartDateStr( false ) );
01649   }
01650   return ret;
01651 }
01652 
01653 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
01654 {
01655   QString tmp( "<br>" + i18n("<i>Period start:</i>&nbsp;%1") );
01656   QString ret = tmp.arg( KGlobal::locale()->formatDateTime( fb->dtStart() ) );
01657   tmp = "<br>" + i18n("<i>Period start:</i>&nbsp;%1");
01658   ret += tmp.arg( KGlobal::locale()->formatDateTime( fb->dtEnd() ) );
01659   return ret;
01660 }
01661 
01662 
01663 
01664 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
01665 {
01666   mResult = generateToolTip( event, dateRangeText( event ) );
01667   return !mResult.isEmpty();
01668 }
01669 
01670 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
01671 {
01672   mResult = generateToolTip( todo, dateRangeText( todo ) );
01673   return !mResult.isEmpty();
01674 }
01675 
01676 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
01677 {
01678   mResult = generateToolTip( journal, dateRangeText( journal ) );
01679   return !mResult.isEmpty();
01680 }
01681 
01682 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
01683 {
01684   mResult = "<qt><b>" + i18n("Free/Busy information for %1")
01685         .arg(fb->organizer().fullName()) + "</b>";
01686   mResult += dateRangeText( fb );
01687   mResult += "</qt>";
01688   return !mResult.isEmpty();
01689 }
01690 
01691 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, QString dtRangeText )
01692 {
01693   QString tmp = "<qt><b>"+ incidence->summary().replace("\n", "<br>")+"</b>";
01694 
01695   tmp += dtRangeText;
01696 
01697   if (!incidence->location().isEmpty()) {
01698     // Put Location: in italics
01699     tmp += "<br>"+i18n("<i>Location:</i>&nbsp;%1").
01700       arg( incidence->location().replace("\n", "<br>") );
01701   }
01702   if (!incidence->description().isEmpty()) {
01703     QString desc(incidence->description());
01704     if (desc.length()>120) {
01705       desc = desc.left(120) + "...";
01706     }
01707     tmp += "<br>----------<br>" + i18n("<i>Description:</i><br>") + desc.replace("\n", "<br>");
01708   }
01709   tmp += "</qt>";
01710   return tmp;
01711 }
01712 
01713 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText )
01714 {
01715   ToolTipVisitor v;
01716   if ( v.act( incidence, richText ) ) {
01717     return v.result();
01718   } else
01719     return QString::null;
01720 }
01721 
01722 
01723 
01724 
01725 /*******************************************************************
01726  *  Helper functions for the Incidence tooltips
01727  *******************************************************************/
01728 
01729 class IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor
01730 {
01731   public:
01732     MailBodyVisitor() : mResult( "" ) {}
01733 
01734     bool act( IncidenceBase *incidence )
01735     {
01736       mResult = "";
01737       return incidence ? incidence->accept( *this ) : false;
01738     }
01739     QString result() const { return mResult; }
01740 
01741   protected:
01742     bool visit( Event *event );
01743     bool visit( Todo *todo );
01744     bool visit( Journal *journal );
01745     bool visit( FreeBusy * ) { mResult = i18n("This is a Free Busy Object"); return !mResult.isEmpty(); }
01746   protected:
01747     QString mResult;
01748 };
01749 
01750 
01751 static QString mailBodyIncidence( Incidence *incidence )
01752 {
01753   QString body;
01754   if ( !incidence->summary().isEmpty() ) {
01755     body += i18n("Summary: %1\n").arg( incidence->summary() );
01756   }
01757   if ( !incidence->organizer().isEmpty() ) {
01758     body += i18n("Organizer: %1\n").arg( incidence->organizer().fullName() );
01759   }
01760   if ( !incidence->location().isEmpty() ) {
01761     body += i18n("Location: %1\n").arg( incidence->location() );
01762   }
01763   return body;
01764 }
01765 
01766 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
01767 {
01768   QString recurrence[]= {i18n("no recurrence", "None"),
01769     i18n("Minutely"), i18n("Hourly"), i18n("Daily"),
01770     i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"),
01771     i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
01772 
01773   mResult = mailBodyIncidence( event );
01774   mResult += i18n("Start Date: %1\n").arg( event->dtStartDateStr() );
01775   if ( !event->doesFloat() ) {
01776     mResult += i18n("Start Time: %1\n").arg( event->dtStartTimeStr() );
01777   }
01778   if ( event->dtStart() != event->dtEnd() ) {
01779     mResult += i18n("End Date: %1\n").arg( event->dtEndDateStr() );
01780   }
01781   if ( !event->doesFloat() ) {
01782     mResult += i18n("End Time: %1\n").arg( event->dtEndTimeStr() );
01783   }
01784   if ( event->doesRecur() ) {
01785     Recurrence *recur = event->recurrence();
01786     // TODO: Merge these two to one of the form "Recurs every 3 days"
01787     mResult += i18n("Recurs: %1\n")
01788              .arg( recurrence[ recur->recurrenceType() ] );
01789     mResult += i18n("Frequency: %1\n")
01790              .arg( event->recurrence()->frequency() );
01791 
01792     if ( recur->duration() > 0 ) {
01793       mResult += i18n ("Repeats once", "Repeats %n times", recur->duration());
01794       mResult += '\n';
01795     } else {
01796       if ( recur->duration() != -1 ) {
01797 // TODO_Recurrence: What to do with floating
01798         QString endstr;
01799         if ( event->doesFloat() ) {
01800           endstr = KGlobal::locale()->formatDate( recur->endDate() );
01801         } else {
01802           endstr = KGlobal::locale()->formatDateTime( recur->endDateTime() );
01803         }
01804         mResult += i18n("Repeat until: %1\n").arg( endstr );
01805       } else {
01806         mResult += i18n("Repeats forever\n");
01807       }
01808     }
01809   }
01810   QString details = event->description();
01811   if ( !details.isEmpty() ) {
01812     mResult += i18n("Details:\n%1\n").arg( details );
01813   }
01814   return !mResult.isEmpty();
01815 }
01816 
01817 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
01818 {
01819   mResult = mailBodyIncidence( todo );
01820 
01821   if ( todo->hasStartDate() ) {
01822     mResult += i18n("Start Date: %1\n").arg( todo->dtStartDateStr() );
01823     if ( !todo->doesFloat() ) {
01824       mResult += i18n("Start Time: %1\n").arg( todo->dtStartTimeStr() );
01825     }
01826   }
01827   if ( todo->hasDueDate() ) {
01828     mResult += i18n("Due Date: %1\n").arg( todo->dtDueDateStr() );
01829     if ( !todo->doesFloat() ) {
01830       mResult += i18n("Due Time: %1\n").arg( todo->dtDueTimeStr() );
01831     }
01832   }
01833   QString details = todo->description();
01834   if ( !details.isEmpty() ) {
01835     mResult += i18n("Details:\n%1\n").arg( details );
01836   }
01837   return !mResult.isEmpty();
01838 }
01839 
01840 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
01841 {
01842   mResult = mailBodyIncidence( journal );
01843   mResult += i18n("Date: %1\n").arg( journal->dtStartDateStr() );
01844   if ( !journal->doesFloat() ) {
01845     mResult += i18n("Time: %1\n").arg( journal->dtStartTimeStr() );
01846   }
01847   if ( !journal->description().isEmpty() )
01848     mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() );
01849   return !mResult.isEmpty();
01850 }
01851 
01852 
01853 
01854 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
01855 {
01856   if ( !incidence )
01857     return QString::null;
01858 
01859   MailBodyVisitor v;
01860   if ( v.act( incidence ) ) {
01861     return v.result();
01862   }
01863   return QString::null;
01864 }
KDE Home | KDE Accessibility Home | Description of Access Keys