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 #include <libkcal/calendarresources.h>
00034 
00035 #include <libemailfunctions/email.h>
00036 
00037 #include <ktnef/ktnefparser.h>
00038 #include <ktnef/ktnefmessage.h>
00039 #include <ktnef/ktnefdefs.h>
00040 #include <kabc/phonenumber.h>
00041 #include <kabc/vcardconverter.h>
00042 #include <kabc/stdaddressbook.h>
00043 
00044 #include <kapplication.h>
00045 #include <kemailsettings.h>
00046 // #include <kdebug.h>
00047 
00048 #include <klocale.h>
00049 #include <kglobal.h>
00050 #include <kiconloader.h>
00051 #include <kcalendarsystem.h>
00052 #include <kmimetype.h>
00053 
00054 #include <qbuffer.h>
00055 #include <qstylesheet.h>
00056 #include <qdatetime.h>
00057 
00058 #include <time.h>
00059 
00060 using namespace KCal;
00061 
00062 /*******************
00063  *  General helpers
00064  *******************/
00065 
00066 static QString htmlAddLink( const QString &ref, const QString &text,
00067                              bool newline = true )
00068 {
00069   QString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
00070   if ( newline ) tmpStr += "\n";
00071   return tmpStr;
00072 }
00073 
00074 static QString htmlAddTag( const QString & tag, const QString & text )
00075 {
00076   int numLineBreaks = text.contains( "\n" );
00077   QString str = "<" + tag + ">";
00078   QString tmpText = text;
00079   QString tmpStr = str;
00080   if( numLineBreaks >= 0 ) {
00081     if ( numLineBreaks > 0) {
00082       int pos = 0;
00083       QString tmp;
00084       for( int i = 0; i <= numLineBreaks; i++ ) {
00085         pos = tmpText.find( "\n" );
00086         tmp = tmpText.left( pos );
00087         tmpText = tmpText.right( tmpText.length() - pos - 1 );
00088         tmpStr += tmp + "<br>";
00089       }
00090     } else {
00091       tmpStr += tmpText;
00092     }
00093   }
00094   tmpStr += "</" + tag + ">";
00095   return tmpStr;
00096 }
00097 
00098 static bool iamAttendee( Attendee *attendee )
00099 {
00100   // Check if I'm this attendee
00101 
00102   bool iam = false;
00103   KEMailSettings settings;
00104   QStringList profiles = settings.profiles();
00105   for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00106     settings.setProfile( *it );
00107     if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
00108       iam = true;
00109       break;
00110     }
00111   }
00112   return iam;
00113 }
00114 
00115 static bool iamOrganizer( Incidence *incidence )
00116 {
00117   // Check if I'm the organizer for this incidence
00118 
00119   if ( !incidence ) {
00120     return false;
00121   }
00122 
00123   bool iam = false;
00124   KEMailSettings settings;
00125   QStringList profiles = settings.profiles();
00126   for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00127     settings.setProfile( *it );
00128     if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) {
00129       iam = true;
00130       break;
00131     }
00132   }
00133   return iam;
00134 }
00135 
00136 /*******************************************************************
00137  *  Helper functions for the extensive display (display viewer)
00138  *******************************************************************/
00139 
00140 static QString displayViewLinkPerson( const QString& email, QString name, QString uid )
00141 {
00142   // Make the search, if there is an email address to search on,
00143   // and either name or uid is missing
00144   if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00145     KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00146     KABC::Addressee::List addressList = add_book->findByEmail( email );
00147     if ( !addressList.isEmpty() ) {
00148       KABC::Addressee o = addressList.first();
00149       if ( !o.isEmpty() && addressList.size() < 2 ) {
00150         if ( name.isEmpty() ) {
00151           // No name set, so use the one from the addressbook
00152           name = o.formattedName();
00153         }
00154         uid = o.uid();
00155       } else {
00156         // Email not found in the addressbook. Don't make a link
00157         uid = QString::null;
00158       }
00159     }
00160   }
00161   kdDebug(5850) << "formatAttendees: uid = " << uid << endl;
00162 
00163   // Show the attendee
00164   QString tmpString;
00165   if ( !uid.isEmpty() ) {
00166     // There is a UID, so make a link to the addressbook
00167     if ( name.isEmpty() ) {
00168       // Use the email address for text
00169       tmpString += htmlAddLink( "uid:" + uid, email );
00170     } else {
00171       tmpString += htmlAddLink( "uid:" + uid, name );
00172     }
00173   } else {
00174     // No UID, just show some text
00175     tmpString += ( name.isEmpty() ? email : name );
00176   }
00177 
00178   // Make the mailto link
00179   if ( !email.isEmpty() ) {
00180     KURL mailto;
00181     mailto.setProtocol( "mailto" );
00182     mailto.setPath( email );
00183     const QString iconPath =
00184       KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small );
00185     tmpString += htmlAddLink( mailto.url(),
00186                               "<img valign=\"top\" src=\"" + iconPath + "\">" );
00187   }
00188 
00189   return tmpString;
00190 }
00191 
00192 static QString displayViewFormatAttendees( Incidence *incidence )
00193 {
00194   QString tmpStr;
00195   Attendee::List attendees = incidence->attendees();
00196 
00197   // Add organizer link
00198   tmpStr += "<tr>";
00199   tmpStr += "<td><b>" + i18n( "Organizer:" ) + "</b></td>";
00200   tmpStr += "<td>" +
00201             displayViewLinkPerson( incidence->organizer().email(),
00202                                    incidence->organizer().name(),
00203                                    QString::null ) +
00204             "</td>";
00205   tmpStr += "</tr>";
00206 
00207   // Add attendees links
00208   tmpStr += "<tr>";
00209   tmpStr += "<td><b>" + i18n( "Attendees:" ) + "</b></td>";
00210   tmpStr += "<td>";
00211   Attendee::List::ConstIterator it;
00212   for( it = attendees.begin(); it != attendees.end(); ++it ) {
00213     Attendee *a = *it;
00214     if ( a->email() == incidence->organizer().email() ) {
00215       // skip attendee that is also the organizer
00216       continue;
00217     }
00218     tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid() );
00219     if ( !a->delegator().isEmpty() ) {
00220       tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
00221     }
00222     if ( !a->delegate().isEmpty() ) {
00223       tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
00224     }
00225     tmpStr += "<br>";
00226   }
00227   if ( tmpStr.endsWith( "<br>" ) ) {
00228     tmpStr.truncate( tmpStr.length() - 4 );
00229   }
00230 
00231   tmpStr += "</td>";
00232   tmpStr += "</tr>";
00233   return tmpStr;
00234 }
00235 
00236 static QString displayViewFormatAttachments( Incidence *incidence )
00237 {
00238   QString tmpStr;
00239   Attachment::List as = incidence->attachments();
00240   Attachment::List::ConstIterator it;
00241   uint count = 0;
00242   for( it = as.begin(); it != as.end(); ++it ) {
00243     count++;
00244     if ( (*it)->isUri() ) {
00245       QString name;
00246       if ( (*it)->uri().startsWith( "kmail:" ) ) {
00247         name = i18n( "Show mail" );
00248       } else {
00249         name = (*it)->label();
00250       }
00251       tmpStr += htmlAddLink( (*it)->uri(), name );
00252     } else {
00253       tmpStr += (*it)->label();
00254     }
00255     if ( count < as.count() ) {
00256       tmpStr += "<br>";
00257     }
00258   }
00259   return tmpStr;
00260 }
00261 
00262 static QString displayViewFormatCategories( Incidence *incidence )
00263 {
00264   return incidence->categoriesStr();
00265 }
00266 
00267 static QString displayViewFormatCreationDate( Incidence *incidence )
00268 {
00269   return i18n( "Creation date: %1" ).
00270     arg( IncidenceFormatter::dateTimeToString( incidence->created(), false, true ) );
00271 }
00272 
00273 static QString displayViewFormatBirthday( Event *event )
00274 {
00275   if ( !event ) {
00276     return  QString::null;
00277   }
00278   if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) {
00279     return QString::null;
00280   }
00281 
00282   QString uid = event->customProperty("KABC","UID-1");
00283   QString name = event->customProperty("KABC","NAME-1");
00284   QString email= event->customProperty("KABC","EMAIL-1");
00285 
00286   QString tmpStr = displayViewLinkPerson( email, name, uid );
00287 
00288   if ( event->customProperty( "KABC", "ANNIVERSARY") == "YES" ) {
00289     uid = event->customProperty("KABC","UID-2");
00290     name = event->customProperty("KABC","NAME-2");
00291     email= event->customProperty("KABC","EMAIL-2");
00292     tmpStr += "<br>";
00293     tmpStr += displayViewLinkPerson( email, name, uid );
00294   }
00295 
00296   return tmpStr;
00297 }
00298 
00299 static QString displayViewFormatHeader( Incidence *incidence )
00300 {
00301   QString tmpStr = "<table><tr>";
00302 
00303   // show icons
00304   {
00305     tmpStr += "<td>";
00306 
00307     if ( incidence->type() == "Event" ) {
00308       QString iconPath;
00309       if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00310         if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00311           iconPath =
00312             KGlobal::iconLoader()->iconPath( "calendaranniversary", KIcon::Small );
00313         } else {
00314           iconPath = KGlobal::iconLoader()->iconPath( "calendarbirthday", KIcon::Small );
00315         }
00316       } else {
00317         iconPath = KGlobal::iconLoader()->iconPath( "appointment", KIcon::Small );
00318       }
00319       tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
00320     }
00321     if ( incidence->type() == "Todo" ) {
00322       tmpStr += "<img valign=\"top\" src=\"" +
00323                 KGlobal::iconLoader()->iconPath( "todo", KIcon::Small ) +
00324                 "\">";
00325     }
00326     if ( incidence->type() == "Journal" ) {
00327       tmpStr += "<img valign=\"top\" src=\"" +
00328                 KGlobal::iconLoader()->iconPath( "journal", KIcon::Small ) +
00329                 "\">";
00330     }
00331     if ( incidence->isAlarmEnabled() ) {
00332       tmpStr += "<img valign=\"top\" src=\"" +
00333                 KGlobal::iconLoader()->iconPath( "bell", KIcon::Small ) +
00334                 "\">";
00335     }
00336     if ( incidence->doesRecur() ) {
00337       tmpStr += "<img valign=\"top\" src=\"" +
00338                 KGlobal::iconLoader()->iconPath( "recur", KIcon::Small ) +
00339                 "\">";
00340     }
00341     if ( incidence->isReadOnly() ) {
00342       tmpStr += "<img valign=\"top\" src=\"" +
00343                 KGlobal::iconLoader()->iconPath( "readonlyevent", KIcon::Small ) +
00344                 "\">";
00345     }
00346 
00347     tmpStr += "</td>";
00348   }
00349 
00350   tmpStr += "<td>";
00351   tmpStr += "<b><u>" + incidence->summary() + "</u></b>";
00352   tmpStr += "</td>";
00353 
00354   tmpStr += "</tr></table>";
00355 
00356   return tmpStr;
00357 }
00358 
00359 static QString displayViewFormatEvent( Calendar *calendar, Event *event )
00360 {
00361   if ( !event ) {
00362     return QString::null;
00363   }
00364 
00365   QString tmpStr = displayViewFormatHeader( event );
00366 
00367   tmpStr += "<table>";
00368   tmpStr += "<col width=\"25%\"/>";
00369   tmpStr += "<col width=\"75%\"/>";
00370 
00371   if ( calendar ) {
00372     QString calStr = IncidenceFormatter::resourceString( calendar, event );
00373     if ( !calStr.isEmpty() ) {
00374       tmpStr += "<tr>";
00375       tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00376       tmpStr += "<td>" + calStr + "</td>";
00377       tmpStr += "</tr>";
00378     }
00379   }
00380 
00381   if ( !event->location().isEmpty() ) {
00382     tmpStr += "<tr>";
00383     tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00384     tmpStr += "<td>" + event->location() + "</td>";
00385     tmpStr += "</tr>";
00386   }
00387 
00388   tmpStr += "<tr>";
00389   if ( event->doesFloat() ) {
00390     if ( event->isMultiDay() ) {
00391       tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>";
00392       tmpStr += "<td>" +
00393                 i18n("<beginTime> - <endTime>","%1 - %2").
00394                 arg( IncidenceFormatter::dateToString( event->dtStart(), false ) ).
00395                 arg( IncidenceFormatter::dateToString( event->dtEnd(), false ) ) +
00396                 "</td>";
00397     } else {
00398       tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00399       tmpStr += "<td>" +
00400                 i18n("date as string","%1").
00401                 arg( IncidenceFormatter::dateToString( event->dtStart(), false ) ) +
00402                 "</td>";
00403     }
00404   } else {
00405     if ( event->isMultiDay() ) {
00406       tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>";
00407       tmpStr += "<td>" +
00408                 i18n("<beginTime> - <endTime>","%1 - %2").
00409                 arg( IncidenceFormatter::dateToString( event->dtStart(), false ) ).
00410                 arg( IncidenceFormatter::dateToString( event->dtEnd(), false ) ) +
00411                 "</td>";
00412     } else {
00413       tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>";
00414       if ( event->hasEndDate() && event->dtStart() != event->dtEnd()) {
00415         tmpStr += "<td>" +
00416                   i18n("<beginTime> - <endTime>","%1 - %2").
00417                   arg( IncidenceFormatter::timeToString( event->dtStart(), true ) ).
00418                   arg( IncidenceFormatter::timeToString( event->dtEnd(), true ) ) +
00419                   "</td>";
00420       } else {
00421         tmpStr += "<td>" +
00422                   IncidenceFormatter::timeToString( event->dtStart(), true ) +
00423                   "</td>";
00424       }
00425       tmpStr += "</tr><tr>";
00426       tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00427       tmpStr += "<td>" +
00428                 i18n("date as string","%1").
00429                 arg( IncidenceFormatter::dateToString( event->dtStart(), false ) ) +
00430                 "</td>";
00431     }
00432   }
00433   tmpStr += "</tr>";
00434 
00435   if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) {
00436     tmpStr += "<tr>";
00437     if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00438       tmpStr += "<td><b>" + i18n( "Anniversary:" ) + "</b></td>";
00439     } else {
00440       tmpStr += "<td><b>" + i18n( "Birthday:" ) + "</b></td>";
00441     }
00442     tmpStr += "<td>" + displayViewFormatBirthday( event ) + "</td>";
00443     tmpStr += "</tr>";
00444     tmpStr += "</table>";
00445     return tmpStr;
00446   }
00447 
00448   if ( !event->description().isEmpty() ) {
00449     tmpStr += "<tr>";
00450     tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00451     tmpStr += "<td>" + event->description() + "</td>";
00452     tmpStr += "</tr>";
00453   }
00454 
00455   int categoryCount = event->categories().count();
00456   if ( categoryCount > 0 ) {
00457     tmpStr += "<tr>";
00458     tmpStr += "<td><b>" +
00459               i18n( "Category:", "Categories:", categoryCount ) +
00460               "</b></td>";
00461     tmpStr += "<td>" + displayViewFormatCategories( event ) + "</td>";
00462     tmpStr += "</tr>";
00463   }
00464 
00465   if ( event->doesRecur() ) {
00466     QDateTime dt =
00467       event->recurrence()->getNextDateTime( QDateTime::currentDateTime() );
00468     tmpStr += "<tr>";
00469     tmpStr += "<td><b>" + i18n( "next recurrence", "Next:" ) + "</b></td>";
00470     tmpStr += "<td>" +
00471               ( dt.isValid() ?
00472                 IncidenceFormatter::dateTimeToString( dt, event->doesFloat(), false ) :
00473                 i18n( "no date", "none" ) ) +
00474               "</td>";
00475 
00476     if ( !event->doesFloat() ) {
00477       tmpStr += "<td>" +
00478                 KGlobal::locale()->formatDateTime( dt, true ) + "</td>";
00479     } else {
00480       tmpStr += "<td>" +
00481                 KGlobal::locale()->formatDate( dt.date(), true ) + "</td>";
00482     }
00483     tmpStr += "</tr>";
00484   }
00485 
00486   if ( event->attendees().count() > 1 ) {
00487     tmpStr += displayViewFormatAttendees( event );
00488   }
00489 
00490   int attachmentCount = event->attachments().count();
00491   if ( attachmentCount > 0 ) {
00492     tmpStr += "<tr>";
00493     tmpStr += "<td><b>" +
00494               i18n( "Attachment:", "Attachments:", attachmentCount ) +
00495               "</b></td>";
00496     tmpStr += "<td>" + displayViewFormatAttachments( event ) + "</td>";
00497     tmpStr += "</tr>";
00498   }
00499   tmpStr += "</table>";
00500 
00501   tmpStr += "<em>" + displayViewFormatCreationDate( event ) + "</em>";
00502 
00503   return tmpStr;
00504 }
00505 
00506 static QString displayViewFormatTodo( Calendar *calendar, Todo *todo )
00507 {
00508   if ( !todo ) {
00509     return QString::null;
00510   }
00511 
00512   QString tmpStr = displayViewFormatHeader( todo );
00513 
00514   tmpStr += "<table>";
00515   tmpStr += "<col width=\"25%\"/>";
00516   tmpStr += "<col width=\"75%\"/>";
00517 
00518   if ( calendar ) {
00519     QString calStr = IncidenceFormatter::resourceString( calendar, todo );
00520     if ( !calStr.isEmpty() ) {
00521       tmpStr += "<tr>";
00522       tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00523       tmpStr += "<td>" + calStr + "</td>";
00524       tmpStr += "</tr>";
00525     }
00526   }
00527 
00528   if ( !todo->location().isEmpty() ) {
00529     tmpStr += "<tr>";
00530     tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00531     tmpStr += "<td>" + todo->location() + "</td>";
00532     tmpStr += "</tr>";
00533   }
00534 
00535   if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
00536     tmpStr += "<tr>";
00537     tmpStr += "<td><b>" + i18n( "Start:" ) + "</b></td>";
00538     tmpStr += "<td>" +
00539               IncidenceFormatter::dateTimeToString( todo->dtStart(),
00540                                                     todo->doesFloat(), false ) +
00541               "</td>";
00542     tmpStr += "</tr>";
00543   }
00544 
00545   if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
00546     tmpStr += "<tr>";
00547     tmpStr += "<td><b>" + i18n( "Due:" ) + "</b></td>";
00548     tmpStr += "<td>" +
00549               IncidenceFormatter::dateTimeToString( todo->dtDue(),
00550                                                     todo->doesFloat(), false ) +
00551               "</td>";
00552     tmpStr += "</tr>";
00553   }
00554 
00555   if ( !todo->description().isEmpty() ) {
00556     tmpStr += "<tr>";
00557     tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00558     tmpStr += "<td>" + todo->description() + "</td>";
00559     tmpStr += "</tr>";
00560   }
00561 
00562   int categoryCount = todo->categories().count();
00563   if ( categoryCount > 0 ) {
00564     tmpStr += "<tr>";
00565     tmpStr += "<td><b>" +
00566               i18n( "Category:", "Categories:", categoryCount ) +
00567               "</b></td>";
00568     tmpStr += "<td>" + displayViewFormatCategories( todo ) + "</td>";
00569     tmpStr += "</tr>";
00570   }
00571 
00572   tmpStr += "<tr>";
00573   tmpStr += "<td><b>" + i18n( "Priority:" ) + "</b></td>";
00574   tmpStr += "<td>";
00575   if ( todo->priority() > 0 ) {
00576     tmpStr += QString::number( todo->priority() );
00577   } else {
00578     tmpStr += i18n( "Unspecified" );
00579   }
00580   tmpStr += "</td>";
00581   tmpStr += "</tr>";
00582 
00583   tmpStr += "<tr>";
00584   tmpStr += "<td><b>" + i18n( "Completed:" ) + "</b></td>";
00585   tmpStr += "<td>" + i18n( "%1%" ).arg( todo->percentComplete() ) + "</td>";
00586   tmpStr += "</tr>";
00587 
00588   if ( todo->doesRecur() ) {
00589     QDateTime dt =
00590       todo->recurrence()->getNextDateTime( QDateTime::currentDateTime() );
00591     tmpStr += "<tr>";
00592     tmpStr += "<td><b>" +
00593               i18n( "next recurrence", "Next:" ) +
00594               "</b></td>";
00595     if ( !todo->doesFloat() ) {
00596       tmpStr += "<td>" +
00597                 KGlobal::locale()->formatDateTime( dt, true ) + "</td>";
00598     } else {
00599       tmpStr += "<td>" +
00600                 KGlobal::locale()->formatDate( dt.date(), true ) + "</td>";
00601     }
00602     tmpStr += "</tr>";
00603   }
00604 
00605   if ( todo->attendees().count() > 1 ) {
00606     tmpStr += displayViewFormatAttendees( todo );
00607   }
00608 
00609   int attachmentCount = todo->attachments().count();
00610   if ( attachmentCount > 0 ) {
00611     tmpStr += "<tr>";
00612     tmpStr += "<td><b>" +
00613               i18n( "Attachment:", "Attachments:", attachmentCount ) +
00614               "</b></td>";
00615     tmpStr += "<td>" + displayViewFormatAttachments( todo ) + "</td>";
00616     tmpStr += "</tr>";
00617   }
00618 
00619   tmpStr += "</table>";
00620 
00621   tmpStr += "<em>" + displayViewFormatCreationDate( todo ) + "</em>";
00622 
00623   return tmpStr;
00624 }
00625 
00626 static QString displayViewFormatJournal( Calendar *calendar, Journal *journal )
00627 {
00628   if ( !journal ) {
00629     return QString::null;
00630   }
00631 
00632   QString tmpStr = displayViewFormatHeader( journal );
00633 
00634   tmpStr += "<table>";
00635   tmpStr += "<col width=\"25%\"/>";
00636   tmpStr += "<col width=\"75%\"/>";
00637 
00638   if ( calendar ) {
00639     QString calStr = IncidenceFormatter::resourceString( calendar, journal );
00640     if ( !calStr.isEmpty() ) {
00641       tmpStr += "<tr>";
00642       tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00643       tmpStr += "<td>" + calStr + "</td>";
00644       tmpStr += "</tr>";
00645     }
00646   }
00647 
00648   tmpStr += "<tr>";
00649   tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00650   tmpStr += "<td>" +
00651             IncidenceFormatter::dateToString( journal->dtStart(), false ) +
00652             "</td>";
00653   tmpStr += "</tr>";
00654 
00655   if ( !journal->description().isEmpty() ) {
00656     tmpStr += "<tr>";
00657     tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00658     tmpStr += "<td>" + journal->description() + "</td>";
00659     tmpStr += "</tr>";
00660   }
00661 
00662   int categoryCount = journal->categories().count();
00663   if ( categoryCount > 0 ) {
00664     tmpStr += "<tr>";
00665     tmpStr += "<td><b>" +
00666               i18n( "Category:", "Categories:", categoryCount ) +
00667               "</b></td>";
00668     tmpStr += "<td>" + displayViewFormatCategories( journal ) + "</td>";
00669     tmpStr += "</tr>";
00670   }
00671   tmpStr += "</table>";
00672 
00673   tmpStr += "<em>" + displayViewFormatCreationDate( journal ) + "</em>";
00674 
00675   return tmpStr;
00676 }
00677 
00678 static QString displayViewFormatFreeBusy( Calendar * /*calendar*/, FreeBusy *fb )
00679 {
00680   if ( !fb ) {
00681     return QString::null;
00682   }
00683 
00684   QString tmpStr = htmlAddTag( "h2",
00685                                htmlAddTag( "b",
00686                                            i18n("Free/Busy information for %1").
00687                                            arg( fb->organizer().fullName() ) ) );
00688 
00689   tmpStr += htmlAddTag( "h4", i18n("Busy times in date range %1 - %2:").
00690                         arg( IncidenceFormatter::dateToString( fb->dtStart(), true ) ).
00691                         arg( IncidenceFormatter::dateToString( fb->dtEnd(), true ) ) );
00692 
00693   QValueList<Period> periods = fb->busyPeriods();
00694 
00695   QString text = htmlAddTag( "em", htmlAddTag( "b", i18n("Busy:") ) );
00696   QValueList<Period>::iterator it;
00697   for ( it = periods.begin(); it != periods.end(); ++it ) {
00698     Period per = *it;
00699     if ( per.hasDuration() ) {
00700       int dur = per.duration().asSeconds();
00701       QString cont;
00702       if ( dur >= 3600 ) {
00703         cont += i18n("1 hour ", "%n hours ", dur / 3600 );
00704         dur %= 3600;
00705       }
00706       if ( dur >= 60 ) {
00707         cont += i18n("1 minute ", "%n minutes ", dur / 60);
00708         dur %= 60;
00709       }
00710       if ( dur > 0 ) {
00711         cont += i18n("1 second", "%n seconds", dur);
00712       }
00713       text += i18n("startDate for duration", "%1 for %2").
00714               arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ).
00715               arg( cont );
00716       text += "<br>";
00717     } else {
00718       if ( per.start().date() == per.end().date() ) {
00719         text += i18n("date, fromTime - toTime ", "%1, %2 - %3").
00720                 arg( IncidenceFormatter::dateToString( per.start().date(), true ) ).
00721                 arg( IncidenceFormatter::timeToString( per.start(), true ) ).
00722                 arg( IncidenceFormatter::timeToString( per.end(), true ) );
00723       } else {
00724         text += i18n("fromDateTime - toDateTime", "%1 - %2").
00725                 arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ).
00726                 arg( IncidenceFormatter::dateTimeToString( per.end(), false, true ) );
00727       }
00728       text += "<br>";
00729     }
00730   }
00731   tmpStr += htmlAddTag( "p", text );
00732   return tmpStr;
00733 }
00734 
00735 class IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor
00736 {
00737   public:
00738     EventViewerVisitor()
00739       : mCalendar( 0 ), mResult( "" ) {}
00740 
00741     bool act( Calendar *calendar, IncidenceBase *incidence )
00742     {
00743       mCalendar = calendar;
00744       mResult = "";
00745       return incidence->accept( *this );
00746     }
00747     QString result() const { return mResult; }
00748 
00749   protected:
00750     bool visit( Event *event )
00751     {
00752       mResult = displayViewFormatEvent( mCalendar, event );
00753       return !mResult.isEmpty();
00754     }
00755     bool visit( Todo *todo )
00756     {
00757       mResult = displayViewFormatTodo( mCalendar, todo );
00758       return !mResult.isEmpty();
00759     }
00760     bool visit( Journal *journal )
00761     {
00762       mResult = displayViewFormatJournal( mCalendar, journal );
00763       return !mResult.isEmpty();
00764     }
00765     bool visit( FreeBusy *fb )
00766     {
00767       mResult = displayViewFormatFreeBusy( mCalendar, fb );
00768       return !mResult.isEmpty();
00769     }
00770 
00771   protected:
00772     Calendar *mCalendar;
00773     QString mResult;
00774 };
00775 
00776 QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
00777 {
00778   return extensiveDisplayStr( 0, incidence );
00779 }
00780 
00781 QString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar,
00782                                                  IncidenceBase *incidence )
00783 {
00784   if ( !incidence ) {
00785     return QString::null;
00786   }
00787 
00788   EventViewerVisitor v;
00789   if ( v.act( calendar, incidence ) ) {
00790     return v.result();
00791   } else {
00792     return QString::null;
00793   }
00794 }
00795 
00796 /***********************************************************************
00797  *  Helper functions for the body part formatter of kmail (Invitations)
00798  ***********************************************************************/
00799 
00800 static QString string2HTML( const QString& str )
00801 {
00802   return QStyleSheet::convertFromPlainText(str, QStyleSheetItem::WhiteSpaceNormal);
00803 }
00804 
00805 static QString eventStartTimeStr( Event *event )
00806 {
00807   QString tmp;
00808   if ( !event->doesFloat() ) {
00809     tmp = i18n( "%1: Start Date, %2: Start Time", "%1 %2" ).
00810           arg( IncidenceFormatter::dateToString( event->dtStart(), true ),
00811                IncidenceFormatter::timeToString( event->dtStart(), true ) );
00812   } else {
00813     tmp = i18n( "%1: Start Date", "%1 (all day)" ).
00814           arg( IncidenceFormatter::dateToString( event->dtStart(), true ) );
00815   }
00816   return tmp;
00817 }
00818 
00819 static QString eventEndTimeStr( Event *event )
00820 {
00821   QString tmp;
00822   if ( event->hasEndDate() && event->dtEnd().isValid() ) {
00823     if ( !event->doesFloat() ) {
00824       tmp = i18n( "%1: End Date, %2: End Time", "%1 %2" ).
00825             arg( IncidenceFormatter::dateToString( event->dtEnd(), true ),
00826                  IncidenceFormatter::timeToString( event->dtEnd(), true ) );
00827     } else {
00828       tmp = i18n( "%1: End Date", "%1 (all day)" ).
00829             arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) );
00830     }
00831   }
00832   return tmp;
00833 }
00834 
00835 static QString invitationRow( const QString &cell1, const QString &cell2 )
00836 {
00837   return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
00838 }
00839 
00840 static Attendee *findMyAttendee( Incidence *incidence )
00841 {
00842   // Return the attendee for the incidence that is probably me
00843 
00844   Attendee *attendee = 0;
00845   if ( !incidence ) {
00846     return attendee;
00847   }
00848 
00849   KEMailSettings settings;
00850   QStringList profiles = settings.profiles();
00851   for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00852     settings.setProfile( *it );
00853 
00854     Attendee::List attendees = incidence->attendees();
00855     Attendee::List::ConstIterator it2;
00856     for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) {
00857       Attendee *a = *it2;
00858       if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
00859         attendee = a;
00860         break;
00861       }
00862     }
00863   }
00864   return attendee;
00865 }
00866 
00867 static Attendee *findAttendee( Incidence *incidence, const QString &email )
00868 {
00869   // Search for an attendee by email address
00870 
00871   Attendee *attendee = 0;
00872   if ( !incidence ) {
00873     return attendee;
00874   }
00875 
00876   Attendee::List attendees = incidence->attendees();
00877   Attendee::List::ConstIterator it;
00878   for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00879     Attendee *a = *it;
00880     if ( email == a->email() ) {
00881       attendee = a;
00882       break;
00883     }
00884   }
00885   return attendee;
00886 }
00887 
00888 static bool rsvpRequested( Incidence *incidence )
00889 {
00890   if ( !incidence ) {
00891     return false;
00892   }
00893 
00894   //use a heuristic to determine if a response is requested.
00895 
00896   bool rsvp = true; // better send superfluously than not at all
00897   Attendee::List attendees = incidence->attendees();
00898   Attendee::List::ConstIterator it;
00899   for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00900     if ( it == attendees.begin() ) {
00901       rsvp = (*it)->RSVP(); // use what the first one has
00902     } else {
00903       if ( (*it)->RSVP() != rsvp ) {
00904         rsvp = true; // they differ, default
00905         break;
00906       }
00907     }
00908   }
00909   return rsvp;
00910 }
00911 
00912 static QString rsvpRequestedStr( bool rsvpRequested )
00913 {
00914   if ( rsvpRequested ) {
00915     return i18n( "Your response is requested" );
00916   } else {
00917     return i18n( "A response is not necessary" );
00918   }
00919 }
00920 
00921 static QString invitationPerson( const QString& email, QString name, QString uid )
00922 {
00923   // Make the search, if there is an email address to search on,
00924   // and either name or uid is missing
00925   if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00926     KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00927     KABC::Addressee::List addressList = add_book->findByEmail( email );
00928     if ( !addressList.isEmpty() ) {
00929       KABC::Addressee o = addressList.first();
00930       if ( !o.isEmpty() && addressList.size() < 2 ) {
00931         if ( name.isEmpty() ) {
00932           // No name set, so use the one from the addressbook
00933           name = o.formattedName();
00934         }
00935         uid = o.uid();
00936       } else {
00937         // Email not found in the addressbook. Don't make a link
00938         uid = QString::null;
00939       }
00940     }
00941   }
00942 
00943   // Show the attendee
00944   QString tmpString;
00945   if ( !uid.isEmpty() ) {
00946     // There is a UID, so make a link to the addressbook
00947     if ( name.isEmpty() ) {
00948       // Use the email address for text
00949       tmpString += htmlAddLink( "uid:" + uid, email );
00950     } else {
00951       tmpString += htmlAddLink( "uid:" + uid, name );
00952     }
00953   } else {
00954     // No UID, just show some text
00955     tmpString += ( name.isEmpty() ? email : name );
00956   }
00957   tmpString += '\n';
00958 
00959   // Make the mailto link
00960   if ( !email.isEmpty() ) {
00961     KCal::Person person( name, email );
00962     KURL mailto;
00963     mailto.setProtocol( "mailto" );
00964     mailto.setPath( person.fullName() );
00965     const QString iconPath =
00966       KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small );
00967     tmpString += htmlAddLink( mailto.url(), "<img src=\"" + iconPath + "\">" )
00968 ;
00969   }
00970   tmpString += "\n";
00971 
00972   return tmpString;
00973 }
00974 
00975 static QString invitationsDetailsIncidence( Incidence *incidence )
00976 {
00977   QString html;
00978   QString descr;
00979   QStringList comments;
00980   if ( incidence->comments().isEmpty() && !incidence->description().isEmpty() ) {
00981     comments << incidence->description();
00982   } else {
00983     descr = incidence->description();
00984     comments = incidence->comments();
00985   }
00986 
00987   if( !descr.isEmpty() ) {
00988     html += "<br/><u>" + i18n("Description:")
00989       + "</u><table border=\"0\"><tr><td>&nbsp;</td><td>";
00990     html += string2HTML(descr) + "</td></tr></table>";
00991   }
00992   if ( !comments.isEmpty() ) {
00993     html += "<br><u>" + i18n("Comments:")
00994           + "</u><table border=\"0\"><tr><td>&nbsp;</td><td>";
00995     if ( comments.count() > 1 ) {
00996       html += "<ul>";
00997       for ( uint i = 0; i < comments.count(); ++i )
00998         html += "<li>" + string2HTML( comments[i] ) + "</li>";
00999       html += "</ul>";
01000     } else {
01001       html += string2HTML( comments[0] );
01002     }
01003     html += "</td></tr></table>";
01004   }
01005   return html;
01006 }
01007 
01008 static QString invitationDetailsEvent( Event* event )
01009 {
01010   // Invitation details are formatted into an HTML table
01011   if ( !event )
01012     return QString::null;
01013 
01014   QString html;
01015 
01016   QString sSummary = i18n( "Summary unspecified" );
01017   if ( ! event->summary().isEmpty() ) {
01018     sSummary = QStyleSheet::escape( event->summary() );
01019   }
01020 
01021   QString sLocation = i18n( "Location unspecified" );
01022   if ( ! event->location().isEmpty() ) {
01023     sLocation = QStyleSheet::escape( event->location() );
01024   }
01025 
01026   QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
01027   html = QString("<div dir=\"%1\">\n").arg(dir);
01028 
01029   html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
01030 
01031   // Invitation summary & location rows
01032   html += invitationRow( i18n( "What:" ), sSummary );
01033   html += invitationRow( i18n( "Where:" ), sLocation );
01034 
01035   // If a 1 day event
01036   if ( event->dtStart().date() == event->dtEnd().date() ) {
01037     html += invitationRow( i18n( "Date:" ),
01038                            IncidenceFormatter::dateToString( event->dtStart(), false ) );
01039     if ( !event->doesFloat() ) {
01040       html += invitationRow( i18n( "Time:" ),
01041                              IncidenceFormatter::timeToString( event->dtStart(), true ) +
01042                              " - " +
01043                              IncidenceFormatter::timeToString( event->dtEnd(), true ) );
01044     }
01045   } else {
01046     html += invitationRow( i18n( "Starting date of an event", "From:" ),
01047                            IncidenceFormatter::dateToString( event->dtStart(), false ) );
01048     if ( !event->doesFloat() ) {
01049       html += invitationRow( i18n( "Starting time of an event", "At:" ),
01050                              IncidenceFormatter::timeToString( event->dtStart(), true ) );
01051     }
01052     if ( event->hasEndDate() ) {
01053       html += invitationRow( i18n( "Ending date of an event", "To:" ),
01054                              IncidenceFormatter::dateToString( event->dtEnd(), false ) );
01055       if ( !event->doesFloat() ) {
01056         html += invitationRow( i18n( "Starting time of an event", "At:" ),
01057                                IncidenceFormatter::timeToString( event->dtEnd(), true ) );
01058       }
01059     } else {
01060       html += invitationRow( i18n( "Ending date of an event", "To:" ),
01061                              i18n( "no end date specified" ) );
01062     }
01063   }
01064 
01065   // Invitation Duration Row
01066   if ( !event->doesFloat() && event->hasEndDate() ) {
01067     QString tmp;
01068     int secs = event->dtStart().secsTo( event->dtEnd() );
01069     int days = secs / 86400;
01070     if ( days > 0 ) {
01071       tmp += i18n( "1 day ", "%n days ", days );
01072       secs -= ( days * 86400 );
01073     }
01074     int hours = secs / 3600;
01075     if ( hours > 0 ) {
01076       tmp += i18n( "1 hour ", "%n hours ", hours );
01077       secs -= ( hours * 3600 );
01078     }
01079     int mins = secs / 60;
01080     if ( mins > 0 ) {
01081       tmp += i18n( "1 minute ", "%n minutes ",  mins );
01082     }
01083     html += invitationRow( i18n( "Duration:" ), tmp );
01084   }
01085 
01086   if ( event->doesRecur() )
01087     html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) );
01088 
01089   html += "</table>\n";
01090   html += invitationsDetailsIncidence( event );
01091   html += "</div>\n";
01092 
01093   return html;
01094 }
01095 
01096 static QString invitationDetailsTodo( Todo *todo )
01097 {
01098   // Task details are formatted into an HTML table
01099   if ( !todo )
01100     return QString::null;
01101 
01102   QString sSummary = i18n( "Summary unspecified" );
01103   if ( ! todo->summary().isEmpty() ) {
01104     sSummary = QStyleSheet::escape( todo->summary() );
01105   }
01106 
01107   QString sLocation = i18n( "Location unspecified" );
01108   if ( ! todo->location().isEmpty() ) {
01109     sLocation = QStyleSheet::escape( todo->location() );
01110   }
01111 
01112   QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
01113   QString html = QString("<div dir=\"%1\">\n").arg(dir);
01114 
01115   html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
01116 
01117   // Invitation summary & location rows
01118   html += invitationRow( i18n( "What:" ), sSummary );
01119   html += invitationRow( i18n( "Where:" ), sLocation );
01120 
01121   if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
01122     html += invitationRow( i18n( "Start Date:" ),
01123                            IncidenceFormatter::dateToString( todo->dtStart(), false ) );
01124   }
01125   if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
01126     html += invitationRow( i18n( "Due Date:" ),
01127                            IncidenceFormatter::dateToString( todo->dtDue(), false ) );
01128     if ( !todo->doesFloat() ) {
01129       html += invitationRow( i18n( "Due Time:" ),
01130                              KGlobal::locale()->formatTime( todo->dtDue().time() ) );
01131     }
01132 
01133   } else {
01134     html += invitationRow( i18n( "Due Date:" ),
01135                            i18n( "Due Date: None", "None" ) );
01136   }
01137 
01138   html += "</table></div>\n";
01139   html += invitationsDetailsIncidence( todo );
01140 
01141   return html;
01142 }
01143 
01144 static QString invitationDetailsJournal( Journal *journal )
01145 {
01146   if ( !journal )
01147     return QString::null;
01148 
01149   QString sSummary = i18n( "Summary unspecified" );
01150   QString sDescr = i18n( "Description unspecified" );
01151   if ( ! journal->summary().isEmpty() ) {
01152     sSummary = journal->summary();
01153   }
01154   if ( ! journal->description().isEmpty() ) {
01155     sDescr = journal->description();
01156   }
01157   QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01158   html += invitationRow( i18n( "Summary:" ), sSummary );
01159   html += invitationRow( i18n( "Date:" ),
01160                          IncidenceFormatter::dateToString( journal->dtStart(), false ) );
01161   html += invitationRow( i18n( "Description:" ), sDescr );
01162   html += "</table>\n";
01163   html += invitationsDetailsIncidence( journal );
01164 
01165   return html;
01166 }
01167 
01168 static QString invitationDetailsFreeBusy( FreeBusy *fb )
01169 {
01170   if ( !fb )
01171     return QString::null;
01172   QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01173 
01174   html += invitationRow( i18n("Person:"), fb->organizer().fullName() );
01175   html += invitationRow( i18n("Start date:"),
01176                          IncidenceFormatter::dateToString( fb->dtStart(), true ) );
01177   html += invitationRow( i18n("End date:"),
01178                          KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) );
01179   html += "<tr><td colspan=2><hr></td></tr>\n";
01180   html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
01181 
01182   QValueList<Period> periods = fb->busyPeriods();
01183 
01184   QValueList<Period>::iterator it;
01185   for ( it = periods.begin(); it != periods.end(); ++it ) {
01186     Period per = *it;
01187     if ( per.hasDuration() ) {
01188       int dur = per.duration().asSeconds();
01189       QString cont;
01190       if ( dur >= 3600 ) {
01191         cont += i18n("1 hour ", "%n hours ", dur / 3600);
01192         dur %= 3600;
01193       }
01194       if ( dur >= 60 ) {
01195         cont += i18n("1 minute", "%n minutes ", dur / 60);
01196         dur %= 60;
01197       }
01198       if ( dur > 0 ) {
01199         cont += i18n("1 second", "%n seconds", dur);
01200       }
01201       html += invitationRow( QString::null, i18n("startDate for duration", "%1 for %2")
01202           .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
01203           .arg(cont) );
01204     } else {
01205       QString cont;
01206       if ( per.start().date() == per.end().date() ) {
01207         cont = i18n("date, fromTime - toTime ", "%1, %2 - %3")
01208             .arg( KGlobal::locale()->formatDate( per.start().date() ) )
01209             .arg( KGlobal::locale()->formatTime( per.start().time() ) )
01210             .arg( KGlobal::locale()->formatTime( per.end().time() ) );
01211       } else {
01212         cont = i18n("fromDateTime - toDateTime", "%1 - %2")
01213           .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
01214           .arg( KGlobal::locale()->formatDateTime( per.end(), false ) );
01215       }
01216 
01217       html += invitationRow( QString::null, cont );
01218     }
01219   }
01220 
01221   html += "</table>\n";
01222   return html;
01223 }
01224 
01225 static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
01226 {
01227   if ( !msg || !event )
01228     return QString::null;
01229 
01230   switch ( msg->method() ) {
01231   case Scheduler::Publish:
01232     return i18n( "This event has been published" );
01233   case Scheduler::Request:
01234     if ( event->revision() > 0 ) {
01235       return i18n( "This invitation has been updated" );
01236     }
01237     if ( iamOrganizer( event ) ) {
01238       return i18n( "I sent this invitation" );
01239     } else {
01240       if ( !event->organizer().fullName().isEmpty() ) {
01241         return i18n( "You received an invitation from %1" ).
01242           arg( event->organizer().fullName() );
01243       } else {
01244         return i18n( "You received an invitation" );
01245       }
01246     }
01247   case Scheduler::Refresh:
01248     return i18n( "This invitation was refreshed" );
01249   case Scheduler::Cancel:
01250     return i18n( "This invitation has been canceled" );
01251   case Scheduler::Add:
01252     return i18n( "Addition to the invitation" );
01253   case Scheduler::Reply: {
01254     Attendee::List attendees = event->attendees();
01255     if( attendees.count() == 0 ) {
01256       kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01257       return QString::null;
01258     }
01259     if( attendees.count() != 1 ) {
01260       kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01261                     << "but is " << attendees.count() << endl;
01262     }
01263     Attendee* attendee = *attendees.begin();
01264     QString attendeeName = attendee->name();
01265     if ( attendeeName.isEmpty() ) {
01266       attendeeName = attendee->email();
01267     }
01268     if ( attendeeName.isEmpty() ) {
01269       attendeeName = i18n( "Sender" );
01270     }
01271 
01272     QString delegatorName, dummy;
01273     KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
01274     if ( delegatorName.isEmpty() ) {
01275       delegatorName = attendee->delegator();
01276     }
01277 
01278     switch( attendee->status() ) {
01279     case Attendee::NeedsAction:
01280       return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName );
01281     case Attendee::Accepted:
01282       if ( delegatorName.isEmpty() ) {
01283         return i18n( "%1 accepts this invitation" ).arg( attendeeName );
01284       } else {
01285         return i18n( "%1 accepts this invitation on behalf of %2" ).
01286           arg( attendeeName ).arg( delegatorName );
01287       }
01288     case Attendee::Tentative:
01289       if ( delegatorName.isEmpty() ) {
01290         return i18n( "%1 tentatively accepts this invitation" ).
01291           arg( attendeeName );
01292       } else {
01293         return i18n( "%1 tentatively accepts this invitation on behalf of %2" ).
01294           arg( attendeeName ).arg( delegatorName );
01295       }
01296     case Attendee::Declined:
01297       if ( delegatorName.isEmpty() ) {
01298         return i18n( "%1 declines this invitation" ).arg( attendeeName );
01299       } else {
01300         return i18n( "%1 declines this invitation on behalf of %2" ).
01301           arg( attendeeName ).arg( delegatorName );
01302       }
01303     case Attendee::Delegated: {
01304       QString delegate, dummy;
01305       KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01306       if ( delegate.isEmpty() ) {
01307         delegate = attendee->delegate();
01308       }
01309       if ( !delegate.isEmpty() ) {
01310         return i18n( "%1 has delegated this invitation to %2" ).
01311           arg( attendeeName ) .arg( delegate );
01312       } else {
01313         return i18n( "%1 has delegated this invitation" ).arg( attendeeName );
01314       }
01315     }
01316     case Attendee::Completed:
01317       return i18n( "This invitation is now completed" );
01318     case Attendee::InProcess:
01319       return i18n( "%1 is still processing the invitation" ).
01320         arg( attendeeName );
01321     default:
01322       return i18n( "Unknown response to this invitation" );
01323     }
01324     break; }
01325   case Scheduler::Counter:
01326     return i18n( "Sender makes this counter proposal" );
01327   case Scheduler::Declinecounter:
01328     return i18n( "Sender declines the counter proposal" );
01329   case Scheduler::NoMethod:
01330     return i18n("Error: iMIP message with unknown method: '%1'").
01331       arg( msg->method() );
01332   }
01333   return QString::null;
01334 }
01335 
01336 static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
01337 {
01338   if ( !msg || !todo ) {
01339     return QString::null;
01340   }
01341 
01342   switch ( msg->method() ) {
01343   case Scheduler::Publish:
01344     return i18n("This task has been published");
01345   case Scheduler::Request:
01346     if ( todo->revision() > 0 ) {
01347       return i18n( "This task has been updated" );
01348     } else {
01349       return i18n( "You have been assigned this task" );
01350     }
01351   case Scheduler::Refresh:
01352     return i18n( "This task was refreshed" );
01353   case Scheduler::Cancel:
01354     return i18n( "This task was canceled" );
01355   case Scheduler::Add:
01356     return i18n( "Addition to the task" );
01357   case Scheduler::Reply: {
01358     Attendee::List attendees = todo->attendees();
01359     if( attendees.count() == 0 ) {
01360       kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01361       return QString::null;
01362     }
01363     if( attendees.count() != 1 ) {
01364       kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01365                     << "but is " << attendees.count() << endl;
01366     }
01367     Attendee* attendee = *attendees.begin();
01368 
01369     switch( attendee->status() ) {
01370     case Attendee::NeedsAction:
01371       return i18n( "Sender indicates this task assignment still needs some action" );
01372     case Attendee::Accepted:
01373       return i18n( "Sender accepts this task" );
01374     case Attendee::Tentative:
01375       return i18n( "Sender tentatively accepts this task" );
01376     case Attendee::Declined:
01377       return i18n( "Sender declines this task" );
01378     case Attendee::Delegated: {
01379       QString delegate, dummy;
01380       KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01381       if ( delegate.isEmpty() ) {
01382         delegate = attendee->delegate();
01383       }
01384       if ( !delegate.isEmpty() ) {
01385         return i18n( "Sender has delegated this request for the task to %1" ).arg( delegate );
01386       } else {
01387         return i18n( "Sender has delegated this request for the task " );
01388       }
01389     }
01390     case Attendee::Completed:
01391       return i18n( "The request for this task is now completed" );
01392     case Attendee::InProcess:
01393       return i18n( "Sender is still processing the invitation" );
01394     default:
01395       return i18n( "Unknown response to this task" );
01396     }
01397     break; }
01398   case Scheduler::Counter:
01399     return i18n( "Sender makes this counter proposal" );
01400   case Scheduler::Declinecounter:
01401     return i18n( "Sender declines the counter proposal" );
01402   case Scheduler::NoMethod:
01403     return i18n("Error: iMIP message with unknown method: '%1'").
01404       arg( msg->method() );
01405   }
01406   return QString::null;
01407 }
01408 
01409 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
01410 {
01411   if ( !msg || !journal ) {
01412     return QString::null;
01413   }
01414 
01415   switch ( msg->method() ) {
01416   case Scheduler::Publish:
01417     return i18n("This journal has been published");
01418   case Scheduler::Request:
01419     return i18n( "You have been assigned this journal" );
01420   case Scheduler::Refresh:
01421     return i18n( "This journal was refreshed" );
01422   case Scheduler::Cancel:
01423     return i18n( "This journal was canceled" );
01424   case Scheduler::Add:
01425     return i18n( "Addition to the journal" );
01426   case Scheduler::Reply: {
01427     Attendee::List attendees = journal->attendees();
01428     if( attendees.count() == 0 ) {
01429       kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01430       return QString::null;
01431     }
01432     if( attendees.count() != 1 ) {
01433       kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01434                     << "but is " << attendees.count() << endl;
01435     }
01436     Attendee* attendee = *attendees.begin();
01437 
01438     switch( attendee->status() ) {
01439     case Attendee::NeedsAction:
01440       return i18n( "Sender indicates this journal assignment still needs some action" );
01441     case Attendee::Accepted:
01442       return i18n( "Sender accepts this journal" );
01443     case Attendee::Tentative:
01444       return i18n( "Sender tentatively accepts this journal" );
01445     case Attendee::Declined:
01446       return i18n( "Sender declines this journal" );
01447     case Attendee::Delegated:
01448       return i18n( "Sender has delegated this request for the journal" );
01449     case Attendee::Completed:
01450       return i18n( "The request for this journal is now completed" );
01451     case Attendee::InProcess:
01452       return i18n( "Sender is still processing the invitation" );
01453     default:
01454       return i18n( "Unknown response to this journal" );
01455     }
01456     break;
01457   }
01458   case Scheduler::Counter:
01459     return i18n( "Sender makes this counter proposal" );
01460   case Scheduler::Declinecounter:
01461     return i18n( "Sender declines the counter proposal" );
01462   case Scheduler::NoMethod:
01463     return i18n("Error: iMIP message with unknown method: '%1'").
01464       arg( msg->method() );
01465   }
01466   return QString::null;
01467 }
01468 
01469 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01470 {
01471   if ( !msg || !fb ) {
01472     return QString::null;
01473   }
01474 
01475   switch ( msg->method() ) {
01476   case Scheduler::Publish:
01477     return i18n("This free/busy list has been published");
01478   case Scheduler::Request:
01479     return i18n( "The free/busy list has been requested" );
01480   case Scheduler::Refresh:
01481     return i18n( "This free/busy list was refreshed" );
01482   case Scheduler::Cancel:
01483     return i18n( "This free/busy list was canceled" );
01484   case Scheduler::Add:
01485     return i18n( "Addition to the free/busy list" );
01486   case Scheduler::NoMethod:
01487   default:
01488     return i18n("Error: Free/Busy iMIP message with unknown method: '%1'").
01489       arg( msg->method() );
01490   }
01491 }
01492 
01493 static QString invitationAttendees( Incidence *incidence )
01494 {
01495   QString tmpStr;
01496   if ( !incidence ) {
01497     return tmpStr;
01498   }
01499 
01500   tmpStr += htmlAddTag( "u", i18n( "Attendee List" ) );
01501   tmpStr += "<br/>";
01502 
01503   int count=0;
01504   Attendee::List attendees = incidence->attendees();
01505   if ( !attendees.isEmpty() ) {
01506 
01507     Attendee::List::ConstIterator it;
01508     for( it = attendees.begin(); it != attendees.end(); ++it ) {
01509       Attendee *a = *it;
01510       if ( !iamAttendee( a ) ) {
01511         count++;
01512         if ( count == 1 ) {
01513           tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\" columns=\"2\">";
01514         }
01515         tmpStr += "<tr>";
01516         tmpStr += "<td>";
01517         tmpStr += invitationPerson( a->email(), a->name(), QString::null );
01518         if ( !a->delegator().isEmpty() ) {
01519           tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
01520         }
01521         if ( !a->delegate().isEmpty() ) {
01522           tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
01523         }
01524         tmpStr += "</td>";
01525         tmpStr += "<td>" + a->statusStr() + "</td>";
01526         tmpStr += "</tr>";
01527       }
01528     }
01529   }
01530   if ( count ) {
01531     tmpStr += "</table>";
01532   } else {
01533     tmpStr += "<i>" + i18n( "No attendee", "None" ) + "</i>";
01534   }
01535 
01536   return tmpStr;
01537 }
01538 
01539 static QString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence )
01540 {
01541   QString tmpStr;
01542   if ( !incidence ) {
01543     return tmpStr;
01544   }
01545 
01546   Attachment::List attachments = incidence->attachments();
01547   if ( !attachments.isEmpty() ) {
01548     tmpStr += i18n( "Attached Documents:" ) + "<ol>";
01549 
01550     Attachment::List::ConstIterator it;
01551     for( it = attachments.begin(); it != attachments.end(); ++it ) {
01552       Attachment *a = *it;
01553       tmpStr += "<li>";
01554       // Attachment icon
01555       KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() );
01556       QString iconStr = mimeType->icon( a->uri(), false );
01557       QString iconPath = KGlobal::iconLoader()->iconPath( iconStr, KIcon::Small );
01558       if ( !iconPath.isEmpty() ) {
01559         tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
01560       }
01561       tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() );
01562       tmpStr += "</li>";
01563     }
01564     tmpStr += "</ol>";
01565   }
01566 
01567   return tmpStr;
01568 }
01569 
01570 class IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor
01571 {
01572   public:
01573     ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
01574     bool act( IncidenceBase *incidence, ScheduleMessage *msg ) { mMessage = msg; return incidence->accept( *this ); }
01575     QString result() const { return mResult; }
01576 
01577   protected:
01578     QString mResult;
01579     ScheduleMessage *mMessage;
01580 };
01581 
01582 class IncidenceFormatter::InvitationHeaderVisitor :
01583       public IncidenceFormatter::ScheduleMessageVisitor
01584 {
01585   protected:
01586     bool visit( Event *event )
01587     {
01588       mResult = invitationHeaderEvent( event, mMessage );
01589       return !mResult.isEmpty();
01590     }
01591     bool visit( Todo *todo )
01592     {
01593       mResult = invitationHeaderTodo( todo, mMessage );
01594       return !mResult.isEmpty();
01595     }
01596     bool visit( Journal *journal )
01597     {
01598       mResult = invitationHeaderJournal( journal, mMessage );
01599       return !mResult.isEmpty();
01600     }
01601     bool visit( FreeBusy *fb )
01602     {
01603       mResult = invitationHeaderFreeBusy( fb, mMessage );
01604       return !mResult.isEmpty();
01605     }
01606 };
01607 
01608 class IncidenceFormatter::InvitationBodyVisitor :
01609       public IncidenceFormatter::ScheduleMessageVisitor
01610 {
01611   protected:
01612     bool visit( Event *event )
01613     {
01614       mResult = invitationDetailsEvent( event );
01615       return !mResult.isEmpty();
01616     }
01617     bool visit( Todo *todo )
01618     {
01619       mResult = invitationDetailsTodo( todo );
01620       return !mResult.isEmpty();
01621     }
01622     bool visit( Journal *journal )
01623     {
01624       mResult = invitationDetailsJournal( journal );
01625       return !mResult.isEmpty();
01626     }
01627     bool visit( FreeBusy *fb )
01628     {
01629       mResult = invitationDetailsFreeBusy( fb );
01630       return !mResult.isEmpty();
01631     }
01632 };
01633 
01634 class IncidenceFormatter::IncidenceCompareVisitor :
01635   public IncidenceBase::Visitor
01636 {
01637   public:
01638     IncidenceCompareVisitor() : mExistingIncidence(0) {}
01639     bool act( IncidenceBase *incidence, Incidence* existingIncidence )
01640     {
01641       Incidence *inc = dynamic_cast<Incidence*>( incidence );
01642       if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() )
01643         return false;
01644       mExistingIncidence = existingIncidence;
01645       return incidence->accept( *this );
01646     }
01647 
01648     QString result() const
01649     {
01650       if ( mChanges.isEmpty() ) {
01651         return QString::null;
01652       }
01653       QString html = "<div align=\"left\"><ul><li>";
01654       html += mChanges.join( "</li><li>" );
01655       html += "</li><ul></div>";
01656       return html;
01657     }
01658 
01659   protected:
01660     bool visit( Event *event )
01661     {
01662       compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
01663       compareIncidences( event, mExistingIncidence );
01664       return !mChanges.isEmpty();
01665     }
01666     bool visit( Todo *todo )
01667     {
01668       compareIncidences( todo, mExistingIncidence );
01669       return !mChanges.isEmpty();
01670     }
01671     bool visit( Journal *journal )
01672     {
01673       compareIncidences( journal, mExistingIncidence );
01674       return !mChanges.isEmpty();
01675     }
01676     bool visit( FreeBusy *fb )
01677     {
01678       Q_UNUSED( fb );
01679       return !mChanges.isEmpty();
01680     }
01681 
01682   private:
01683     void compareEvents( Event *newEvent, Event *oldEvent )
01684     {
01685       if ( !oldEvent || !newEvent )
01686         return;
01687       if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() )
01688         mChanges += i18n( "The invitation starting time has been changed from %1 to %2" )
01689             .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) );
01690       if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() )
01691         mChanges += i18n( "The invitation ending time has been changed from %1 to %2" )
01692             .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) );
01693     }
01694 
01695     void compareIncidences( Incidence *newInc, Incidence *oldInc )
01696     {
01697       if ( !oldInc || !newInc )
01698         return;
01699       if ( oldInc->summary() != newInc->summary() )
01700         mChanges += i18n( "The summary has been changed to: \"%1\"" ).arg( newInc->summary() );
01701       if ( oldInc->location() != newInc->location() )
01702         mChanges += i18n( "The location has been changed to: \"%1\"" ).arg( newInc->location() );
01703       if ( oldInc->description() != newInc->description() )
01704         mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() );
01705       Attendee::List oldAttendees = oldInc->attendees();
01706       Attendee::List newAttendees = newInc->attendees();
01707       for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); it != newAttendees.constEnd(); ++it ) {
01708         Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
01709         if ( !oldAtt ) {
01710           mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() );
01711         } else {
01712           if ( oldAtt->status() != (*it)->status() )
01713             mChanges += i18n( "The status of attendee %1 has been changed to: %2" ).arg( (*it)->fullName() )
01714                 .arg( (*it)->statusStr() );
01715         }
01716       }
01717       for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); it != oldAttendees.constEnd(); ++it ) {
01718         Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
01719         if ( !newAtt )
01720           mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() );
01721       }
01722     }
01723 
01724   private:
01725     Incidence* mExistingIncidence;
01726     QStringList mChanges;
01727 };
01728 
01729 
01730 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
01731 {
01732   if ( !id.startsWith( "ATTACH:" ) ) {
01733     QString res = QString( "<a href=\"%1\"><b>%2</b></a>" ).
01734                   arg( generateLinkURL( id ), text );
01735     return res;
01736   } else {
01737     // draw the attachment links in non-bold face
01738     QString res = QString( "<a href=\"%1\">%2</a>" ).
01739                   arg( generateLinkURL( id ), text );
01740     return res;
01741   }
01742 }
01743 
01744 // Check if the given incidence is likely one that we own instead one from
01745 // a shared calendar (Kolab-specific)
01746 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
01747 {
01748   CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
01749   if ( !cal || !incidence ) {
01750     return true;
01751   }
01752   ResourceCalendar *res = cal->resource( incidence );
01753   if ( !res ) {
01754     return true;
01755   }
01756   const QString subRes = res->subresourceIdentifier( incidence );
01757   if ( !subRes.contains( "/.INBOX.directory/" ) ) {
01758     return false;
01759   }
01760   return true;
01761 }
01762 
01763 QString IncidenceFormatter::formatICalInvitation( QString invitation, Calendar *mCalendar,
01764     InvitationFormatterHelper *helper )
01765 {
01766   if ( invitation.isEmpty() ) return QString::null;
01767 
01768   ICalFormat format;
01769   // parseScheduleMessage takes the tz from the calendar, no need to set it manually here for the format!
01770   ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
01771 
01772   if( !msg ) {
01773     kdDebug( 5850 ) << "Failed to parse the scheduling message" << endl;
01774     Q_ASSERT( format.exception() );
01775     kdDebug( 5850 ) << format.exception()->message() << endl;
01776     return QString::null;
01777   }
01778 
01779   IncidenceBase *incBase = msg->event();
01780 
01781   // Determine if this incidence is in my calendar (and owned by me)
01782   Incidence *existingIncidence = 0;
01783   if ( incBase && helper->calendar() ) {
01784     existingIncidence = helper->calendar()->incidence( incBase->uid() );
01785     if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
01786       existingIncidence = 0;
01787     }
01788     if ( !existingIncidence ) {
01789       const Incidence::List list = helper->calendar()->incidences();
01790       for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
01791         if ( (*it)->schedulingID() == incBase->uid() &&
01792              incidenceOwnedByMe( helper->calendar(), *it ) ) {
01793           existingIncidence = *it;
01794           break;
01795         }
01796       }
01797     }
01798   }
01799 
01800   // First make the text of the message
01801   QString html;
01802 
01803   QString tableStyle = QString::fromLatin1(
01804     "style=\"border: solid 1px; margin: 0em;\"" );
01805   QString tableHead = QString::fromLatin1(
01806     "<div align=\"center\">"
01807     "<table width=\"80%\" cellpadding=\"1\" cellspacing=\"0\" %1>"
01808     "<tr><td>").arg(tableStyle);
01809 
01810   html += tableHead;
01811   InvitationHeaderVisitor headerVisitor;
01812   // The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled
01813   if ( !headerVisitor.act( incBase, msg ) )
01814     return QString::null;
01815   html += "<b>" + headerVisitor.result() + "</b>";
01816 
01817   InvitationBodyVisitor bodyVisitor;
01818   if ( !bodyVisitor.act( incBase, msg ) )
01819     return QString::null;
01820   html += bodyVisitor.result();
01821 
01822   if ( msg->method() == Scheduler::Request ) { // ### Scheduler::Publish/Refresh/Add as well?
01823     IncidenceCompareVisitor compareVisitor;
01824     if ( compareVisitor.act( incBase, existingIncidence ) ) {
01825       html += i18n("<p align=\"left\">The following changes have been made by the organizer:</p>");
01826       html += compareVisitor.result();
01827     }
01828   }
01829 
01830   Incidence *inc = dynamic_cast<Incidence*>( incBase );
01831 
01832   // determine if I am the organizer for this invitation
01833   bool myInc = iamOrganizer( inc );
01834 
01835   // determine if the invitation response has already been recorded
01836   bool rsvpRec = false;
01837   Attendee *ea = 0;
01838   if ( !myInc ) {
01839     if ( existingIncidence ) {
01840       ea = findMyAttendee( existingIncidence );
01841     }
01842     if ( ea && ( ea->status() == Attendee::Accepted || ea->status() == Attendee::Declined ) ) {
01843       rsvpRec = true;
01844     }
01845   }
01846 
01847   // Print if RSVP needed, not-needed, or response already recorded
01848   bool rsvpReq = rsvpRequested( inc );
01849   if ( !myInc ) {
01850     html += "<br/>";
01851     html += "<i><u>";
01852     if ( rsvpRec && ( inc && inc->revision() == 0 ) ) {
01853       html += i18n( "Your response has already been recorded [%1]" ).
01854               arg( ea->statusStr() );
01855       rsvpReq = false;
01856     } else if ( msg->method() == Scheduler::Cancel ) {
01857       html += i18n( "This invitation was declined" );
01858     } else if ( msg->method() == Scheduler::Add ) {
01859       html += i18n( "This invitation was accepted" );
01860     } else {
01861       html += rsvpRequestedStr( rsvpReq );
01862     }
01863     html += "</u></i><br>";
01864   }
01865 
01866   // Add groupware links
01867 
01868   html += "<table border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td></tr><tr>";
01869 
01870   switch ( msg->method() ) {
01871     case Scheduler::Publish:
01872     case Scheduler::Request:
01873     case Scheduler::Refresh:
01874     case Scheduler::Add:
01875     {
01876       if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
01877         if ( inc->type() == "Todo" ) {
01878           html += "<td colspan=\"9\">";
01879           html += helper->makeLink( "reply", i18n( "[Record invitation to my task list]" ) );
01880         } else {
01881           html += "<td colspan=\"13\">";
01882           html += helper->makeLink( "reply", i18n( "[Record invitation to my calendar]" ) );
01883         }
01884         html += "</td></tr><tr>";
01885       }
01886       html += "<td>";
01887 
01888       if ( !myInc ) {
01889         if ( rsvpReq ) {
01890           // Accept
01891           html += helper->makeLink( "accept", i18n( "[Accept]" ) );
01892           html += "</td><td> &nbsp; </td><td>";
01893           html += helper->makeLink( "accept_conditionally",
01894                                     i18n( "Accept conditionally", "[Accept cond.]" ) );
01895           html += "</td><td> &nbsp; </td><td>";
01896         }
01897 
01898         if ( rsvpReq ) {
01899           // Counter proposal
01900           html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
01901           html += "</td><td> &nbsp; </td><td>";
01902         }
01903 
01904         if ( rsvpReq ) {
01905           // Decline
01906           html += helper->makeLink( "decline", i18n( "[Decline]" ) );
01907           html += "</td><td> &nbsp; </td><td>";
01908         }
01909 
01910         if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
01911           // Delegate
01912           html += helper->makeLink( "delegate", i18n( "[Delegate]" ) );
01913           html += "</td><td> &nbsp; </td><td>";
01914 
01915           // Forward
01916           html += helper->makeLink( "forward", i18n( "[Forward]" ) );
01917 
01918           // Check calendar
01919           if ( inc && inc->type() == "Event" ) {
01920             html += "</td><td> &nbsp; </td><td>";
01921             html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01922           }
01923         }
01924       }
01925       break;
01926     }
01927 
01928     case Scheduler::Cancel:
01929       // Remove invitation
01930       if ( inc->type() == "Todo" ) {
01931         html += helper->makeLink( "cancel", i18n( "[Remove invitation from my task list]" ) );
01932       } else {
01933         html += helper->makeLink( "cancel", i18n( "[Remove invitation from my calendar]" ) );
01934       }
01935       break;
01936 
01937     case Scheduler::Reply:
01938     {
01939       // Record invitation response
01940       Attendee *a = 0;
01941       Attendee *ea = 0;
01942       if ( inc ) {
01943         a = inc->attendees().first();
01944         if ( a ) {
01945           ea = findAttendee( existingIncidence, a->email() );
01946         }
01947       }
01948       if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
01949         if ( inc && inc->revision() > 0 ) {
01950           html += "<br><u><i>";
01951           html += i18n( "The response has been recorded [%1]" ).arg( ea->statusStr() );
01952           html += "</i></u>";
01953         }
01954       } else {
01955         if ( inc ) {
01956           if ( inc->type() == "Todo" ) {
01957             html += helper->makeLink( "reply", i18n( "[Record response into my task list]" ) );
01958           } else {
01959             html += helper->makeLink( "reply", i18n( "[Record response into my calendar]" ) );
01960           }
01961         }
01962       }
01963       break;
01964     }
01965 
01966     case Scheduler::Counter:
01967       // Counter proposal
01968       html += helper->makeLink( "accept_counter", i18n("[Accept]") );
01969       html += "&nbsp;";
01970       html += helper->makeLink( "decline_counter", i18n("[Decline]") );
01971       html += "&nbsp;";
01972       html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01973       break;
01974 
01975     case Scheduler::Declinecounter:
01976     case Scheduler::NoMethod:
01977       break;
01978   }
01979 
01980   // close the groupware table
01981   html += "</td></tr></table>";
01982 
01983   // Add the attendee list if I am the organizer
01984   if ( myInc && helper->calendar() ) {
01985     html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
01986   }
01987 
01988   // close the top-level table
01989   html += "</td></tr></table><br></div>";
01990 
01991   // Add the attachment list
01992   html += invitationAttachments( helper, inc );
01993 
01994   return html;
01995 }
01996 
01997 
01998 
01999 
02000 /*******************************************************************
02001  *  Helper functions for the msTNEF -> VPart converter
02002  *******************************************************************/
02003 
02004 
02005 //-----------------------------------------------------------------------------
02006 
02007 static QString stringProp( KTNEFMessage* tnefMsg, const Q_UINT32& key,
02008                            const QString& fallback = QString::null)
02009 {
02010   return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16,
02011                             fallback );
02012 }
02013 
02014 static QString sNamedProp( KTNEFMessage* tnefMsg, const QString& name,
02015                            const QString& fallback = QString::null )
02016 {
02017   return tnefMsg->findNamedProp( name, fallback );
02018 }
02019 
02020 struct save_tz { char* old_tz; char* tz_env_str; };
02021 
02022 /* temporarily go to a different timezone */
02023 static struct save_tz set_tz( const char* _tc )
02024 {
02025   const char *tc = _tc?_tc:"UTC";
02026 
02027   struct save_tz rv;
02028 
02029   rv.old_tz = 0;
02030   rv.tz_env_str = 0;
02031 
02032   //kdDebug(5006) << "set_tz(), timezone before = " << timezone << endl;
02033 
02034   char* tz_env = 0;
02035   if( getenv( "TZ" ) ) {
02036     tz_env = strdup( getenv( "TZ" ) );
02037     rv.old_tz = tz_env;
02038   }
02039   char* tmp_env = (char*)malloc( strlen( tc ) + 4 );
02040   strcpy( tmp_env, "TZ=" );
02041   strcpy( tmp_env+3, tc );
02042   putenv( tmp_env );
02043 
02044   rv.tz_env_str = tmp_env;
02045 
02046   /* tmp_env is not free'ed -- it is part of the environment */
02047 
02048   tzset();
02049   //kdDebug(5006) << "set_tz(), timezone after = " << timezone << endl;
02050 
02051   return rv;
02052 }
02053 
02054 /* restore previous timezone */
02055 static void unset_tz( struct save_tz old_tz )
02056 {
02057   if( old_tz.old_tz ) {
02058     char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 );
02059     strcpy( tmp_env, "TZ=" );
02060     strcpy( tmp_env+3, old_tz.old_tz );
02061     putenv( tmp_env );
02062     /* tmp_env is not free'ed -- it is part of the environment */
02063     free( old_tz.old_tz );
02064   } else {
02065     /* clear TZ from env */
02066     putenv( strdup("TZ") );
02067   }
02068   tzset();
02069 
02070   /* is this OK? */
02071   if( old_tz.tz_env_str ) free( old_tz.tz_env_str );
02072 }
02073 
02074 static QDateTime utc2Local( const QDateTime& utcdt )
02075 {
02076   struct tm tmL;
02077 
02078   save_tz tmp_tz = set_tz("UTC");
02079   time_t utc = utcdt.toTime_t();
02080   unset_tz( tmp_tz );
02081 
02082   localtime_r( &utc, &tmL );
02083   return QDateTime( QDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ),
02084                     QTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) );
02085 }
02086 
02087 
02088 static QDateTime pureISOToLocalQDateTime( const QString& dtStr,
02089                                           bool bDateOnly = false )
02090 {
02091   QDate tmpDate;
02092   QTime tmpTime;
02093   int year, month, day, hour, minute, second;
02094 
02095   if( bDateOnly ) {
02096     year = dtStr.left( 4 ).toInt();
02097     month = dtStr.mid( 4, 2 ).toInt();
02098     day = dtStr.mid( 6, 2 ).toInt();
02099     hour = 0;
02100     minute = 0;
02101     second = 0;
02102   } else {
02103     year = dtStr.left( 4 ).toInt();
02104     month = dtStr.mid( 4, 2 ).toInt();
02105     day = dtStr.mid( 6, 2 ).toInt();
02106     hour = dtStr.mid( 9, 2 ).toInt();
02107     minute = dtStr.mid( 11, 2 ).toInt();
02108     second = dtStr.mid( 13, 2 ).toInt();
02109   }
02110   tmpDate.setYMD( year, month, day );
02111   tmpTime.setHMS( hour, minute, second );
02112 
02113   if( tmpDate.isValid() && tmpTime.isValid() ) {
02114     QDateTime dT = QDateTime( tmpDate, tmpTime );
02115 
02116     if( !bDateOnly ) {
02117       // correct for GMT ( == Zulu time == UTC )
02118       if (dtStr.at(dtStr.length()-1) == 'Z') {
02119         //dT = dT.addSecs( 60 * KRFCDate::localUTCOffset() );
02120         //localUTCOffset( dT ) );
02121         dT = utc2Local( dT );
02122       }
02123     }
02124     return dT;
02125   } else
02126     return QDateTime();
02127 }
02128 
02129 
02130 
02131 QString IncidenceFormatter::msTNEFToVPart( const QByteArray& tnef )
02132 {
02133   bool bOk = false;
02134 
02135   KTNEFParser parser;
02136   QBuffer buf( tnef );
02137   CalendarLocal cal ( QString::fromLatin1( "UTC" ) );
02138   KABC::Addressee addressee;
02139   KABC::VCardConverter cardConv;
02140   ICalFormat calFormat;
02141   Event* event = new Event();
02142 
02143   if( parser.openDevice( &buf ) ) {
02144     KTNEFMessage* tnefMsg = parser.message();
02145     //QMap<int,KTNEFProperty*> props = parser.message()->properties();
02146 
02147     // Everything depends from property PR_MESSAGE_CLASS
02148     // (this is added by KTNEFParser):
02149     QString msgClass = tnefMsg->findProp( 0x001A, QString::null, true )
02150       .upper();
02151     if( !msgClass.isEmpty() ) {
02152       // Match the old class names that might be used by Outlook for
02153       // compatibility with Microsoft Mail for Windows for Workgroups 3.1.
02154       bool bCompatClassAppointment = false;
02155       bool bCompatMethodRequest = false;
02156       bool bCompatMethodCancled = false;
02157       bool bCompatMethodAccepted = false;
02158       bool bCompatMethodAcceptedCond = false;
02159       bool bCompatMethodDeclined = false;
02160       if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) {
02161         bCompatClassAppointment = true;
02162         if( msgClass.endsWith( ".MTGREQ" ) )
02163           bCompatMethodRequest = true;
02164         if( msgClass.endsWith( ".MTGCNCL" ) )
02165           bCompatMethodCancled = true;
02166         if( msgClass.endsWith( ".MTGRESPP" ) )
02167           bCompatMethodAccepted = true;
02168         if( msgClass.endsWith( ".MTGRESPA" ) )
02169           bCompatMethodAcceptedCond = true;
02170         if( msgClass.endsWith( ".MTGRESPN" ) )
02171           bCompatMethodDeclined = true;
02172       }
02173       bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" );
02174 
02175       if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) {
02176         // Compose a vCal
02177         bool bIsReply = false;
02178         QString prodID = "-//Microsoft Corporation//Outlook ";
02179         prodID += tnefMsg->findNamedProp( "0x8554", "9.0" );
02180         prodID += "MIMEDIR/EN\n";
02181         prodID += "VERSION:2.0\n";
02182         calFormat.setApplication( "Outlook", prodID );
02183 
02184         Scheduler::Method method;
02185         if( bCompatMethodRequest )
02186           method = Scheduler::Request;
02187         else if( bCompatMethodCancled )
02188           method = Scheduler::Cancel;
02189         else if( bCompatMethodAccepted || bCompatMethodAcceptedCond ||
02190                  bCompatMethodDeclined ) {
02191           method = Scheduler::Reply;
02192           bIsReply = true;
02193         } else {
02194           // pending(khz): verify whether "0x0c17" is the right tag ???
02195           //
02196           // at the moment we think there are REQUESTS and UPDATES
02197           //
02198           // but WHAT ABOUT REPLIES ???
02199           //
02200           //
02201 
02202           if( tnefMsg->findProp(0x0c17) == "1" )
02203             bIsReply = true;
02204           method = Scheduler::Request;
02205         }
02206 
02208         ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown );
02209 
02210         QString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) );
02211 
02212         if( !sSenderSearchKeyEmail.isEmpty() ) {
02213           int colon = sSenderSearchKeyEmail.find( ':' );
02214           // May be e.g. "SMTP:KHZ@KDE.ORG"
02215           if( sSenderSearchKeyEmail.find( ':' ) == -1 )
02216             sSenderSearchKeyEmail.remove( 0, colon+1 );
02217         }
02218 
02219         QString s( tnefMsg->findProp( 0x0e04 ) );
02220         QStringList attendees = QStringList::split( ';', s );
02221         if( attendees.count() ) {
02222           for( QStringList::Iterator it = attendees.begin();
02223                it != attendees.end(); ++it ) {
02224             // Skip all entries that have no '@' since these are
02225             // no mail addresses
02226             if( (*it).find('@') == -1 ) {
02227               s = (*it).stripWhiteSpace();
02228 
02229               Attendee *attendee = new Attendee( s, s, true );
02230               if( bIsReply ) {
02231                 if( bCompatMethodAccepted )
02232                   attendee->setStatus( Attendee::Accepted );
02233                 if( bCompatMethodDeclined )
02234                   attendee->setStatus( Attendee::Declined );
02235                 if( bCompatMethodAcceptedCond )
02236                   attendee->setStatus(Attendee::Tentative);
02237               } else {
02238                 attendee->setStatus( Attendee::NeedsAction );
02239                 attendee->setRole( Attendee::ReqParticipant );
02240               }
02241               event->addAttendee(attendee);
02242             }
02243           }
02244         } else {
02245           // Oops, no attendees?
02246           // This must be old style, let us use the PR_SENDER_SEARCH_KEY.
02247           s = sSenderSearchKeyEmail;
02248           if( !s.isEmpty() ) {
02249             Attendee *attendee = new Attendee( QString::null, QString::null,
02250                                                true );
02251             if( bIsReply ) {
02252               if( bCompatMethodAccepted )
02253                 attendee->setStatus( Attendee::Accepted );
02254               if( bCompatMethodAcceptedCond )
02255                 attendee->setStatus( Attendee::Declined );
02256               if( bCompatMethodDeclined )
02257                 attendee->setStatus( Attendee::Tentative );
02258             } else {
02259               attendee->setStatus(Attendee::NeedsAction);
02260               attendee->setRole(Attendee::ReqParticipant);
02261             }
02262             event->addAttendee(attendee);
02263           }
02264         }
02265         s = tnefMsg->findProp( 0x0c1f ); // look for organizer property
02266         if( s.isEmpty() && !bIsReply )
02267           s = sSenderSearchKeyEmail;
02268         // TODO: Use the common name?
02269         if( !s.isEmpty() )
02270           event->setOrganizer( s );
02271 
02272         s = tnefMsg->findProp( 0x8516 ).replace( QChar( '-' ), QString::null )
02273           .replace( QChar( ':' ), QString::null );
02274         event->setDtStart( QDateTime::fromString( s ) ); // ## Format??
02275 
02276         s = tnefMsg->findProp( 0x8517 ).replace( QChar( '-' ), QString::null )
02277           .replace( QChar( ':' ), QString::null );
02278         event->setDtEnd( QDateTime::fromString( s ) );
02279 
02280         s = tnefMsg->findProp( 0x8208 );
02281         event->setLocation( s );
02282 
02283         // is it OK to set this to OPAQUE always ??
02284         //vPart += "TRANSP:OPAQUE\n"; ###FIXME, portme!
02285         //vPart += "SEQUENCE:0\n";
02286 
02287         // is "0x0023" OK  -  or should we look for "0x0003" ??
02288         s = tnefMsg->findProp( 0x0023 );
02289         event->setUid( s );
02290 
02291         // PENDING(khz): is this value in local timezone? Must it be
02292         // adjusted? Most likely this is a bug in the server or in
02293         // Outlook - we ignore it for now.
02294         s = tnefMsg->findProp( 0x8202 ).replace( QChar( '-' ), QString::null )
02295           .replace( QChar( ':' ), QString::null );
02296         // ### libkcal always uses currentDateTime()
02297         // event->setDtStamp(QDateTime::fromString(s));
02298 
02299         s = tnefMsg->findNamedProp( "Keywords" );
02300         event->setCategories( s );
02301 
02302         s = tnefMsg->findProp( 0x1000 );
02303         event->setDescription( s );
02304 
02305         s = tnefMsg->findProp( 0x0070 );
02306         event->setSummary( s );
02307 
02308         s = tnefMsg->findProp( 0x0026 );
02309         event->setPriority( s.toInt() );
02310 
02311         // is reminder flag set ?
02312         if(!tnefMsg->findProp(0x8503).isEmpty()) {
02313           Alarm *alarm = new Alarm(event);
02314           QDateTime highNoonTime =
02315             pureISOToLocalQDateTime( tnefMsg->findProp( 0x8502 )
02316                                      .replace( QChar( '-' ), "" )
02317                                      .replace( QChar( ':' ), "" ) );
02318           QDateTime wakeMeUpTime =
02319             pureISOToLocalQDateTime( tnefMsg->findProp( 0x8560, "" )
02320                                      .replace( QChar( '-' ), "" )
02321                                      .replace( QChar( ':' ), "" ) );
02322           alarm->setTime(wakeMeUpTime);
02323 
02324           if( highNoonTime.isValid() && wakeMeUpTime.isValid() )
02325             alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) );
02326           else
02327             // default: wake them up 15 minutes before the appointment
02328             alarm->setStartOffset( Duration( 15*60 ) );
02329           alarm->setDisplayAlarm( i18n( "Reminder" ) );
02330 
02331           // Sorry: the different action types are not known (yet)
02332           //        so we always set 'DISPLAY' (no sounds, no images...)
02333           event->addAlarm( alarm );
02334         }
02335         cal.addEvent( event );
02336         bOk = true;
02337         // we finished composing a vCal
02338       } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) {
02339         addressee.setUid( stringProp( tnefMsg, attMSGID ) );
02340         addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) );
02341         addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true );
02342         addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false );
02343         addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false );
02344         addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) );
02345         addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) );
02346         addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) );
02347         addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) );
02348         addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) );
02349         addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) );
02350         addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) );
02351 
02352         QString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY )
02353           .replace( QChar( '-' ), QString::null )
02354           .replace( QChar( ':' ), QString::null );
02355         if( !s.isEmpty() )
02356           addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s );
02357 
02358         addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE )  ) );
02359 
02360         // collect parts of Name entry
02361         addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) );
02362         addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) );
02363         addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) );
02364         addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) );
02365         addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) );
02366 
02367         addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) );
02368         addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) );
02369         addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) );
02370         /*
02371         the MAPI property ID of this (multiline) )field is unknown:
02372         vPart += stringProp(tnefMsg, "\n","NOTE", ... , "" );
02373         */
02374 
02375         KABC::Address adr;
02376         adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) );
02377         adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) );
02378         adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) );
02379         adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) );
02380         adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) );
02381         adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) );
02382         adr.setType(KABC::Address::Home);
02383         addressee.insertAddress(adr);
02384 
02385         adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) );
02386         adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) );
02387         adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) );
02388         adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) );
02389         adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) );
02390         adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) );
02391         adr.setType( KABC::Address::Work );
02392         addressee.insertAddress( adr );
02393 
02394         adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) );
02395         adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) );
02396         adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) );
02397         adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) );
02398         adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) );
02399         adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) );
02400         adr.setType( KABC::Address::Dom );
02401         addressee.insertAddress(adr);
02402 
02403         // problem: the 'other' address was stored by KOrganizer in
02404         //          a line looking like the following one:
02405         // 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
02406 
02407         QString nr;
02408         nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER );
02409         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) );
02410         nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER );
02411         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) );
02412         nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER );
02413         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) );
02414         nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER );
02415         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) );
02416         nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER );
02417         addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) );
02418 
02419         s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY )
02420           .replace( QChar( '-' ), QString::null )
02421           .replace( QChar( ':' ), QString::null );
02422         if( !s.isEmpty() )
02423           addressee.setBirthday( QDateTime::fromString( s ) );
02424 
02425         bOk = ( !addressee.isEmpty() );
02426       } else if( "IPM.NOTE" == msgClass ) {
02427 
02428       } // else if ... and so on ...
02429     }
02430   }
02431 
02432   // Compose return string
02433   QString iCal = calFormat.toString( &cal );
02434   if( !iCal.isEmpty() )
02435     // This was an iCal
02436     return iCal;
02437 
02438   // Not an iCal - try a vCard
02439   KABC::VCardConverter converter;
02440   return converter.createVCard( addressee );
02441 }
02442 
02443 
02444 QString IncidenceFormatter::formatTNEFInvitation( const QByteArray& tnef,
02445         Calendar *mCalendar, InvitationFormatterHelper *helper )
02446 {
02447   QString vPart = IncidenceFormatter::msTNEFToVPart( tnef );
02448   QString iCal = IncidenceFormatter::formatICalInvitation( vPart, mCalendar, helper );
02449   if( !iCal.isEmpty() )
02450     return iCal;
02451   return vPart;
02452 }
02453 
02454 
02455 
02456 
02457 /*******************************************************************
02458  *  Helper functions for the Incidence tooltips
02459  *******************************************************************/
02460 
02461 class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
02462 {
02463   public:
02464     ToolTipVisitor()
02465       : mCalendar( 0 ), mRichText( true ), mResult( "" ) {}
02466 
02467     bool act( Calendar *calendar, IncidenceBase *incidence, bool richText=true)
02468     {
02469       mCalendar = calendar;
02470       mRichText = richText;
02471       mResult = "";
02472       return incidence ? incidence->accept( *this ) : false;
02473     }
02474     QString result() const { return mResult; }
02475 
02476   protected:
02477     bool visit( Event *event );
02478     bool visit( Todo *todo );
02479     bool visit( Journal *journal );
02480     bool visit( FreeBusy *fb );
02481 
02482     QString dateRangeText( Event*event );
02483     QString dateRangeText( Todo *todo );
02484     QString dateRangeText( Journal *journal );
02485     QString dateRangeText( FreeBusy *fb );
02486 
02487     QString generateToolTip( Incidence* incidence, QString dtRangeText );
02488 
02489   protected:
02490     Calendar *mCalendar;
02491     bool mRichText;
02492     QString mResult;
02493 };
02494 
02495 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event*event )
02496 {
02497   QString ret;
02498   QString tmp;
02499   if ( event->isMultiDay() ) {
02500 
02501     tmp = "<br>" + i18n("Event start", "<i>From:</i>&nbsp;%1");
02502     if (event->doesFloat())
02503       ret += tmp.arg( IncidenceFormatter::dateToString( event->dtStart(), false ).replace(" ", "&nbsp;") );
02504     else
02505       ret += tmp.arg( IncidenceFormatter::dateToString( event->dtStart() ).replace(" ", "&nbsp;") );
02506 
02507     tmp = "<br>" + i18n("Event end","<i>To:</i>&nbsp;%1");
02508     if (event->doesFloat())
02509       ret += tmp.arg( IncidenceFormatter::dateToString( event->dtEnd(), false ).replace(" ", "&nbsp;") );
02510     else
02511       ret += tmp.arg( IncidenceFormatter::dateToString( event->dtEnd() ).replace(" ", "&nbsp;") );
02512 
02513   } else {
02514 
02515     ret += "<br>"+i18n("<i>Date:</i>&nbsp;%1").
02516            arg( IncidenceFormatter::dateToString( event->dtStart(), false ).replace(" ", "&nbsp;") );
02517     if ( !event->doesFloat() ) {
02518       const QString dtStartTime =
02519         IncidenceFormatter::timeToString( event->dtStart(), true ).replace( " ", "&nbsp;" );
02520       const QString dtEndTime =
02521         IncidenceFormatter::timeToString( event->dtEnd(), true ).replace( " ", "&nbsp;" );
02522       if ( dtStartTime == dtEndTime ) { // to prevent 'Time: 17:00 - 17:00'
02523         tmp = "<br>" + i18n("time for event, &nbsp; to prevent ugly line breaks",
02524         "<i>Time:</i>&nbsp;%1").
02525         arg( dtStartTime );
02526       } else {
02527         tmp = "<br>" + i18n("time range for event, &nbsp; to prevent ugly line breaks",
02528         "<i>Time:</i>&nbsp;%1&nbsp;-&nbsp;%2").
02529         arg( dtStartTime, dtEndTime );
02530       }
02531       ret += tmp;
02532     }
02533 
02534   }
02535   return ret;
02536 }
02537 
02538 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo*todo )
02539 {
02540   QString ret;
02541   bool floats( todo->doesFloat() );
02542   if ( todo->hasStartDate() && todo->dtStart().isValid() )
02543     // No need to add <i> here. This is separated issue and each line
02544     // is very visible on its own. On the other hand... Yes, I like it
02545     // italics here :)
02546     ret += "<br>" + i18n("<i>Start:</i>&nbsp;%1").arg(
02547       (floats)
02548       ?(IncidenceFormatter::dateToString( todo->dtStart( false ), false ).replace(" ", "&nbsp;"))
02549         :(todo->dtStartStr( false ).replace(" ", "&nbsp;")) ) ;
02550   if ( todo->hasDueDate() && todo->dtDue().isValid() )
02551     ret += "<br>" +
02552            i18n("<i>Due:</i>&nbsp;%1").
02553            arg( IncidenceFormatter::dateTimeToString( todo->dtDue(), floats, false ).replace(" ", "&nbsp;"));
02554 
02555   if (todo->isCompleted())
02556     ret += "<br>" + i18n("<i>Completed:</i>&nbsp;%1").arg( todo->completedStr().replace(" ", "&nbsp;") );
02557   else
02558     ret += "<br>" + i18n("%1% completed").arg(todo->percentComplete());
02559 
02560   return ret;
02561 }
02562 
02563 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal )
02564 {
02565   QString ret;
02566   if (journal->dtStart().isValid() ) {
02567     ret += "<br>" +
02568            i18n("<i>Date:</i>&nbsp;%1").
02569            arg( IncidenceFormatter::dateToString( journal->dtStart(), false ) );
02570   }
02571   return ret;
02572 }
02573 
02574 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
02575 {
02576   QString tmp( "<br>" + i18n("<i>Period start:</i>&nbsp;%1") );
02577   QString ret = tmp.arg( KGlobal::locale()->formatDateTime( fb->dtStart() ) );
02578   tmp = "<br>" + i18n("<i>Period start:</i>&nbsp;%1");
02579   ret += tmp.arg( KGlobal::locale()->formatDateTime( fb->dtEnd() ) );
02580   return ret;
02581 }
02582 
02583 
02584 
02585 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
02586 {
02587   mResult = generateToolTip( event, dateRangeText( event ) );
02588   return !mResult.isEmpty();
02589 }
02590 
02591 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
02592 {
02593   mResult = generateToolTip( todo, dateRangeText( todo ) );
02594   return !mResult.isEmpty();
02595 }
02596 
02597 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
02598 {
02599   mResult = generateToolTip( journal, dateRangeText( journal ) );
02600   return !mResult.isEmpty();
02601 }
02602 
02603 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
02604 {
02605   mResult = "<qt><b>" + i18n("Free/Busy information for %1")
02606         .arg(fb->organizer().fullName()) + "</b>";
02607   mResult += dateRangeText( fb );
02608   mResult += "</qt>";
02609   return !mResult.isEmpty();
02610 }
02611 
02612 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, QString dtRangeText )
02613 {
02614   if ( !incidence )
02615     return QString::null;
02616 
02617   QString tmp = "<qt><b>"+ incidence->summary().replace("\n", "<br>")+"</b>";
02618 
02619   if ( mCalendar ) {
02620     QString calStr = IncidenceFormatter::resourceString( mCalendar, incidence );
02621     if ( !calStr.isEmpty() ) {
02622       tmp += "<br>" + i18n( "<i>Calendar:</i> %1" ).arg( calStr );
02623     }
02624   }
02625 
02626   tmp += dtRangeText;
02627 
02628   if (!incidence->location().isEmpty()) {
02629     tmp += "<br>"+i18n("<i>Location:</i>&nbsp;%1").
02630       arg( incidence->location().replace("\n", "<br>") );
02631   }
02632   if (!incidence->description().isEmpty()) {
02633     QString desc(incidence->description());
02634     if (desc.length()>120) {
02635       desc = desc.left(120) + "...";
02636     }
02637     tmp += "<br>----------<br>" + i18n("<i>Description:</i><br>") + desc.replace("\n", "<br>");
02638   }
02639   tmp += "</qt>";
02640   return tmp;
02641 }
02642 
02643 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText )
02644 {
02645   return toolTipStr( 0, incidence, richText );
02646 }
02647 
02648 QString IncidenceFormatter::toolTipStr( Calendar *calendar,
02649                                         IncidenceBase *incidence,
02650                                         bool richText )
02651 {
02652   ToolTipVisitor v;
02653   if ( v.act( calendar, incidence, richText ) ) {
02654     return v.result();
02655   } else {
02656     return QString::null;
02657   }
02658 }
02659 
02660 /*******************************************************************
02661  *  Helper functions for the Incidence tooltips
02662  *******************************************************************/
02663 
02664 class IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor
02665 {
02666   public:
02667     MailBodyVisitor() : mResult( "" ) {}
02668 
02669     bool act( IncidenceBase *incidence )
02670     {
02671       mResult = "";
02672       return incidence ? incidence->accept( *this ) : false;
02673     }
02674     QString result() const { return mResult; }
02675 
02676   protected:
02677     bool visit( Event *event );
02678     bool visit( Todo *todo );
02679     bool visit( Journal *journal );
02680     bool visit( FreeBusy * ) { mResult = i18n("This is a Free Busy Object"); return !mResult.isEmpty(); }
02681   protected:
02682     QString mResult;
02683 };
02684 
02685 
02686 static QString mailBodyIncidence( Incidence *incidence )
02687 {
02688   QString body;
02689   if ( !incidence->summary().isEmpty() ) {
02690     body += i18n("Summary: %1\n").arg( incidence->summary() );
02691   }
02692   if ( !incidence->organizer().isEmpty() ) {
02693     body += i18n("Organizer: %1\n").arg( incidence->organizer().fullName() );
02694   }
02695   if ( !incidence->location().isEmpty() ) {
02696     body += i18n("Location: %1\n").arg( incidence->location() );
02697   }
02698   return body;
02699 }
02700 
02701 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
02702 {
02703   QString recurrence[]= {i18n("no recurrence", "None"),
02704     i18n("Minutely"), i18n("Hourly"), i18n("Daily"),
02705     i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"),
02706     i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
02707 
02708   mResult = mailBodyIncidence( event );
02709   mResult += i18n("Start Date: %1\n").
02710              arg( IncidenceFormatter::dateToString( event->dtStart(), true ) );
02711   if ( !event->doesFloat() ) {
02712     mResult += i18n("Start Time: %1\n").
02713                arg( IncidenceFormatter::timeToString( event->dtStart(), true ) );
02714   }
02715   if ( event->dtStart() != event->dtEnd() ) {
02716     mResult += i18n("End Date: %1\n").
02717                arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) );
02718   }
02719   if ( !event->doesFloat() ) {
02720     mResult += i18n("End Time: %1\n").
02721                arg( IncidenceFormatter::timeToString( event->dtEnd(), true ) );
02722   }
02723   if ( event->doesRecur() ) {
02724     Recurrence *recur = event->recurrence();
02725     // TODO: Merge these two to one of the form "Recurs every 3 days"
02726     mResult += i18n("Recurs: %1\n")
02727              .arg( recurrence[ recur->recurrenceType() ] );
02728     mResult += i18n("Frequency: %1\n")
02729              .arg( event->recurrence()->frequency() );
02730 
02731     if ( recur->duration() > 0 ) {
02732       mResult += i18n ("Repeats once", "Repeats %n times", recur->duration());
02733       mResult += '\n';
02734     } else {
02735       if ( recur->duration() != -1 ) {
02736 // TODO_Recurrence: What to do with floating
02737         QString endstr;
02738         if ( event->doesFloat() ) {
02739           endstr = KGlobal::locale()->formatDate( recur->endDate() );
02740         } else {
02741           endstr = KGlobal::locale()->formatDateTime( recur->endDateTime() );
02742         }
02743         mResult += i18n("Repeat until: %1\n").arg( endstr );
02744       } else {
02745         mResult += i18n("Repeats forever\n");
02746       }
02747     }
02748   }
02749   QString details = event->description();
02750   if ( !details.isEmpty() ) {
02751     mResult += i18n("Details:\n%1\n").arg( details );
02752   }
02753   return !mResult.isEmpty();
02754 }
02755 
02756 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
02757 {
02758   mResult = mailBodyIncidence( todo );
02759 
02760   if ( todo->hasStartDate() ) {
02761     mResult += i18n("Start Date: %1\n").
02762                arg( IncidenceFormatter::dateToString( todo->dtStart( false ), true ) );
02763     if ( !todo->doesFloat() ) {
02764       mResult += i18n("Start Time: %1\n").
02765                  arg( IncidenceFormatter::timeToString( todo->dtStart( false ),true ) );
02766     }
02767   }
02768   if ( todo->hasDueDate() ) {
02769     mResult += i18n("Due Date: %1\n").
02770                arg( IncidenceFormatter::dateToString( todo->dtDue(), true ) );
02771     if ( !todo->doesFloat() ) {
02772       mResult += i18n("Due Time: %1\n").
02773                  arg( IncidenceFormatter::timeToString( todo->dtDue(), true ) );
02774     }
02775   }
02776   QString details = todo->description();
02777   if ( !details.isEmpty() ) {
02778     mResult += i18n("Details:\n%1\n").arg( details );
02779   }
02780   return !mResult.isEmpty();
02781 }
02782 
02783 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
02784 {
02785   mResult = mailBodyIncidence( journal );
02786   mResult += i18n("Date: %1\n").
02787              arg( IncidenceFormatter::dateToString( journal->dtStart(), true ) );
02788   if ( !journal->doesFloat() ) {
02789     mResult += i18n("Time: %1\n").
02790                arg( IncidenceFormatter::timeToString( journal->dtStart(), true ) );
02791   }
02792   if ( !journal->description().isEmpty() )
02793     mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() );
02794   return !mResult.isEmpty();
02795 }
02796 
02797 
02798 
02799 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
02800 {
02801   if ( !incidence )
02802     return QString::null;
02803 
02804   MailBodyVisitor v;
02805   if ( v.act( incidence ) ) {
02806     return v.result();
02807   }
02808   return QString::null;
02809 }
02810 
02811 /************************************
02812  *  More static formatting functions
02813  ************************************/
02814 
02815 QString IncidenceFormatter::recurrenceString(Incidence * incidence)
02816 {
02817   if ( !incidence->doesRecur() )
02818     return i18n( "No recurrence" );
02819 
02820      // recurrence
02821   QStringList dayList;
02822   dayList.append( i18n( "31st Last" ) );
02823   dayList.append( i18n( "30th Last" ) );
02824   dayList.append( i18n( "29th Last" ) );
02825   dayList.append( i18n( "28th Last" ) );
02826   dayList.append( i18n( "27th Last" ) );
02827   dayList.append( i18n( "26th Last" ) );
02828   dayList.append( i18n( "25th Last" ) );
02829   dayList.append( i18n( "24th Last" ) );
02830   dayList.append( i18n( "23rd Last" ) );
02831   dayList.append( i18n( "22nd Last" ) );
02832   dayList.append( i18n( "21st Last" ) );
02833   dayList.append( i18n( "20th Last" ) );
02834   dayList.append( i18n( "19th Last" ) );
02835   dayList.append( i18n( "18th Last" ) );
02836   dayList.append( i18n( "17th Last" ) );
02837   dayList.append( i18n( "16th Last" ) );
02838   dayList.append( i18n( "15th Last" ) );
02839   dayList.append( i18n( "14th Last" ) );
02840   dayList.append( i18n( "13th Last" ) );
02841   dayList.append( i18n( "12th Last" ) );
02842   dayList.append( i18n( "11th Last" ) );
02843   dayList.append( i18n( "10th Last" ) );
02844   dayList.append( i18n( "9th Last" ) );
02845   dayList.append( i18n( "8th Last" ) );
02846   dayList.append( i18n( "7th Last" ) );
02847   dayList.append( i18n( "6th Last" ) );
02848   dayList.append( i18n( "5th Last" ) );
02849   dayList.append( i18n( "4th Last" ) );
02850   dayList.append( i18n( "3rd Last" ) );
02851   dayList.append( i18n( "2nd Last" ) );
02852   dayList.append( i18n( "last day of the month", "Last" ) );
02853   dayList.append( i18n( "unknown day of the month", "unknown" ) ); //#31 - zero offset from UI
02854   dayList.append( i18n( "1st" ) );
02855   dayList.append( i18n( "2nd" ) );
02856   dayList.append( i18n( "3rd" ) );
02857   dayList.append( i18n( "4th" ) );
02858   dayList.append( i18n( "5th" ) );
02859 
02860   QString recurString;
02861   const KCalendarSystem *calSys = KGlobal::locale()->calendar();;
02862 
02863   Recurrence *recurs = incidence->recurrence();
02864   switch ( recurs->recurrenceType() ) {
02865 
02866       case Recurrence::rNone:
02867           recurString = i18n( "no recurrence", "None" );
02868           break;
02869       case Recurrence::rDaily:
02870           recurString = i18n( "Every day", "Every %1 days", recurs->frequency() );
02871           break;
02872       case Recurrence::rWeekly:
02873       {
02874           QString dayNames;
02875           // Respect start of week setting
02876           int weekStart = KGlobal::locale()->weekStartDay();
02877           bool addSpace = false;
02878           for ( int i = 0; i < 7; ++i ) {
02879               if ( recurs->days().testBit( (i+weekStart+6)%7 )) {
02880                   if (addSpace) dayNames.append(" ");
02881                   dayNames.append( calSys->weekDayName( ((i+weekStart+6)%7)+1, true ) );
02882                   addSpace=true;
02883               }
02884           }
02885           recurString = i18n( "Every week on %1",
02886                               "Every %n weeks on %1",
02887                               recurs->frequency()).arg( dayNames );
02888           break;
02889       }
02890       case Recurrence::rMonthlyPos:
02891       {
02892           KCal::RecurrenceRule::WDayPos rule = recurs->monthPositions()[0];
02893           recurString = i18n( "Every month on the %1 %2",
02894                               "Every %n months on the %1 %2",
02895                               recurs->frequency() ).arg(dayList[rule.pos() + 31]).arg(
02896                                       calSys->weekDayName( rule.day(),false ) );
02897           break;
02898       }
02899       case Recurrence::rMonthlyDay:
02900       {
02901           int days = recurs->monthDays()[0];
02902           if (days < 0) {
02903               recurString = i18n( "Every month on the %1 day",
02904                                   "Every %n months on the %1 day",
02905                                   recurs->frequency() ).arg( dayList[days + 31] );
02906           } else {
02907               recurString = i18n( "Every month on day %1",
02908                                   "Every %n months on day %1",
02909                                   recurs->frequency() ).arg( recurs->monthDays()[0] );
02910           }
02911           break;
02912       }
02913 
02914       case Recurrence::rYearlyMonth:
02915       {
02916           recurString = i18n( "Every year on day %1 of %2",
02917                               "Every %n years on day %1 of %2",
02918                               recurs->frequency() )
02919                   .arg(recurs->yearDates()[0])
02920                   .arg(calSys->monthName( recurs->yearMonths()[0], recurs->startDate().year() ) );
02921           break;
02922       }
02923       case Recurrence::rYearlyPos:
02924       {
02925           KCal::RecurrenceRule::WDayPos rule = recurs->yearPositions()[0];
02926           recurString = i18n( "Every year on the %1 %2 of %3",
02927                               "Every %n years on the %1 %2of %3",
02928                               recurs->frequency()).arg( dayList[rule.pos() + 31] )
02929                   .arg( calSys->weekDayName( rule.day(), false ))
02930                   .arg( calSys->monthName( recurs->yearMonths()[0], recurs->startDate().year() ) );
02931           break;
02932       }
02933       case Recurrence::rYearlyDay:
02934       {
02935           recurString = i18n( "Every year on day %1",
02936                               "Every %n years on day %1",
02937                               recurs->frequency()).arg( recurs->yearDays()[0] );
02938           break;
02939       }
02940 
02941       default:
02942           return i18n( "Incidence recurs" );
02943   }
02944 
02945   return recurString;
02946 }
02947 
02948 QString IncidenceFormatter::timeToString( const QDateTime &date, bool shortfmt )
02949 {
02950   return KGlobal::locale()->formatTime( date.time(), !shortfmt );
02951 }
02952 
02953 QString IncidenceFormatter::dateToString( const QDateTime &date, bool shortfmt )
02954 {
02955   return
02956     KGlobal::locale()->formatDate( date.date(), shortfmt );
02957 }
02958 
02959 QString IncidenceFormatter::dateTimeToString( const QDateTime &date,
02960                                               bool allDay, bool shortfmt )
02961 {
02962   if ( allDay ) {
02963     return dateToString( date, shortfmt );
02964   }
02965 
02966   return  KGlobal::locale()->formatDateTime( date, shortfmt );
02967 }
02968 
02969 QString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence )
02970 {
02971   if ( !calendar || !incidence ) {
02972     return QString::null;
02973   }
02974 
02975   CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar );
02976   if ( !calendarResource ) {
02977     return QString::null;
02978   }
02979 
02980   ResourceCalendar *resourceCalendar = calendarResource->resource( incidence );
02981   if ( resourceCalendar ) {
02982     if ( !resourceCalendar->subresources().isEmpty() ) {
02983       QString subRes = resourceCalendar->subresourceIdentifier( incidence );
02984       if ( subRes.isEmpty() ) {
02985         return resourceCalendar->resourceName();
02986       } else {
02987         return resourceCalendar->labelForSubresource( subRes );
02988       }
02989     }
02990     return resourceCalendar->resourceName();
02991   }
02992 
02993   return QString::null;
02994 }
KDE Home | KDE Accessibility Home | Description of Access Keys