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