libkcal

incidenceformatter.cpp

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