libkcal

incidenceformatter.cpp

00001 /*
00002     This file is part of libkcal.
00003 
00004     Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006     Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012 
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017 
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00021     Boston, MA 02110-1301, USA.
00022 */
00023 
00024 #include "incidenceformatter.h"
00025 
00026 #include <libkcal/attachment.h>
00027 #include <libkcal/event.h>
00028 #include <libkcal/todo.h>
00029 #include <libkcal/journal.h>
00030 #include <libkcal/calendar.h>
00031 #include <libkcal/calendarlocal.h>
00032 #include <libkcal/icalformat.h>
00033 #include <libkcal/freebusy.h>
00034 #include <libkcal/calendarresources.h>
00035 
00036 #include <libemailfunctions/email.h>
00037 
00038 #include <ktnef/ktnefparser.h>
00039 #include <ktnef/ktnefmessage.h>
00040 #include <ktnef/ktnefdefs.h>
00041 #include <tdeabc/phonenumber.h>
00042 #include <tdeabc/vcardconverter.h>
00043 #include <tdeabc/stdaddressbook.h>
00044 
00045 #include <tdeapplication.h>
00046 #include <tdeemailsettings.h>
00047 
00048 #include <tdelocale.h>
00049 #include <tdeglobal.h>
00050 #include <kiconloader.h>
00051 #include <kcalendarsystem.h>
00052 #include <kmimetype.h>
00053 
00054 #include <tqbuffer.h>
00055 #include <tqstylesheet.h>
00056 #include <tqdatetime.h>
00057 #include <tqregexp.h>
00058 
00059 #include <time.h>
00060 
00061 using namespace KCal;
00062 
00063 /*******************
00064  *  General helpers
00065  *******************/
00066 
00067 static TQString htmlAddLink( const TQString &ref, const TQString &text,
00068                              bool newline = true )
00069 {
00070   TQString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
00071   if ( newline ) tmpStr += "\n";
00072   return tmpStr;
00073 }
00074 
00075 static TQString htmlAddTag( const TQString & tag, const TQString & text )
00076 {
00077   int numLineBreaks = text.contains( "\n" );
00078   TQString str = "<" + tag + ">";
00079   TQString tmpText = text;
00080   TQString tmpStr = str;
00081   if( numLineBreaks >= 0 ) {
00082     if ( numLineBreaks > 0) {
00083       int pos = 0;
00084       TQString tmp;
00085       for( int i = 0; i <= numLineBreaks; i++ ) {
00086         pos = tmpText.find( "\n" );
00087         tmp = tmpText.left( pos );
00088         tmpText = tmpText.right( tmpText.length() - pos - 1 );
00089         tmpStr += tmp + "<br>";
00090       }
00091     } else {
00092       tmpStr += tmpText;
00093     }
00094   }
00095   tmpStr += "</" + tag + ">";
00096   return tmpStr;
00097 }
00098 
00099 static bool iamAttendee( Attendee *attendee )
00100 {
00101   // Check if I'm this attendee
00102 
00103   bool iam = false;
00104   KEMailSettings settings;
00105   TQStringList profiles = settings.profiles();
00106   for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00107     settings.setProfile( *it );
00108     if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
00109       iam = true;
00110       break;
00111     }
00112   }
00113   return iam;
00114 }
00115 
00116 static bool iamOrganizer( Incidence *incidence )
00117 {
00118   // Check if I'm the organizer for this incidence
00119 
00120   if ( !incidence ) {
00121     return false;
00122   }
00123 
00124   bool iam = false;
00125   KEMailSettings settings;
00126   TQStringList profiles = settings.profiles();
00127   for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00128     settings.setProfile( *it );
00129     if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) {
00130       iam = true;
00131       break;
00132     }
00133   }
00134   return iam;
00135 }
00136 
00137 static bool senderIsOrganizer( Incidence *incidence, const TQString &sender )
00138 {
00139   // Check if the specified sender is the organizer
00140 
00141   if ( !incidence || sender.isEmpty() ) {
00142     return true;
00143   }
00144   bool isorg = true;
00145   TQString senderName, senderEmail;
00146   if ( KPIM::getNameAndMail( sender, senderName, senderEmail ) ) {
00147     // for this heuristic, we say the sender is the organizer if either the name or the email match.
00148     if ( incidence->organizer().email() != senderEmail &&
00149          incidence->organizer().name() != senderName ) {
00150       isorg = false;
00151     }
00152   }
00153   return isorg;
00154 }
00155 
00156 static TQString firstAttendeeName( Incidence *incidence, const TQString &defName )
00157 {
00158   TQString name;
00159   if ( !incidence ) {
00160     return name;
00161   }
00162 
00163   Attendee::List attendees = incidence->attendees();
00164   if( attendees.count() > 0 ) {
00165     Attendee *attendee = *attendees.begin();
00166     name = attendee->name();
00167     if ( name.isEmpty() ) {
00168       name = attendee->email();
00169     }
00170     if ( name.isEmpty() ) {
00171       name = defName;
00172     }
00173   }
00174   return name;
00175 }
00176 
00177 /*******************************************************************
00178  *  Helper functions for the extensive display (display viewer)
00179  *******************************************************************/
00180 
00181 static TQString displayViewLinkPerson( const TQString& email, TQString name, TQString uid )
00182 {
00183   // Make the search, if there is an email address to search on,
00184   // and either name or uid is missing
00185   if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00186     TDEABC::AddressBook *add_book = TDEABC::StdAddressBook::self( true );
00187     TDEABC::Addressee::List addressList = add_book->findByEmail( email );
00188     if ( !addressList.isEmpty() ) {
00189       TDEABC::Addressee o = addressList.first();
00190       if ( !o.isEmpty() && addressList.size() < 2 ) {
00191         if ( name.isEmpty() ) {
00192           // No name set, so use the one from the addressbook
00193           name = o.formattedName();
00194         }
00195         uid = o.uid();
00196       } else {
00197         // Email not found in the addressbook. Don't make a link
00198         uid = TQString();
00199       }
00200     }
00201   }
00202 
00203   // Show the attendee
00204   TQString tmpString;
00205   if ( !uid.isEmpty() ) {
00206     // There is a UID, so make a link to the addressbook
00207     if ( name.isEmpty() ) {
00208       // Use the email address for text
00209       tmpString += htmlAddLink( "uid:" + uid, email );
00210     } else {
00211       tmpString += htmlAddLink( "uid:" + uid, name );
00212     }
00213   } else {
00214     // No UID, just show some text
00215     tmpString += ( name.isEmpty() ? email : name );
00216   }
00217 
00218   // Make the mailto link
00219   if ( !email.isEmpty() ) {
00220     KURL mailto;
00221     mailto.setProtocol( "mailto" );
00222     mailto.setPath( email );
00223     const TQString iconPath =
00224       TDEGlobal::iconLoader()->iconPath( "mail-message-new", TDEIcon::Small );
00225     tmpString += "&nbsp;" +
00226                  htmlAddLink( mailto.url(),
00227                               "<img valign=\"top\" src=\"" + iconPath + "\">" );
00228   }
00229 
00230   return tmpString;
00231 }
00232 
00233 static TQString displayViewFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
00234 {
00235   TQString tmpStr;
00236   Attendee::List::ConstIterator it;
00237   Attendee::List attendees = incidence->attendees();
00238 
00239   for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00240     Attendee *a = *it;
00241     if ( a->role() != role ) {
00242       // skip this role
00243       continue;
00244     }
00245     if ( a->email() == incidence->organizer().email() ) {
00246       // skip attendee that is also the organizer
00247       continue;
00248     }
00249     tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid() );
00250     if ( !a->delegator().isEmpty() ) {
00251       tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
00252     }
00253     if ( !a->delegate().isEmpty() ) {
00254       tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
00255     }
00256     tmpStr += "<br>";
00257   }
00258   if ( tmpStr.endsWith( "<br>" ) ) {
00259     tmpStr.truncate( tmpStr.length() - 4 );
00260   }
00261   return tmpStr;
00262 }
00263 
00264 static TQString displayViewFormatAttendees( Incidence *incidence )
00265 {
00266   TQString tmpStr, str;
00267 
00268   // Add organizer link
00269   int attendeeCount = incidence->attendees().count();
00270   if ( attendeeCount > 1 ||
00271        ( attendeeCount == 1 &&
00272          incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
00273     tmpStr += "<tr>";
00274     tmpStr += "<td><b>" + i18n( "Organizer:" ) + "</b></td>";
00275     tmpStr += "<td>" +
00276               displayViewLinkPerson( incidence->organizer().email(),
00277                                      incidence->organizer().name(),
00278                                      TQString() ) +
00279               "</td>";
00280     tmpStr += "</tr>";
00281   }
00282 
00283   // Add "chair"
00284   str = displayViewFormatAttendeeRoleList( incidence, Attendee::Chair );
00285   if ( !str.isEmpty() ) {
00286     tmpStr += "<tr>";
00287     tmpStr += "<td><b>" + i18n( "Chair:" ) + "</b></td>";
00288     tmpStr += "<td>" + str + "</td>";
00289     tmpStr += "</tr>";
00290   }
00291 
00292   // Add required participants
00293   str = displayViewFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
00294   if ( !str.isEmpty() ) {
00295     tmpStr += "<tr>";
00296     tmpStr += "<td><b>" + i18n( "Required Participants:" ) + "</b></td>";
00297     tmpStr += "<td>" + str + "</td>";
00298     tmpStr += "</tr>";
00299   }
00300 
00301   // Add optional participants
00302   str = displayViewFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
00303   if ( !str.isEmpty() ) {
00304     tmpStr += "<tr>";
00305     tmpStr += "<td><b>" + i18n( "Optional Participants:" ) + "</b></td>";
00306     tmpStr += "<td>" + str + "</td>";
00307     tmpStr += "</tr>";
00308   }
00309 
00310   // Add observers
00311   str = displayViewFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
00312   if ( !str.isEmpty() ) {
00313     tmpStr += "<tr>";
00314     tmpStr += "<td><b>" + i18n( "Observers:" ) + "</b></td>";
00315     tmpStr += "<td>" + str + "</td>";
00316     tmpStr += "</tr>";
00317   }
00318 
00319   return tmpStr;
00320 }
00321 
00322 static TQString displayViewFormatAttachments( Incidence *incidence )
00323 {
00324   TQString tmpStr;
00325   Attachment::List as = incidence->attachments();
00326   Attachment::List::ConstIterator it;
00327   uint count = 0;
00328   for( it = as.begin(); it != as.end(); ++it ) {
00329     count++;
00330     if ( (*it)->isUri() ) {
00331       TQString name;
00332       if ( (*it)->uri().startsWith( "kmail:" ) ) {
00333         name = i18n( "Show mail" );
00334       } else {
00335         if ( (*it)->label().isEmpty() ) {
00336           name = (*it)->uri();
00337         } else {
00338           name = (*it)->label();
00339         }
00340       }
00341       tmpStr += htmlAddLink( (*it)->uri(), name );
00342     } else {
00343       tmpStr += htmlAddLink( "ATTACH:" + incidence->uid() + ':' + (*it)->label(),
00344                              (*it)->label(), false );
00345     }
00346     if ( count < as.count() ) {
00347       tmpStr += "<br>";
00348     }
00349   }
00350   return tmpStr;
00351 }
00352 
00353 static TQString displayViewFormatCategories( Incidence *incidence )
00354 {
00355   // We do not use Incidence::categoriesStr() since it does not have whitespace
00356   return incidence->categories().join( ", " );
00357 }
00358 
00359 static TQString displayViewFormatCreationDate( Incidence *incidence )
00360 {
00361   return i18n( "Creation date: %1" ).
00362     arg( IncidenceFormatter::dateTimeToString( incidence->created(), false, true ) );
00363 }
00364 
00365 static TQString displayViewFormatBirthday( Event *event )
00366 {
00367   if ( !event ) {
00368     return  TQString();
00369   }
00370   if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) {
00371     return TQString();
00372   }
00373 
00374   TQString uid = event->customProperty("KABC","UID-1");
00375   TQString name = event->customProperty("KABC","NAME-1");
00376   TQString email= event->customProperty("KABC","EMAIL-1");
00377 
00378   TQString tmpStr = displayViewLinkPerson( email, name, uid );
00379 
00380   if ( event->customProperty( "KABC", "ANNIVERSARY") == "YES" ) {
00381     uid = event->customProperty("KABC","UID-2");
00382     name = event->customProperty("KABC","NAME-2");
00383     email= event->customProperty("KABC","EMAIL-2");
00384     tmpStr += "<br>";
00385     tmpStr += displayViewLinkPerson( email, name, uid );
00386   }
00387 
00388   return tmpStr;
00389 }
00390 
00391 static TQString displayViewFormatHeader( Incidence *incidence )
00392 {
00393   TQString tmpStr = "<table><tr>";
00394 
00395   // show icons
00396   {
00397     tmpStr += "<td>";
00398 
00399     if ( incidence->type() == "Event" ) {
00400       TQString iconPath;
00401       if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00402         if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00403           iconPath =
00404             TDEGlobal::iconLoader()->iconPath( "calendaranniversary", TDEIcon::Small );
00405         } else {
00406           iconPath = TDEGlobal::iconLoader()->iconPath( "calendarbirthday", TDEIcon::Small );
00407         }
00408       } else {
00409         iconPath = TDEGlobal::iconLoader()->iconPath( "appointment", TDEIcon::Small );
00410       }
00411       tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
00412     }
00413     if ( incidence->type() == "Todo" ) {
00414       tmpStr += "<img valign=\"top\" src=\"" +
00415                 TDEGlobal::iconLoader()->iconPath( "todo", TDEIcon::Small ) +
00416                 "\">";
00417     }
00418     if ( incidence->type() == "Journal" ) {
00419       tmpStr += "<img valign=\"top\" src=\"" +
00420                 TDEGlobal::iconLoader()->iconPath( "journal", TDEIcon::Small ) +
00421                 "\">";
00422     }
00423     if ( incidence->isAlarmEnabled() ) {
00424       tmpStr += "<img valign=\"top\" src=\"" +
00425                 TDEGlobal::iconLoader()->iconPath( "bell", TDEIcon::Small ) +
00426                 "\">";
00427     }
00428     if ( incidence->doesRecur() ) {
00429       tmpStr += "<img valign=\"top\" src=\"" +
00430                 TDEGlobal::iconLoader()->iconPath( "recur", TDEIcon::Small ) +
00431                 "\">";
00432     }
00433     if ( incidence->isReadOnly() ) {
00434       tmpStr += "<img valign=\"top\" src=\"" +
00435                 TDEGlobal::iconLoader()->iconPath( "readonlyevent", TDEIcon::Small ) +
00436                 "\">";
00437     }
00438 
00439     tmpStr += "</td>";
00440   }
00441 
00442   tmpStr += "<td>";
00443   tmpStr += "<b><u>" + incidence->summary() + "</u></b>";
00444   tmpStr += "</td>";
00445 
00446   tmpStr += "</tr></table>";
00447 
00448   return tmpStr;
00449 }
00450 
00451 static TQString displayViewFormatEvent( Calendar *calendar, Event *event,
00452                                        const TQDate &date )
00453 {
00454   if ( !event ) {
00455     return TQString();
00456   }
00457 
00458   TQString tmpStr = displayViewFormatHeader( event );
00459 
00460   tmpStr += "<table>";
00461   tmpStr += "<col width=\"25%\"/>";
00462   tmpStr += "<col width=\"75%\"/>";
00463 
00464   if ( calendar ) {
00465     TQString calStr = IncidenceFormatter::resourceString( calendar, event );
00466     if ( !calStr.isEmpty() ) {
00467       tmpStr += "<tr>";
00468       tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00469       tmpStr += "<td>" + calStr + "</td>";
00470       tmpStr += "</tr>";
00471     }
00472   }
00473 
00474   if ( !event->location().isEmpty() ) {
00475     tmpStr += "<tr>";
00476     tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00477     tmpStr += "<td>" + event->location() + "</td>";
00478     tmpStr += "</tr>";
00479   }
00480 
00481   TQDateTime startDt = event->dtStart();
00482   TQDateTime endDt = event->dtEnd();
00483   if ( event->doesRecur() ) {
00484     if ( date.isValid() ) {
00485       TQDateTime dt( date, TQTime( 0, 0, 0 ) );
00486       int diffDays = startDt.daysTo( dt );
00487       dt = dt.addSecs( -1 );
00488       startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() );
00489       if ( event->hasEndDate() ) {
00490         endDt = endDt.addDays( diffDays );
00491         if ( startDt > endDt ) {
00492           startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() );
00493           endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
00494         }
00495       }
00496     }
00497   }
00498 
00499   tmpStr += "<tr>";
00500   if ( event->doesFloat() ) {
00501     if ( event->isMultiDay() ) {
00502       tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00503       tmpStr += "<td>" +
00504                 i18n("<beginDate> - <endDate>","%1 - %2").
00505                 arg( IncidenceFormatter::dateToString( startDt, false ) ).
00506                 arg( IncidenceFormatter::dateToString( endDt, false ) ) +
00507                 "</td>";
00508     } else {
00509       tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00510       tmpStr += "<td>" +
00511                 i18n("date as string","%1").
00512                 arg( IncidenceFormatter::dateToString( startDt, false ) ) +
00513                 "</td>";
00514     }
00515   } else {
00516     if ( event->isMultiDay() ) {
00517       tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00518       tmpStr += "<td>" +
00519                 i18n("<beginDate> - <endDate>","%1 - %2").
00520                 arg( IncidenceFormatter::dateToString( startDt, false ) ).
00521                 arg( IncidenceFormatter::dateToString( endDt, false ) ) +
00522                 "</td>";
00523     } else {
00524       tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00525       tmpStr += "<td>" +
00526                 i18n("date as string","%1").
00527                 arg( IncidenceFormatter::dateToString( startDt, false ) ) +
00528                 "</td>";
00529 
00530       tmpStr += "</tr><tr>";
00531       tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>";
00532       if ( event->hasEndDate() && startDt != endDt ) {
00533         tmpStr += "<td>" +
00534                   i18n("<beginTime> - <endTime>","%1 - %2").
00535                   arg( IncidenceFormatter::timeToString( startDt, true ) ).
00536                   arg( IncidenceFormatter::timeToString( endDt, true ) ) +
00537                   "</td>";
00538       } else {
00539         tmpStr += "<td>" +
00540                   IncidenceFormatter::timeToString( startDt, true ) +
00541                   "</td>";
00542       }
00543     }
00544   }
00545   tmpStr += "</tr>";
00546 
00547   TQString durStr = IncidenceFormatter::durationString( event );
00548   if ( !durStr.isEmpty() ) {
00549     tmpStr += "<tr>";
00550     tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
00551     tmpStr += "<td>" + durStr + "</td>";
00552     tmpStr += "</tr>";
00553   }
00554 
00555   if ( event->doesRecur() ) {
00556     tmpStr += "<tr>";
00557     tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
00558     tmpStr += "<td>" +
00559               IncidenceFormatter::recurrenceString( event ) +
00560               "</td>";
00561     tmpStr += "</tr>";
00562   }
00563 
00564   if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) {
00565     tmpStr += "<tr>";
00566     if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00567       tmpStr += "<td><b>" + i18n( "Anniversary:" ) + "</b></td>";
00568     } else {
00569       tmpStr += "<td><b>" + i18n( "Birthday:" ) + "</b></td>";
00570     }
00571     tmpStr += "<td>" + displayViewFormatBirthday( event ) + "</td>";
00572     tmpStr += "</tr>";
00573     tmpStr += "</table>";
00574     return tmpStr;
00575   }
00576 
00577   if ( !event->description().isEmpty() ) {
00578     tmpStr += "<tr>";
00579     tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00580     tmpStr += "<td>" + event->description() + "</td>";
00581     tmpStr += "</tr>";
00582   }
00583 
00584   // TODO: print comments?
00585 
00586   int reminderCount = event->alarms().count();
00587   if ( reminderCount > 0 && event->isAlarmEnabled() ) {
00588     tmpStr += "<tr>";
00589     tmpStr += "<td><b>" +
00590               i18n( "Reminder:", "%n Reminders:", reminderCount ) +
00591               "</b></td>";
00592     tmpStr += "<td>" + IncidenceFormatter::reminderStringList( event ).join( "<br>" ) + "</td>";
00593     tmpStr += "</tr>";
00594   }
00595 
00596   tmpStr += displayViewFormatAttendees( event );
00597 
00598   int categoryCount = event->categories().count();
00599   if ( categoryCount > 0 ) {
00600     tmpStr += "<tr>";
00601     tmpStr += "<td><b>" +
00602               i18n( "Category:", "%n Categories:", categoryCount ) +
00603               "</b></td>";
00604     tmpStr += "<td>" + displayViewFormatCategories( event ) + "</td>";
00605     tmpStr += "</tr>";
00606   }
00607 
00608   int attachmentCount = event->attachments().count();
00609   if ( attachmentCount > 0 ) {
00610     tmpStr += "<tr>";
00611     tmpStr += "<td><b>" +
00612               i18n( "Attachment:", "%n Attachments:", attachmentCount ) +
00613               "</b></td>";
00614     tmpStr += "<td>" + displayViewFormatAttachments( event ) + "</td>";
00615     tmpStr += "</tr>";
00616   }
00617   tmpStr += "</table>";
00618 
00619   tmpStr += "<em>" + displayViewFormatCreationDate( event ) + "</em>";
00620 
00621   return tmpStr;
00622 }
00623 
00624 static TQString displayViewFormatTodo( Calendar *calendar, Todo *todo,
00625                                       const TQDate &date )
00626 {
00627   if ( !todo ) {
00628     return TQString();
00629   }
00630 
00631   TQString tmpStr = displayViewFormatHeader( todo );
00632 
00633   tmpStr += "<table>";
00634   tmpStr += "<col width=\"25%\"/>";
00635   tmpStr += "<col width=\"75%\"/>";
00636 
00637   if ( calendar ) {
00638     TQString calStr = IncidenceFormatter::resourceString( calendar, todo );
00639     if ( !calStr.isEmpty() ) {
00640       tmpStr += "<tr>";
00641       tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00642       tmpStr += "<td>" + calStr + "</td>";
00643       tmpStr += "</tr>";
00644     }
00645   }
00646 
00647   if ( !todo->location().isEmpty() ) {
00648     tmpStr += "<tr>";
00649     tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00650     tmpStr += "<td>" + todo->location() + "</td>";
00651     tmpStr += "</tr>";
00652   }
00653 
00654   if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
00655     TQDateTime startDt = todo->dtStart();
00656     if ( todo->doesRecur() ) {
00657       if ( date.isValid() ) {
00658         startDt.setDate( date );
00659       }
00660     }
00661     tmpStr += "<tr>";
00662     tmpStr += "<td><b>" + i18n( "Start:" ) + "</b></td>";
00663     tmpStr += "<td>" +
00664               IncidenceFormatter::dateTimeToString( startDt,
00665                                                     todo->doesFloat(), false ) +
00666               "</td>";
00667     tmpStr += "</tr>";
00668   }
00669 
00670   if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
00671     TQDateTime dueDt = todo->dtDue();
00672     if ( todo->doesRecur() ) {
00673       if ( date.isValid() ) {
00674         TQDateTime dt( date, TQTime( 0, 0, 0 ) );
00675         dt = dt.addSecs( -1 );
00676         dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() );
00677       }
00678     }
00679     tmpStr += "<tr>";
00680     tmpStr += "<td><b>" + i18n( "Due:" ) + "</b></td>";
00681     tmpStr += "<td>" +
00682               IncidenceFormatter::dateTimeToString( dueDt,
00683                                                     todo->doesFloat(), false ) +
00684               "</td>";
00685     tmpStr += "</tr>";
00686   }
00687 
00688   TQString durStr = IncidenceFormatter::durationString( todo );
00689   if ( !durStr.isEmpty() ) {
00690     tmpStr += "<tr>";
00691     tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
00692     tmpStr += "<td>" + durStr + "</td>";
00693     tmpStr += "</tr>";
00694   }
00695 
00696   if ( todo->doesRecur() ) {
00697     tmpStr += "<tr>";
00698     tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
00699     tmpStr += "<td>" +
00700               IncidenceFormatter::recurrenceString( todo ) +
00701               "</td>";
00702     tmpStr += "</tr>";
00703   }
00704 
00705   if ( !todo->description().isEmpty() ) {
00706     tmpStr += "<tr>";
00707     tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00708     tmpStr += "<td>" + todo->description() + "</td>";
00709     tmpStr += "</tr>";
00710   }
00711 
00712   // TODO: print comments?
00713 
00714   int reminderCount = todo->alarms().count();
00715   if ( reminderCount > 0 && todo->isAlarmEnabled() ) {
00716     tmpStr += "<tr>";
00717     tmpStr += "<td><b>" +
00718               i18n( "Reminder:", "%n Reminders:", reminderCount ) +
00719               "</b></td>";
00720     tmpStr += "<td>" + IncidenceFormatter::reminderStringList( todo ).join( "<br>" ) + "</td>";
00721     tmpStr += "</tr>";
00722   }
00723 
00724   tmpStr += displayViewFormatAttendees( todo );
00725 
00726   int categoryCount = todo->categories().count();
00727   if ( categoryCount > 0 ) {
00728     tmpStr += "<tr>";
00729     tmpStr += "<td><b>" +
00730               i18n( "Category:", "%n Categories:", categoryCount ) +
00731               "</b></td>";
00732     tmpStr += "<td>" + displayViewFormatCategories( todo ) + "</td>";
00733     tmpStr += "</tr>";
00734   }
00735 
00736   if ( todo->priority() > 0 ) {
00737     tmpStr += "<tr>";
00738     tmpStr += "<td><b>" + i18n( "Priority:" ) + "</b></td>";
00739     tmpStr += "<td>";
00740     tmpStr += TQString::number( todo->priority() );
00741     tmpStr += "</td>";
00742     tmpStr += "</tr>";
00743   }
00744 
00745   tmpStr += "<tr>";
00746   if ( todo->isCompleted() ) {
00747     tmpStr += "<td><b>" + i18n( "Completed:" ) + "</b></td>";
00748     tmpStr += "<td>";
00749     tmpStr += todo->completedStr();
00750   } else {
00751     tmpStr += "<td><b>" + i18n( "Percent Done:" ) + "</b></td>";
00752     tmpStr += "<td>";
00753     tmpStr += i18n( "%1%" ).arg( todo->percentComplete() );
00754   }
00755   tmpStr += "</td>";
00756   tmpStr += "</tr>";
00757 
00758   int attachmentCount = todo->attachments().count();
00759   if ( attachmentCount > 0 ) {
00760     tmpStr += "<tr>";
00761     tmpStr += "<td><b>" +
00762               i18n( "Attachment:", "Attachments:", attachmentCount ) +
00763               "</b></td>";
00764     tmpStr += "<td>" + displayViewFormatAttachments( todo ) + "</td>";
00765     tmpStr += "</tr>";
00766   }
00767 
00768   tmpStr += "</table>";
00769 
00770   tmpStr += "<em>" + displayViewFormatCreationDate( todo ) + "</em>";
00771 
00772   return tmpStr;
00773 }
00774 
00775 static TQString displayViewFormatJournal( Calendar *calendar, Journal *journal )
00776 {
00777   if ( !journal ) {
00778     return TQString();
00779   }
00780 
00781   TQString tmpStr = displayViewFormatHeader( journal );
00782 
00783   tmpStr += "<table>";
00784   tmpStr += "<col width=\"25%\"/>";
00785   tmpStr += "<col width=\"75%\"/>";
00786 
00787   if ( calendar ) {
00788     TQString calStr = IncidenceFormatter::resourceString( calendar, journal );
00789     if ( !calStr.isEmpty() ) {
00790       tmpStr += "<tr>";
00791       tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00792       tmpStr += "<td>" + calStr + "</td>";
00793       tmpStr += "</tr>";
00794     }
00795   }
00796 
00797   tmpStr += "<tr>";
00798   tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00799   tmpStr += "<td>" +
00800             IncidenceFormatter::dateToString( journal->dtStart(), false ) +
00801             "</td>";
00802   tmpStr += "</tr>";
00803 
00804   if ( !journal->description().isEmpty() ) {
00805     tmpStr += "<tr>";
00806     tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00807     tmpStr += "<td>" + journal->description() + "</td>";
00808     tmpStr += "</tr>";
00809   }
00810 
00811   int categoryCount = journal->categories().count();
00812   if ( categoryCount > 0 ) {
00813     tmpStr += "<tr>";
00814     tmpStr += "<td><b>" +
00815               i18n( "Category:", "%n Categories:", categoryCount ) +
00816               "</b></td>";
00817     tmpStr += "<td>" + displayViewFormatCategories( journal ) + "</td>";
00818     tmpStr += "</tr>";
00819   }
00820   tmpStr += "</table>";
00821 
00822   tmpStr += "<em>" + displayViewFormatCreationDate( journal ) + "</em>";
00823 
00824   return tmpStr;
00825 }
00826 
00827 static TQString displayViewFormatFreeBusy( Calendar * /*calendar*/, FreeBusy *fb )
00828 {
00829   if ( !fb ) {
00830     return TQString();
00831   }
00832 
00833   TQString tmpStr = htmlAddTag( "h2",
00834                                htmlAddTag( "b",
00835                                            i18n("Free/Busy information for %1").
00836                                            arg( fb->organizer().fullName() ) ) );
00837 
00838   tmpStr += htmlAddTag( "h4", i18n("Busy times in date range %1 - %2:").
00839                         arg( IncidenceFormatter::dateToString( fb->dtStart(), true ) ).
00840                         arg( IncidenceFormatter::dateToString( fb->dtEnd(), true ) ) );
00841 
00842   TQValueList<Period> periods = fb->busyPeriods();
00843 
00844   TQString text = htmlAddTag( "em", htmlAddTag( "b", i18n("Busy:") ) );
00845   TQValueList<Period>::iterator it;
00846   for ( it = periods.begin(); it != periods.end(); ++it ) {
00847     Period per = *it;
00848     if ( per.hasDuration() ) {
00849       int dur = per.duration().asSeconds();
00850       TQString cont;
00851       if ( dur >= 3600 ) {
00852         cont += i18n("1 hour ", "%n hours ", dur / 3600 );
00853         dur %= 3600;
00854       }
00855       if ( dur >= 60 ) {
00856         cont += i18n("1 minute ", "%n minutes ", dur / 60);
00857         dur %= 60;
00858       }
00859       if ( dur > 0 ) {
00860         cont += i18n("1 second", "%n seconds", dur);
00861       }
00862       text += i18n("startDate for duration", "%1 for %2").
00863               arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ).
00864               arg( cont );
00865       text += "<br>";
00866     } else {
00867       if ( per.start().date() == per.end().date() ) {
00868         text += i18n("date, fromTime - toTime ", "%1, %2 - %3").
00869                 arg( IncidenceFormatter::dateToString( per.start().date(), true ) ).
00870                 arg( IncidenceFormatter::timeToString( per.start(), true ) ).
00871                 arg( IncidenceFormatter::timeToString( per.end(), true ) );
00872       } else {
00873         text += i18n("fromDateTime - toDateTime", "%1 - %2").
00874                 arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ).
00875                 arg( IncidenceFormatter::dateTimeToString( per.end(), false, true ) );
00876       }
00877       text += "<br>";
00878     }
00879   }
00880   tmpStr += htmlAddTag( "p", text );
00881   return tmpStr;
00882 }
00883 
00884 class IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor
00885 {
00886   public:
00887     EventViewerVisitor()
00888       : mCalendar( 0 ), mResult( "" ) {}
00889 
00890     bool act( Calendar *calendar, IncidenceBase *incidence, const TQDate &date )
00891     {
00892       mCalendar = calendar;
00893       mDate = date;
00894       mResult = "";
00895       return incidence->accept( *this );
00896     }
00897     TQString result() const { return mResult; }
00898 
00899   protected:
00900     bool visit( Event *event )
00901     {
00902       mResult = displayViewFormatEvent( mCalendar, event, mDate );
00903       return !mResult.isEmpty();
00904     }
00905     bool visit( Todo *todo )
00906     {
00907       mResult = displayViewFormatTodo( mCalendar, todo, mDate );
00908       return !mResult.isEmpty();
00909     }
00910     bool visit( Journal *journal )
00911     {
00912       mResult = displayViewFormatJournal( mCalendar, journal );
00913       return !mResult.isEmpty();
00914     }
00915     bool visit( FreeBusy *fb )
00916     {
00917       mResult = displayViewFormatFreeBusy( mCalendar, fb );
00918       return !mResult.isEmpty();
00919     }
00920 
00921   protected:
00922     Calendar *mCalendar;
00923     TQDate mDate;
00924     TQString mResult;
00925 };
00926 
00927 TQString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
00928 {
00929   return extensiveDisplayStr( 0, incidence, TQDate() );
00930 }
00931 
00932 TQString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar,
00933                                                  IncidenceBase *incidence,
00934                                                  const TQDate &date )
00935 {
00936   if ( !incidence ) {
00937     return TQString();
00938   }
00939 
00940   EventViewerVisitor v;
00941   if ( v.act( calendar, incidence, date ) ) {
00942     return v.result();
00943   } else {
00944     return TQString();
00945   }
00946 }
00947 
00948 /***********************************************************************
00949  *  Helper functions for the body part formatter of kmail (Invitations)
00950  ***********************************************************************/
00951 
00952 static TQString string2HTML( const TQString& str )
00953 {
00954   return TQStyleSheet::convertFromPlainText(str, TQStyleSheetItem::WhiteSpaceNormal);
00955 }
00956 
00957 static TQString cleanHtml( const TQString &html )
00958 {
00959   TQRegExp rx( "<body[^>]*>(.*)</body>" );
00960   rx.setCaseSensitive( false );
00961   rx.search( html );
00962   TQString body = rx.cap( 1 );
00963 
00964   return TQStyleSheet::escape( body.remove( TQRegExp( "<[^>]*>" ) ).stripWhiteSpace() );
00965 }
00966 
00967 static TQString eventStartTimeStr( Event *event )
00968 {
00969   TQString tmp;
00970   if ( !event->doesFloat() ) {
00971     tmp = i18n( "%1: Start Date, %2: Start Time", "%1 %2" ).
00972           arg( IncidenceFormatter::dateToString( event->dtStart(), true ),
00973                IncidenceFormatter::timeToString( event->dtStart(), true ) );
00974   } else {
00975     tmp = i18n( "%1: Start Date", "%1 (all day)" ).
00976           arg( IncidenceFormatter::dateToString( event->dtStart(), true ) );
00977   }
00978   return tmp;
00979 }
00980 
00981 static TQString eventEndTimeStr( Event *event )
00982 {
00983   TQString tmp;
00984   if ( event->hasEndDate() && event->dtEnd().isValid() ) {
00985     if ( !event->doesFloat() ) {
00986       tmp = i18n( "%1: End Date, %2: End Time", "%1 %2" ).
00987             arg( IncidenceFormatter::dateToString( event->dtEnd(), true ),
00988                  IncidenceFormatter::timeToString( event->dtEnd(), true ) );
00989     } else {
00990       tmp = i18n( "%1: End Date", "%1 (all day)" ).
00991             arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) );
00992     }
00993   }
00994   return tmp;
00995 }
00996 
00997 static TQString invitationRow( const TQString &cell1, const TQString &cell2 )
00998 {
00999   return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
01000 }
01001 
01002 static Attendee *findDelegatedFromMyAttendee( Incidence *incidence )
01003 {
01004   // Return the first attendee that was delegated-from me
01005 
01006   Attendee *attendee = 0;
01007   if ( !incidence ) {
01008     return attendee;
01009   }
01010 
01011   KEMailSettings settings;
01012   TQStringList profiles = settings.profiles();
01013   for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
01014     settings.setProfile( *it );
01015 
01016     TQString delegatorName, delegatorEmail;
01017     Attendee::List attendees = incidence->attendees();
01018     Attendee::List::ConstIterator it2;
01019     for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) {
01020       Attendee *a = *it2;
01021       KPIM::getNameAndMail( a->delegator(), delegatorName, delegatorEmail );
01022       if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) {
01023         attendee = a;
01024         break;
01025       }
01026     }
01027   }
01028   return attendee;
01029 }
01030 
01031 static Attendee *findMyAttendee( Incidence *incidence )
01032 {
01033   // Return the attendee for the incidence that is probably me
01034 
01035   Attendee *attendee = 0;
01036   if ( !incidence ) {
01037     return attendee;
01038   }
01039 
01040   KEMailSettings settings;
01041   TQStringList profiles = settings.profiles();
01042   for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
01043     settings.setProfile( *it );
01044 
01045     Attendee::List attendees = incidence->attendees();
01046     Attendee::List::ConstIterator it2;
01047     for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) {
01048       Attendee *a = *it2;
01049       if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
01050         attendee = a;
01051         break;
01052       }
01053     }
01054   }
01055   return attendee;
01056 }
01057 
01058 static Attendee *findAttendee( Incidence *incidence, const TQString &email )
01059 {
01060   // Search for an attendee by email address
01061 
01062   Attendee *attendee = 0;
01063   if ( !incidence ) {
01064     return attendee;
01065   }
01066 
01067   Attendee::List attendees = incidence->attendees();
01068   Attendee::List::ConstIterator it;
01069   for ( it = attendees.begin(); it != attendees.end(); ++it ) {
01070     Attendee *a = *it;
01071     if ( email == a->email() ) {
01072       attendee = a;
01073       break;
01074     }
01075   }
01076   return attendee;
01077 }
01078 
01079 static bool rsvpRequested( Incidence *incidence )
01080 {
01081   if ( !incidence ) {
01082     return false;
01083   }
01084 
01085   //use a heuristic to determine if a response is requested.
01086 
01087   bool rsvp = true; // better send superfluously than not at all
01088   Attendee::List attendees = incidence->attendees();
01089   Attendee::List::ConstIterator it;
01090   for ( it = attendees.begin(); it != attendees.end(); ++it ) {
01091     if ( it == attendees.begin() ) {
01092       rsvp = (*it)->RSVP(); // use what the first one has
01093     } else {
01094       if ( (*it)->RSVP() != rsvp ) {
01095         rsvp = true; // they differ, default
01096         break;
01097       }
01098     }
01099   }
01100   return rsvp;
01101 }
01102 
01103 static TQString rsvpRequestedStr( bool rsvpRequested, const TQString &role )
01104 {
01105   if ( rsvpRequested ) {
01106     if ( role.isEmpty() ) {
01107       return i18n( "Your response is requested" );
01108     } else {
01109       return i18n( "Your response as <b>%1</b> is requested" ).arg( role );
01110     }
01111   } else {
01112     if ( role.isEmpty() ) {
01113       return i18n( "No response is necessary" );
01114     } else {
01115       return i18n( "No response as <b>%1</b> is necessary" ).arg( role );
01116     }
01117   }
01118 }
01119 
01120 static TQString myStatusStr( Incidence *incidence )
01121 {
01122   TQString ret;
01123   Attendee *a = findMyAttendee( incidence );
01124   if ( a &&
01125        a->status() != Attendee::NeedsAction && a->status() != Attendee::Delegated ) {
01126     ret = i18n( "(<b>Note</b>: the Organizer preset your response to <b>%1</b>)" ).
01127           arg( Attendee::statusName( a->status() ) );
01128   }
01129   return ret;
01130 }
01131 
01132 static TQString invitationPerson( const TQString& email, TQString name, TQString uid )
01133 {
01134   // Make the search, if there is an email address to search on,
01135   // and either name or uid is missing
01136   if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
01137     TDEABC::AddressBook *add_book = TDEABC::StdAddressBook::self( true );
01138     TDEABC::Addressee::List addressList = add_book->findByEmail( email );
01139     if ( !addressList.isEmpty() ) {
01140       TDEABC::Addressee o = addressList.first();
01141       if ( !o.isEmpty() && addressList.size() < 2 ) {
01142         if ( name.isEmpty() ) {
01143           // No name set, so use the one from the addressbook
01144           name = o.formattedName();
01145         }
01146         uid = o.uid();
01147       } else {
01148         // Email not found in the addressbook. Don't make a link
01149         uid = TQString();
01150       }
01151     }
01152   }
01153 
01154   // Show the attendee
01155   TQString tmpString;
01156   if ( !uid.isEmpty() ) {
01157     // There is a UID, so make a link to the addressbook
01158     if ( name.isEmpty() ) {
01159       // Use the email address for text
01160       tmpString += htmlAddLink( "uid:" + uid, email );
01161     } else {
01162       tmpString += htmlAddLink( "uid:" + uid, name );
01163     }
01164   } else {
01165     // No UID, just show some text
01166     tmpString += ( name.isEmpty() ? email : name );
01167   }
01168   tmpString += '\n';
01169 
01170   // Make the mailto link
01171   if ( !email.isEmpty() ) {
01172     KCal::Person person( name, email );
01173     KURL mailto;
01174     mailto.setProtocol( "mailto" );
01175     mailto.setPath( person.fullName() );
01176     const TQString iconPath =
01177       TDEGlobal::iconLoader()->iconPath( "mail-message-new", TDEIcon::Small );
01178     tmpString += "&nbsp;" +
01179                  htmlAddLink( mailto.url(), "<img src=\"" + iconPath + "\">" )
01180 ;
01181   }
01182   tmpString += "\n";
01183 
01184   return tmpString;
01185 }
01186 
01187 static TQString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode )
01188 {
01189   // if description and comment -> use both
01190   // if description, but no comment -> use the desc as the comment (and no desc)
01191   // if comment, but no description -> use the comment and no description
01192 
01193   TQString html;
01194   TQString descr;
01195   TQStringList comments;
01196 
01197   if ( incidence->comments().isEmpty() ) {
01198     if ( !incidence->description().isEmpty() ) {
01199       // use description as comments
01200       if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) {
01201         comments << string2HTML( incidence->description() );
01202       } else {
01203         comments << incidence->description();
01204         if ( noHtmlMode ) {
01205           comments[0] = cleanHtml( comments[0] );
01206         }
01207         comments[0] = htmlAddTag( "p", comments[0] );
01208       }
01209     }
01210     //else desc and comments are empty
01211   } else {
01212     // non-empty comments
01213     TQStringList cl = incidence->comments();
01214     uint i = 0;
01215     for( TQStringList::Iterator it=cl.begin(); it!=cl.end(); ++it ) {
01216       if ( !TQStyleSheet::mightBeRichText( *it ) ) {
01217         comments.append( string2HTML( *it ) );
01218       } else {
01219         if ( noHtmlMode ) {
01220           comments.append( cleanHtml( "<body>" + (*it) + "</body>" ) );
01221         } else {
01222           comments.append( *it );
01223         }
01224       }
01225       i++;
01226     }
01227     if ( !incidence->description().isEmpty() ) {
01228       // use description too
01229       if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) {
01230         descr = string2HTML( incidence->description() );
01231       } else {
01232         descr = incidence->description();
01233         if ( noHtmlMode ) {
01234           descr = cleanHtml( descr );
01235         }
01236         descr = htmlAddTag( "p", descr );
01237       }
01238     }
01239   }
01240 
01241   if( !descr.isEmpty() ) {
01242     html += "<p>";
01243     html += "<table border=\"0\" style=\"margin-top:4px;\">";
01244     html += "<tr><td><center>" +
01245             htmlAddTag( "u", i18n( "Description:" ) ) +
01246             "</center></td></tr>";
01247     html += "<tr><td>" + descr + "</td></tr>";
01248     html += "</table>";
01249   }
01250 
01251   if ( !comments.isEmpty() ) {
01252     html += "<p>";
01253     html += "<table border=\"0\" style=\"margin-top:4px;\">";
01254     html += "<tr><td><center>" +
01255             htmlAddTag( "u", i18n( "Comments:" ) ) +
01256             "</center></td></tr>";
01257     html += "<tr><td>";
01258     if ( comments.count() > 1 ) {
01259       html += "<ul>";
01260       for ( uint i=0; i < comments.count(); ++i ) {
01261         html += "<li>" + comments[i] + "</li>";
01262       }
01263       html += "</ul>";
01264     } else {
01265       html += comments[0];
01266     }
01267     html += "</td></tr>";
01268     html += "</table>";
01269   }
01270   return html;
01271 }
01272 
01273 static TQString invitationDetailsEvent( Event* event, bool noHtmlMode )
01274 {
01275   // Invitation details are formatted into an HTML table
01276   if ( !event ) {
01277     return TQString();
01278   }
01279 
01280   TQString sSummary = i18n( "Summary unspecified" );
01281   if ( !event->summary().isEmpty() ) {
01282     if ( !TQStyleSheet::mightBeRichText( event->summary() ) ) {
01283       sSummary = TQStyleSheet::escape( event->summary() );
01284     } else {
01285       sSummary = event->summary();
01286       if ( noHtmlMode ) {
01287         sSummary = cleanHtml( sSummary );
01288       }
01289     }
01290   }
01291 
01292   TQString sLocation = i18n( "Location unspecified" );
01293   if ( !event->location().isEmpty() ) {
01294     if ( !TQStyleSheet::mightBeRichText( event->location() ) ) {
01295       sLocation = TQStyleSheet::escape( event->location() );
01296     } else {
01297       sLocation = event->location();
01298       if ( noHtmlMode ) {
01299         sLocation = cleanHtml( sLocation );
01300       }
01301     }
01302   }
01303 
01304   TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
01305   TQString html = TQString("<div dir=\"%1\">\n").arg(dir);
01306 
01307   html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
01308 
01309   // Invitation summary & location rows
01310   html += invitationRow( i18n( "What:" ), sSummary );
01311   html += invitationRow( i18n( "Where:" ), sLocation );
01312 
01313   if (event->doesRecur() == true) {
01314     html += invitationRow( i18n( "First Start Time:" ), eventStartTimeStr( event ) );
01315     html += invitationRow( i18n( "First End Time:" ), eventEndTimeStr( event ) );
01316   }
01317 //   else {
01318     // If a 1 day event
01319     if ( event->dtStart().date() == event->dtEnd().date() ) {
01320       html += invitationRow( i18n( "Date:" ),
01321                              IncidenceFormatter::dateToString( event->dtStart(), false ) );
01322       if ( !event->doesFloat() ) {
01323         html += invitationRow( i18n( "Time:" ),
01324                                IncidenceFormatter::timeToString( event->dtStart(), true ) +
01325                                " - " +
01326                                IncidenceFormatter::timeToString( event->dtEnd(), true ) );
01327       }
01328     } else {
01329       html += invitationRow( i18n( "Starting date of an event", "From:" ),
01330                              IncidenceFormatter::dateToString( event->dtStart(), false ) );
01331       if ( !event->doesFloat() ) {
01332         html += invitationRow( i18n( "Starting time of an event", "At:" ),
01333                                IncidenceFormatter::timeToString( event->dtStart(), true ) );
01334       }
01335       if ( event->hasEndDate() ) {
01336         html += invitationRow( i18n( "Ending date of an event", "To:" ),
01337                                IncidenceFormatter::dateToString( event->dtEnd(), false ) );
01338         if ( !event->doesFloat() ) {
01339           html += invitationRow( i18n( "Starting time of an event", "At:" ),
01340                                  IncidenceFormatter::timeToString( event->dtEnd(), true ) );
01341         }
01342       } else {
01343         html += invitationRow( i18n( "Ending date of an event", "To:" ),
01344                                i18n( "no end date specified" ) );
01345       }
01346     }
01347 //   }
01348 
01349   // Invitation Duration Row
01350   TQString durStr = IncidenceFormatter::durationString( event );
01351   if ( !durStr.isEmpty() ) {
01352     html += invitationRow( i18n( "Duration:" ), durStr );
01353   }
01354 
01355   // Recurrence Information Rows
01356   if ( event->doesRecur() ) {
01357     Recurrence *recur = event->recurrence();
01358     html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) ); 
01359 
01360     DateList exceptions = recur->exDates();
01361     if (exceptions.isEmpty() == false) {
01362       bool isFirstExRow;
01363       isFirstExRow = true;
01364       DateList::ConstIterator ex_iter;
01365       for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) {
01366         if (isFirstExRow == true) {
01367           isFirstExRow = false;
01368           html += invitationRow( i18n("Cancelled on:"), TDEGlobal::locale()->formatDate(* ex_iter ) );
01369         }
01370         else {
01371           html += invitationRow(" ", TDEGlobal::locale()->formatDate(* ex_iter ) );
01372         }
01373       }
01374     }
01375   }
01376 
01377   html += "</table>\n";
01378   html += invitationsDetailsIncidence( event, noHtmlMode );
01379   html += "</div>\n";
01380 
01381   return html;
01382 }
01383 
01384 static TQString invitationDetailsTodo( Todo *todo, bool noHtmlMode )
01385 {
01386   // Task details are formatted into an HTML table
01387   if ( !todo ) {
01388     return TQString();
01389   }
01390 
01391   TQString sSummary = i18n( "Summary unspecified" );
01392   if ( !todo->summary().isEmpty() ) {
01393     if ( !TQStyleSheet::mightBeRichText( todo->summary() ) ) {
01394       sSummary = TQStyleSheet::escape( todo->summary() );
01395     } else {
01396       sSummary = todo->summary();
01397       if ( noHtmlMode ) {
01398         sSummary = cleanHtml( sSummary );
01399       }
01400     }
01401   }
01402 
01403   TQString sLocation = i18n( "Location unspecified" );
01404   if ( !todo->location().isEmpty() ) {
01405     if ( !TQStyleSheet::mightBeRichText( todo->location() ) ) {
01406       sLocation = TQStyleSheet::escape( todo->location() );
01407     } else {
01408       sLocation = todo->location();
01409       if ( noHtmlMode ) {
01410         sLocation = cleanHtml( sLocation );
01411       }
01412     }
01413   }
01414 
01415   TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" );
01416   TQString html = TQString("<div dir=\"%1\">\n").arg(dir);
01417   html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
01418 
01419   // Invitation summary & location rows
01420   html += invitationRow( i18n( "What:" ), sSummary );
01421   html += invitationRow( i18n( "Where:" ), sLocation );
01422 
01423   if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
01424     html += invitationRow( i18n( "Start Date:" ),
01425                            IncidenceFormatter::dateToString( todo->dtStart(), false ) );
01426     if ( !todo->doesFloat() ) {
01427       html += invitationRow( i18n( "Start Time:" ),
01428                              IncidenceFormatter::timeToString( todo->dtStart(), false ) );
01429     }
01430   }
01431   if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
01432     html += invitationRow( i18n( "Due Date:" ),
01433                            IncidenceFormatter::dateToString( todo->dtDue(), false ) );
01434     if ( !todo->doesFloat() ) {
01435       html += invitationRow( i18n( "Due Time:" ),
01436                              IncidenceFormatter::timeToString( todo->dtDue(), false ) );
01437     }
01438 
01439   } else {
01440     html += invitationRow( i18n( "Due Date:" ), i18n( "Due Date: None", "None" ) );
01441   }
01442 
01443   html += "</table></div>\n";
01444   html += invitationsDetailsIncidence( todo, noHtmlMode );
01445 
01446   return html;
01447 }
01448 
01449 static TQString invitationDetailsJournal( Journal *journal, bool noHtmlMode )
01450 {
01451   if ( !journal ) {
01452     return TQString();
01453   }
01454 
01455   TQString sSummary = i18n( "Summary unspecified" );
01456   TQString sDescr = i18n( "Description unspecified" );
01457   if ( ! journal->summary().isEmpty() ) {
01458     sSummary = journal->summary();
01459     if ( noHtmlMode ) {
01460       sSummary = cleanHtml( sSummary );
01461     }
01462   }
01463   if ( ! journal->description().isEmpty() ) {
01464     sDescr = journal->description();
01465     if ( noHtmlMode ) {
01466       sDescr = cleanHtml( sDescr );
01467     }
01468   }
01469   TQString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01470   html += invitationRow( i18n( "Summary:" ), sSummary );
01471   html += invitationRow( i18n( "Date:" ),
01472                          IncidenceFormatter::dateToString( journal->dtStart(), false ) );
01473   html += invitationRow( i18n( "Description:" ), sDescr );
01474   html += "</table>\n";
01475   html += invitationsDetailsIncidence( journal, noHtmlMode );
01476 
01477   return html;
01478 }
01479 
01480 static TQString invitationDetailsFreeBusy( FreeBusy *fb, bool /*noHtmlMode*/ )
01481 {
01482   if ( !fb )
01483     return TQString();
01484   TQString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01485 
01486   html += invitationRow( i18n("Person:"), fb->organizer().fullName() );
01487   html += invitationRow( i18n("Start date:"),
01488                          IncidenceFormatter::dateToString( fb->dtStart(), true ) );
01489   html += invitationRow( i18n("End date:"),
01490                          TDEGlobal::locale()->formatDate( fb->dtEnd().date(), true ) );
01491   html += "<tr><td colspan=2><hr></td></tr>\n";
01492   html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
01493 
01494   TQValueList<Period> periods = fb->busyPeriods();
01495 
01496   TQValueList<Period>::iterator it;
01497   for ( it = periods.begin(); it != periods.end(); ++it ) {
01498     Period per = *it;
01499     if ( per.hasDuration() ) {
01500       int dur = per.duration().asSeconds();
01501       TQString cont;
01502       if ( dur >= 3600 ) {
01503         cont += i18n("1 hour ", "%n hours ", dur / 3600);
01504         dur %= 3600;
01505       }
01506       if ( dur >= 60 ) {
01507         cont += i18n("1 minute", "%n minutes ", dur / 60);
01508         dur %= 60;
01509       }
01510       if ( dur > 0 ) {
01511         cont += i18n("1 second", "%n seconds", dur);
01512       }
01513       html += invitationRow( TQString(), i18n("startDate for duration", "%1 for %2")
01514           .arg( TDEGlobal::locale()->formatDateTime( per.start(), false ) )
01515           .arg(cont) );
01516     } else {
01517       TQString cont;
01518       if ( per.start().date() == per.end().date() ) {
01519         cont = i18n("date, fromTime - toTime ", "%1, %2 - %3")
01520             .arg( TDEGlobal::locale()->formatDate( per.start().date() ) )
01521             .arg( TDEGlobal::locale()->formatTime( per.start().time() ) )
01522             .arg( TDEGlobal::locale()->formatTime( per.end().time() ) );
01523       } else {
01524         cont = i18n("fromDateTime - toDateTime", "%1 - %2")
01525           .arg( TDEGlobal::locale()->formatDateTime( per.start(), false ) )
01526           .arg( TDEGlobal::locale()->formatDateTime( per.end(), false ) );
01527       }
01528 
01529       html += invitationRow( TQString(), cont );
01530     }
01531   }
01532 
01533   html += "</table>\n";
01534   return html;
01535 }
01536 
01537 static bool replyMeansCounter( Incidence */*incidence*/ )
01538 {
01539   return false;
01554 }
01555 
01556 static TQString invitationHeaderEvent( Event *event, Incidence *existingIncidence,
01557                                       ScheduleMessage *msg, const TQString &sender )
01558 {
01559   if ( !msg || !event )
01560     return TQString();
01561 
01562   switch ( msg->method() ) {
01563   case Scheduler::Publish:
01564     return i18n( "This invitation has been published" );
01565   case Scheduler::Request:
01566     if ( existingIncidence && event->revision() > 0 ) {
01567       return i18n( "This invitation has been updated by the organizer %1" ).
01568         arg( event->organizer().fullName() );
01569     }
01570     if ( iamOrganizer( event ) ) {
01571       return i18n( "I created this invitation" );
01572     } else {
01573       TQString orgStr;
01574       if ( !event->organizer().fullName().isEmpty() ) {
01575         orgStr = event->organizer().fullName();
01576       } else if ( !event->organizer().email().isEmpty() ) {
01577         orgStr = event->organizer().email();
01578       }
01579       if ( senderIsOrganizer( event, sender ) ) {
01580         if ( !orgStr.isEmpty() ) {
01581           return i18n( "You received an invitation from %1" ).arg( orgStr );
01582         } else {
01583           return i18n( "You received an invitation" );
01584         }
01585       } else {
01586         if ( !orgStr.isEmpty() ) {
01587           return i18n( "You received an invitation from %1 as a representative of %2" ).
01588             arg( sender, orgStr );
01589         } else {
01590           return i18n( "You received an invitation from %1 as the organizer's representative" ).
01591             arg( sender );
01592         }
01593       }
01594     }
01595   case Scheduler::Refresh:
01596     return i18n( "This invitation was refreshed" );
01597   case Scheduler::Cancel:
01598     return i18n( "This invitation has been canceled" );
01599   case Scheduler::Add:
01600     return i18n( "Addition to the invitation" );
01601   case Scheduler::Reply:
01602   {
01603     if ( replyMeansCounter( event ) ) {
01604       return i18n( "%1 makes this counter proposal" ).
01605         arg( firstAttendeeName( event, i18n( "Sender" ) ) );
01606     }
01607 
01608     Attendee::List attendees = event->attendees();
01609     if( attendees.count() == 0 ) {
01610       kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01611       return TQString();
01612     }
01613     if( attendees.count() != 1 ) {
01614       kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01615                     << "but is " << attendees.count() << endl;
01616     }
01617     TQString attendeeName = firstAttendeeName( event, i18n( "Sender" ) );
01618 
01619     TQString delegatorName, dummy;
01620     Attendee* attendee = *attendees.begin();
01621     KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
01622     if ( delegatorName.isEmpty() ) {
01623       delegatorName = attendee->delegator();
01624     }
01625 
01626     switch( attendee->status() ) {
01627     case Attendee::NeedsAction:
01628       return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName );
01629     case Attendee::Accepted:
01630       if ( event->revision() > 0 ) {
01631         if ( !sender.isEmpty() ) {
01632           return i18n( "This invitation has been updated by attendee %1" ).arg( sender );
01633         } else {
01634           return i18n( "This invitation has been updated by an attendee" );
01635         }
01636       } else {
01637         if ( delegatorName.isEmpty() ) {
01638           return i18n( "%1 accepts this invitation" ).arg( attendeeName );
01639         } else {
01640           return i18n( "%1 accepts this invitation on behalf of %2" ).
01641             arg( attendeeName ).arg( delegatorName );
01642         }
01643       }
01644     case Attendee::Tentative:
01645       if ( delegatorName.isEmpty() ) {
01646         return i18n( "%1 tentatively accepts this invitation" ).
01647           arg( attendeeName );
01648       } else {
01649         return i18n( "%1 tentatively accepts this invitation on behalf of %2" ).
01650           arg( attendeeName ).arg( delegatorName );
01651       }
01652     case Attendee::Declined:
01653       if ( delegatorName.isEmpty() ) {
01654         return i18n( "%1 declines this invitation" ).arg( attendeeName );
01655       } else {
01656         return i18n( "%1 declines this invitation on behalf of %2" ).
01657           arg( attendeeName ).arg( delegatorName );
01658       }
01659     case Attendee::Delegated: {
01660       TQString delegate, dummy;
01661       KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01662       if ( delegate.isEmpty() ) {
01663         delegate = attendee->delegate();
01664       }
01665       if ( !delegate.isEmpty() ) {
01666         return i18n( "%1 has delegated this invitation to %2" ).
01667           arg( attendeeName ) .arg( delegate );
01668       } else {
01669         return i18n( "%1 has delegated this invitation" ).arg( attendeeName );
01670       }
01671     }
01672     case Attendee::Completed:
01673       return i18n( "This invitation is now completed" );
01674     case Attendee::InProcess:
01675       return i18n( "%1 is still processing the invitation" ).
01676         arg( attendeeName );
01677     default:
01678       return i18n( "Unknown response to this invitation" );
01679     }
01680     break;
01681   }
01682 
01683   case Scheduler::Counter:
01684     return i18n( "%1 makes this counter proposal" ).
01685       arg( firstAttendeeName( event, i18n( "Sender" ) ) );
01686 
01687   case Scheduler::Declinecounter:
01688     return i18n( "%1 declines the counter proposal" ).
01689       arg( firstAttendeeName( event, i18n( "Sender" ) ) );
01690 
01691   case Scheduler::NoMethod:
01692     return i18n("Error: iMIP message with unknown method: '%1'").
01693       arg( msg->method() );
01694   }
01695   return TQString();
01696 }
01697 
01698 static TQString invitationHeaderTodo( Todo *todo, Incidence *existingIncidence,
01699                                      ScheduleMessage *msg, const TQString &sender )
01700 {
01701   if ( !msg || !todo ) {
01702     return TQString();
01703   }
01704 
01705   switch ( msg->method() ) {
01706   case Scheduler::Publish:
01707     return i18n("This task has been published");
01708   case Scheduler::Request:
01709     if ( existingIncidence && todo->revision() > 0 ) {
01710       return i18n( "This task has been updated by the organizer %1" ).
01711         arg( todo->organizer().fullName() );
01712     } else {
01713       if ( iamOrganizer( todo ) ) {
01714         return i18n( "I created this task" );
01715       } else {
01716         TQString orgStr;
01717         if ( !todo->organizer().fullName().isEmpty() ) {
01718           orgStr = todo->organizer().fullName();
01719         } else if ( !todo->organizer().email().isEmpty() ) {
01720           orgStr = todo->organizer().email();
01721         }
01722         if ( senderIsOrganizer( todo, sender ) ) {
01723           if ( !orgStr.isEmpty() ) {
01724             return i18n( "You have been assigned this task by %1" ).arg( orgStr );
01725           } else {
01726             return i18n( "You have been assigned this task" );
01727           }
01728         } else {
01729           if ( !orgStr.isEmpty() ) {
01730             return i18n( "You have been assigned this task by %1 as a representative of %2" ).
01731               arg( sender, orgStr );
01732           } else {
01733             return i18n( "You have been assigned this task by %1 as the organizer's representative" ).
01734               arg( sender );
01735           }
01736         }
01737       }
01738     }
01739   case Scheduler::Refresh:
01740     return i18n( "This task was refreshed" );
01741   case Scheduler::Cancel:
01742     return i18n( "This task was canceled" );
01743   case Scheduler::Add:
01744     return i18n( "Addition to the task" );
01745   case Scheduler::Reply:
01746   {
01747     if ( replyMeansCounter( todo ) ) {
01748       return i18n( "%1 makes this counter proposal" ).
01749         arg( firstAttendeeName( todo, i18n( "Sender" ) ) );
01750     }
01751 
01752     Attendee::List attendees = todo->attendees();
01753     if( attendees.count() == 0 ) {
01754       kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01755       return TQString();
01756     }
01757     if( attendees.count() != 1 ) {
01758       kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01759                     << "but is " << attendees.count() << endl;
01760     }
01761     TQString attendeeName = firstAttendeeName( todo, i18n( "Sender" ) );
01762 
01763     TQString delegatorName, dummy;
01764     Attendee* attendee = *attendees.begin();
01765     KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
01766     if ( delegatorName.isEmpty() ) {
01767       delegatorName = attendee->delegator();
01768     }
01769 
01770     switch( attendee->status() ) {
01771     case Attendee::NeedsAction:
01772       return i18n( "%1 indicates this task assignment still needs some action" ).arg( attendeeName );
01773     case Attendee::Accepted:
01774       if ( todo->revision() > 0 ) {
01775         if ( !sender.isEmpty() ) {
01776           if ( todo->isCompleted() ) {
01777             return i18n( "This task has been completed by assignee %1" ).arg( sender );
01778           } else {
01779             return i18n( "This task has been updated by assignee %1" ).arg( sender );
01780           }
01781         } else {
01782           if ( todo->isCompleted() ) {
01783             return i18n( "This task has been completed by an assignee" );
01784           } else {
01785             return i18n( "This task has been updated by an assignee" );
01786           }
01787         }
01788       } else {
01789         if ( delegatorName.isEmpty() ) {
01790           return i18n( "%1 accepts this task" ).arg( attendeeName );
01791         } else {
01792           return i18n( "%1 accepts this task on behalf of %2" ).
01793             arg( attendeeName ).arg( delegatorName );
01794         }
01795       }
01796     case Attendee::Tentative:
01797       if ( delegatorName.isEmpty() ) {
01798         return i18n( "%1 tentatively accepts this task" ).
01799           arg( attendeeName );
01800       } else {
01801         return i18n( "%1 tentatively accepts this task on behalf of %2" ).
01802           arg( attendeeName ).arg( delegatorName );
01803       }
01804     case Attendee::Declined:
01805       if ( delegatorName.isEmpty() ) {
01806         return i18n( "%1 declines this task" ).arg( attendeeName );
01807       } else {
01808         return i18n( "%1 declines this task on behalf of %2" ).
01809           arg( attendeeName ).arg( delegatorName );
01810       }
01811     case Attendee::Delegated: {
01812       TQString delegate, dummy;
01813       KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01814       if ( delegate.isEmpty() ) {
01815         delegate = attendee->delegate();
01816       }
01817       if ( !delegate.isEmpty() ) {
01818         return i18n( "%1 has delegated this request for the task to %2" ).
01819           arg( attendeeName ).arg( delegate );
01820       } else {
01821         return i18n( "%1 has delegated this request for the task" ).
01822           arg( attendeeName );
01823       }
01824     }
01825     case Attendee::Completed:
01826       return i18n( "The request for this task is now completed" );
01827     case Attendee::InProcess:
01828       return i18n( "%1 is still processing the task" ).
01829         arg( attendeeName );
01830     default:
01831       return i18n( "Unknown response to this task" );
01832     }
01833     break;
01834   }
01835 
01836   case Scheduler::Counter:
01837     return i18n( "%1 makes this counter proposal" ).
01838       arg( firstAttendeeName( todo, i18n( "Sender" ) ) );
01839 
01840   case Scheduler::Declinecounter:
01841     return i18n( "%1 declines the counter proposal" ).
01842       arg( firstAttendeeName( todo, i18n( "Sender" ) ) );
01843 
01844   case Scheduler::NoMethod:
01845     return i18n( "Error: iMIP message with unknown method: '%1'" ).
01846       arg( msg->method() );
01847   }
01848   return TQString();
01849 }
01850 
01851 static TQString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
01852 {
01853   if ( !msg || !journal ) {
01854     return TQString();
01855   }
01856 
01857   switch ( msg->method() ) {
01858   case Scheduler::Publish:
01859     return i18n("This journal has been published");
01860   case Scheduler::Request:
01861     return i18n( "You have been assigned this journal" );
01862   case Scheduler::Refresh:
01863     return i18n( "This journal was refreshed" );
01864   case Scheduler::Cancel:
01865     return i18n( "This journal was canceled" );
01866   case Scheduler::Add:
01867     return i18n( "Addition to the journal" );
01868   case Scheduler::Reply:
01869   {
01870     if ( replyMeansCounter( journal ) ) {
01871       return i18n( "Sender makes this counter proposal" );
01872     }
01873 
01874     Attendee::List attendees = journal->attendees();
01875     if( attendees.count() == 0 ) {
01876       kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01877       return TQString();
01878     }
01879     if( attendees.count() != 1 ) {
01880       kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01881                     << "but is " << attendees.count() << endl;
01882     }
01883     Attendee* attendee = *attendees.begin();
01884 
01885     switch( attendee->status() ) {
01886     case Attendee::NeedsAction:
01887       return i18n( "Sender indicates this journal assignment still needs some action" );
01888     case Attendee::Accepted:
01889       return i18n( "Sender accepts this journal" );
01890     case Attendee::Tentative:
01891       return i18n( "Sender tentatively accepts this journal" );
01892     case Attendee::Declined:
01893       return i18n( "Sender declines this journal" );
01894     case Attendee::Delegated:
01895       return i18n( "Sender has delegated this request for the journal" );
01896     case Attendee::Completed:
01897       return i18n( "The request for this journal is now completed" );
01898     case Attendee::InProcess:
01899       return i18n( "Sender is still processing the invitation" );
01900     default:
01901       return i18n( "Unknown response to this journal" );
01902     }
01903     break;
01904   }
01905   case Scheduler::Counter:
01906     return i18n( "Sender makes this counter proposal" );
01907   case Scheduler::Declinecounter:
01908     return i18n( "Sender declines the counter proposal" );
01909   case Scheduler::NoMethod:
01910     return i18n("Error: iMIP message with unknown method: '%1'").
01911       arg( msg->method() );
01912   }
01913   return TQString();
01914 }
01915 
01916 static TQString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01917 {
01918   if ( !msg || !fb ) {
01919     return TQString();
01920   }
01921 
01922   switch ( msg->method() ) {
01923   case Scheduler::Publish:
01924     return i18n("This free/busy list has been published");
01925   case Scheduler::Request:
01926     return i18n( "The free/busy list has been requested" );
01927   case Scheduler::Refresh:
01928     return i18n( "This free/busy list was refreshed" );
01929   case Scheduler::Cancel:
01930     return i18n( "This free/busy list was canceled" );
01931   case Scheduler::Add:
01932     return i18n( "Addition to the free/busy list" );
01933   case Scheduler::NoMethod:
01934   default:
01935     return i18n("Error: Free/Busy iMIP message with unknown method: '%1'").
01936       arg( msg->method() );
01937   }
01938 }
01939 
01940 static TQString invitationAttendees( Incidence *incidence )
01941 {
01942   TQString tmpStr;
01943   if ( !incidence ) {
01944     return tmpStr;
01945   }
01946 
01947   if ( incidence->type() == "Todo" ) {
01948     tmpStr += htmlAddTag( "u", i18n( "Assignees" ) );
01949   } else {
01950     tmpStr += htmlAddTag( "u", i18n( "Attendees" ) );
01951   }
01952   tmpStr += "<br/>";
01953 
01954   int count=0;
01955   Attendee::List attendees = incidence->attendees();
01956   if ( !attendees.isEmpty() ) {
01957 
01958     Attendee::List::ConstIterator it;
01959     for( it = attendees.begin(); it != attendees.end(); ++it ) {
01960       Attendee *a = *it;
01961       if ( !iamAttendee( a ) ) {
01962         count++;
01963         if ( count == 1 ) {
01964           tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\" columns=\"2\">";
01965         }
01966         tmpStr += "<tr>";
01967         tmpStr += "<td>";
01968         tmpStr += invitationPerson( a->email(), a->name(), TQString() );
01969         if ( !a->delegator().isEmpty() ) {
01970           tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
01971         }
01972         if ( !a->delegate().isEmpty() ) {
01973           tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
01974         }
01975         tmpStr += "</td>";
01976         tmpStr += "<td>" + a->statusStr() + "</td>";
01977         tmpStr += "</tr>";
01978       }
01979     }
01980   }
01981   if ( count ) {
01982     tmpStr += "</table>";
01983   } else {
01984     tmpStr += "<i>" + i18n( "No attendee", "None" ) + "</i>";
01985   }
01986 
01987   return tmpStr;
01988 }
01989 
01990 static TQString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence )
01991 {
01992   TQString tmpStr;
01993   if ( !incidence ) {
01994     return tmpStr;
01995   }
01996 
01997   Attachment::List attachments = incidence->attachments();
01998   if ( !attachments.isEmpty() ) {
01999     tmpStr += i18n( "Attached Documents:" ) + "<ol>";
02000 
02001     Attachment::List::ConstIterator it;
02002     for( it = attachments.begin(); it != attachments.end(); ++it ) {
02003       Attachment *a = *it;
02004       tmpStr += "<li>";
02005       // Attachment icon
02006       KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() );
02007       const TQString iconStr = mimeType ? mimeType->icon( a->uri(), false ) : TQString( "application-octet-stream" );
02008       const TQString iconPath = TDEGlobal::iconLoader()->iconPath( iconStr, TDEIcon::Small );
02009       if ( !iconPath.isEmpty() ) {
02010         tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
02011       }
02012       tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() );
02013       tmpStr += "</li>";
02014     }
02015     tmpStr += "</ol>";
02016   }
02017 
02018   return tmpStr;
02019 }
02020 
02021 class IncidenceFormatter::ScheduleMessageVisitor
02022   : public IncidenceBase::Visitor
02023 {
02024   public:
02025     ScheduleMessageVisitor() : mExistingIncidence( 0 ), mMessage( 0 ) { mResult = ""; }
02026     bool act( IncidenceBase *incidence, Incidence *existingIncidence, ScheduleMessage *msg,
02027               const TQString &sender )
02028     {
02029       mExistingIncidence = existingIncidence;
02030       mMessage = msg;
02031       mSender = sender;
02032       return incidence->accept( *this );
02033     }
02034     TQString result() const { return mResult; }
02035 
02036   protected:
02037     TQString mResult;
02038     Incidence *mExistingIncidence;
02039     ScheduleMessage *mMessage;
02040     TQString mSender;
02041 };
02042 
02043 class IncidenceFormatter::InvitationHeaderVisitor
02044   : public IncidenceFormatter::ScheduleMessageVisitor
02045 {
02046   protected:
02047     bool visit( Event *event )
02048     {
02049       mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender );
02050       return !mResult.isEmpty();
02051     }
02052     bool visit( Todo *todo )
02053     {
02054       mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender );
02055       return !mResult.isEmpty();
02056     }
02057     bool visit( Journal *journal )
02058     {
02059       mResult = invitationHeaderJournal( journal, mMessage );
02060       return !mResult.isEmpty();
02061     }
02062     bool visit( FreeBusy *fb )
02063     {
02064       mResult = invitationHeaderFreeBusy( fb, mMessage );
02065       return !mResult.isEmpty();
02066     }
02067 };
02068 
02069 class IncidenceFormatter::InvitationBodyVisitor
02070   : public IncidenceFormatter::ScheduleMessageVisitor
02071 {
02072   public:
02073     InvitationBodyVisitor( bool noHtmlMode )
02074       : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ) {}
02075 
02076   protected:
02077     bool visit( Event *event )
02078     {
02079       mResult = invitationDetailsEvent( event, mNoHtmlMode );
02080       return !mResult.isEmpty();
02081     }
02082     bool visit( Todo *todo )
02083     {
02084       mResult = invitationDetailsTodo( todo, mNoHtmlMode );
02085       return !mResult.isEmpty();
02086     }
02087     bool visit( Journal *journal )
02088     {
02089       mResult = invitationDetailsJournal( journal, mNoHtmlMode );
02090       return !mResult.isEmpty();
02091     }
02092     bool visit( FreeBusy *fb )
02093     {
02094       mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode );
02095       return !mResult.isEmpty();
02096     }
02097 
02098   private:
02099     bool mNoHtmlMode;
02100 };
02101 
02102 class IncidenceFormatter::IncidenceCompareVisitor
02103   : public IncidenceBase::Visitor
02104 {
02105   public:
02106     IncidenceCompareVisitor() : mExistingIncidence(0) {}
02107     bool act( IncidenceBase *incidence, Incidence *existingIncidence, int method )
02108     {
02109       Incidence *inc = dynamic_cast<Incidence*>( incidence );
02110       if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() )
02111         return false;
02112       mExistingIncidence = existingIncidence;
02113       mMethod = method;
02114       return incidence->accept( *this );
02115     }
02116 
02117     TQString result() const
02118     {
02119       if ( mChanges.isEmpty() ) {
02120         return TQString();
02121       }
02122       TQString html = "<div align=\"left\"><ul><li>";
02123       html += mChanges.join( "</li><li>" );
02124       html += "</li><ul></div>";
02125       return html;
02126     }
02127 
02128   protected:
02129     bool visit( Event *event )
02130     {
02131       compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
02132       compareIncidences( event, mExistingIncidence, mMethod );
02133       return !mChanges.isEmpty();
02134     }
02135     bool visit( Todo *todo )
02136     {
02137       compareTodos( todo, dynamic_cast<Todo*>( mExistingIncidence ) );
02138       compareIncidences( todo, mExistingIncidence, mMethod );
02139       return !mChanges.isEmpty();
02140     }
02141     bool visit( Journal *journal )
02142     {
02143       compareIncidences( journal, mExistingIncidence, mMethod );
02144       return !mChanges.isEmpty();
02145     }
02146     bool visit( FreeBusy *fb )
02147     {
02148       Q_UNUSED( fb );
02149       return !mChanges.isEmpty();
02150     }
02151 
02152   private:
02153     void compareEvents( Event *newEvent, Event *oldEvent )
02154     {
02155       if ( !oldEvent || !newEvent )
02156         return;
02157       if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() )
02158         mChanges += i18n( "The invitation starting time has been changed from %1 to %2" )
02159                     .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) );
02160       if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() )
02161         mChanges += i18n( "The invitation ending time has been changed from %1 to %2" )
02162                     .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) );
02163     }
02164 
02165     void compareTodos( Todo *newTodo, Todo *oldTodo )
02166     {
02167       if ( !oldTodo || !newTodo ) {
02168         return;
02169       }
02170 
02171       if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) {
02172         mChanges += i18n( "The task has been completed" );
02173       }
02174       if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) {
02175         mChanges += i18n( "The task is no longer completed" );
02176       }
02177       if ( oldTodo->percentComplete() != newTodo->percentComplete() ) {
02178         const TQString oldPer = i18n( "%1%" ).arg( oldTodo->percentComplete() );
02179         const TQString newPer = i18n( "%1%" ).arg( newTodo->percentComplete() );
02180         mChanges += i18n( "The task completed percentage has changed from %1 to %2" ).
02181                     arg( oldPer, newPer );
02182       }
02183 
02184       if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) {
02185         mChanges += i18n( "A task starting time has been added" );
02186       }
02187       if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) {
02188         mChanges += i18n( "The task starting time has been removed" );
02189       }
02190       if ( oldTodo->hasStartDate() && newTodo->hasStartDate() &&
02191            oldTodo->dtStart() != newTodo->dtStart() ) {
02192         mChanges += i18n( "The task starting time has been changed from %1 to %2" ).
02193                     arg( dateTimeToString( oldTodo->dtStart(), oldTodo->doesFloat(), false ),
02194                          dateTimeToString( newTodo->dtStart(), newTodo->doesFloat(), false ) );
02195       }
02196 
02197       if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) {
02198         mChanges += i18n( "A task due time has been added" );
02199       }
02200       if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) {
02201         mChanges += i18n( "The task due time has been removed" );
02202       }
02203       if ( oldTodo->hasDueDate() && newTodo->hasDueDate() &&
02204            oldTodo->dtDue() != newTodo->dtDue() ) {
02205         mChanges += i18n( "The task due time has been changed from %1 to %2" ).
02206                     arg( dateTimeToString( oldTodo->dtDue(), oldTodo->doesFloat(), false ),
02207                          dateTimeToString( newTodo->dtDue(), newTodo->doesFloat(), false ) );
02208       }
02209     }
02210 
02211     void compareIncidences( Incidence *newInc, Incidence *oldInc, int method )
02212     {
02213       if ( !oldInc || !newInc )
02214         return;
02215       if ( oldInc->summary() != newInc->summary() )
02216         mChanges += i18n( "The summary has been changed to: \"%1\"" ).arg( newInc->summary() );
02217       if ( oldInc->location() != newInc->location() )
02218         mChanges += i18n( "The location has been changed to: \"%1\"" ).arg( newInc->location() );
02219       if ( oldInc->description() != newInc->description() )
02220         mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() );
02221       Attendee::List oldAttendees = oldInc->attendees();
02222       Attendee::List newAttendees = newInc->attendees();
02223       for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
02224             it != newAttendees.constEnd(); ++it ) {
02225         Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
02226         if ( !oldAtt ) {
02227           mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() );
02228         } else {
02229           if ( oldAtt->status() != (*it)->status() )
02230             mChanges += i18n( "The status of attendee %1 has been changed to: %2" ).
02231                         arg( (*it)->fullName() ).arg( (*it)->statusStr() );
02232         }
02233       }
02234       if ( method == Scheduler::Request ) {
02235         for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
02236               it != oldAttendees.constEnd(); ++it ) {
02237           if ( (*it)->email() != oldInc->organizer().email() ) {
02238             Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
02239             if ( !newAtt ) {
02240               mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() );
02241             }
02242           }
02243         }
02244       }
02245     }
02246 
02247   private:
02248     Incidence *mExistingIncidence;
02249     int mMethod;
02250     TQStringList mChanges;
02251 };
02252 
02253 
02254 TQString InvitationFormatterHelper::makeLink( const TQString &id, const TQString &text )
02255 {
02256   if ( !id.startsWith( "ATTACH:" ) ) {
02257     TQString res = TQString( "<a href=\"%1\"><b>%2</b></a>" ).
02258                   arg( generateLinkURL( id ), text );
02259     return res;
02260   } else {
02261     // draw the attachment links in non-bold face
02262     TQString res = TQString( "<a href=\"%1\">%2</a>" ).
02263                   arg( generateLinkURL( id ), text );
02264     return res;
02265   }
02266 }
02267 
02268 // Check if the given incidence is likely one that we own instead one from
02269 // a shared calendar (Kolab-specific)
02270 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
02271 {
02272   CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
02273   if ( !cal || !incidence ) {
02274     return true;
02275   }
02276   ResourceCalendar *res = cal->resource( incidence );
02277   if ( !res ) {
02278     return true;
02279   }
02280   const TQString subRes = res->subresourceIdentifier( incidence );
02281   if ( !subRes.contains( "/.INBOX.directory/" ) ) {
02282     return false;
02283   }
02284   return true;
02285 }
02286 
02287 // The spacer for the invitation buttons
02288 static TQString spacer = "<td> &nbsp; </td>";
02289 // The open & close table cell tags for the invitation buttons
02290 static TQString tdOpen = "<td>";
02291 static TQString tdClose = "</td>" + spacer;
02292 
02293 static TQString responseButtons( Incidence *inc, bool rsvpReq, bool rsvpRec,
02294                                 InvitationFormatterHelper *helper )
02295 {
02296   TQString html;
02297   if ( !helper ) {
02298     return html;
02299   }
02300 
02301   if ( !rsvpReq && ( inc && inc->revision() == 0 ) ) {
02302     // Record only
02303     html += tdOpen;
02304     html += helper->makeLink( "record", i18n( "[Record]" ) );
02305     html += tdClose;
02306 
02307     // Move to trash
02308     html += tdOpen;
02309     html += helper->makeLink( "delete", i18n( "[Move to Trash]" ) );
02310     html += tdClose;
02311 
02312   } else {
02313 
02314     // Accept
02315     html += tdOpen;
02316     html += helper->makeLink( "accept", i18n( "[Accept]" ) );
02317     html += tdClose;
02318 
02319     // Tentative
02320     html += tdOpen;
02321     html += helper->makeLink( "accept_conditionally",
02322                               i18n( "Accept conditionally", "[Accept cond.]" ) );
02323     html += tdClose;
02324 
02325     // Counter proposal
02326     html += tdOpen;
02327     html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
02328     html += tdClose;
02329 
02330     // Decline
02331     html += tdOpen;
02332     html += helper->makeLink( "decline", i18n( "[Decline]" ) );
02333     html += tdClose;
02334   }
02335 
02336   if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
02337     // Delegate
02338     html += tdOpen;
02339     html += helper->makeLink( "delegate", i18n( "[Delegate]" ) );
02340     html += tdClose;
02341 
02342     // Forward
02343     html += tdOpen;
02344     html += helper->makeLink( "forward", i18n( "[Forward]" ) );
02345     html += tdClose;
02346 
02347     // Check calendar
02348     if ( inc && inc->type() == "Event" ) {
02349       html += tdOpen;
02350       html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
02351       html += tdClose;
02352     }
02353   }
02354   return html;
02355 }
02356 
02357 static TQString counterButtons( Incidence *incidence,
02358                                InvitationFormatterHelper *helper )
02359 {
02360   TQString html;
02361   if ( !helper ) {
02362     return html;
02363   }
02364 
02365   // Accept proposal
02366   html += tdOpen;
02367   html += helper->makeLink( "accept_counter", i18n("[Accept]") );
02368   html += tdClose;
02369 
02370   // Decline proposal
02371   html += tdOpen;
02372   html += helper->makeLink( "decline_counter", i18n("[Decline]") );
02373   html += tdClose;
02374 
02375   // Check calendar
02376   if ( incidence && incidence->type() == "Event" ) {
02377     html += tdOpen;
02378     html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
02379     html += tdClose;
02380   }
02381   return html;
02382 }
02383 
02384 TQString IncidenceFormatter::formatICalInvitationHelper( TQString invitation,
02385                                                         Calendar *mCalendar,
02386                                                         InvitationFormatterHelper *helper,
02387                                                         bool noHtmlMode,
02388                                                         const TQString &sender )
02389 {
02390   if ( invitation.isEmpty() ) {
02391     return TQString();
02392   }
02393 
02394   ICalFormat format;
02395   // parseScheduleMessage takes the tz from the calendar, no need to set it manually here for the format!
02396   ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
02397 
02398   if( !msg ) {
02399     kdDebug( 5850 ) << "Failed to parse the scheduling message" << endl;
02400     Q_ASSERT( format.exception() );
02401     kdDebug( 5850 ) << format.exception()->message() << endl;
02402     return TQString();
02403   }
02404 
02405   IncidenceBase *incBase = msg->event();
02406 
02407   // Determine if this incidence is in my calendar (and owned by me)
02408   Incidence *existingIncidence = 0;
02409   if ( incBase && helper->calendar() ) {
02410     existingIncidence = helper->calendar()->incidence( incBase->uid() );
02411     if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
02412       existingIncidence = 0;
02413     }
02414     if ( !existingIncidence ) {
02415       const Incidence::List list = helper->calendar()->incidences();
02416       for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
02417         if ( (*it)->schedulingID() == incBase->uid() &&
02418              incidenceOwnedByMe( helper->calendar(), *it ) ) {
02419           existingIncidence = *it;
02420           break;
02421         }
02422       }
02423     }
02424   }
02425 
02426   // First make the text of the message
02427   TQString html;
02428 
02429   TQString tableStyle = TQString::fromLatin1(
02430     "style=\"border: solid 1px; margin: 0em;\"" );
02431   TQString tableHead = TQString::fromLatin1(
02432     "<div align=\"center\">"
02433     "<table width=\"80%\" cellpadding=\"1\" cellspacing=\"0\" %1>"
02434     "<tr><td>").arg(tableStyle);
02435 
02436   html += tableHead;
02437   InvitationHeaderVisitor headerVisitor;
02438   // The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled
02439   if ( !headerVisitor.act( incBase, existingIncidence, msg, sender ) )
02440     return TQString();
02441   html += "<b>" + headerVisitor.result() + "</b>";
02442 
02443   InvitationBodyVisitor bodyVisitor( noHtmlMode );
02444   if ( !bodyVisitor.act( incBase, existingIncidence, msg, sender ) )
02445     return TQString();
02446   html += bodyVisitor.result();
02447 
02448   if ( msg->method() == Scheduler::Request ) {
02449     IncidenceCompareVisitor compareVisitor;
02450     if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
02451       html += "<p align=\"left\">";
02452       html += i18n( "The following changes have been made by the organizer:" );
02453       html += "</p>";
02454       html += compareVisitor.result();
02455     }
02456   }
02457   if ( msg->method() == Scheduler::Reply ) {
02458     IncidenceCompareVisitor compareVisitor;
02459     if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
02460       html += "<p align=\"left\">";
02461       if ( !sender.isEmpty() ) {
02462         html += i18n( "The following changes have been made by %1:" ).arg( sender );
02463       } else {
02464         html += i18n( "The following changes have been made by an attendee:" );
02465       }
02466       html += "</p>";
02467       html += compareVisitor.result();
02468     }
02469   }
02470 
02471   Incidence *inc = dynamic_cast<Incidence*>( incBase );
02472 
02473   // determine if I am the organizer for this invitation
02474   bool myInc = iamOrganizer( inc );
02475 
02476   // determine if the invitation response has already been recorded
02477   bool rsvpRec = false;
02478   Attendee *ea = 0;
02479   if ( !myInc ) {
02480     Incidence *rsvpIncidence = existingIncidence;
02481     if ( !rsvpIncidence && inc && inc->revision() > 0 ) {
02482       rsvpIncidence = inc;
02483     }
02484     if ( rsvpIncidence ) {
02485       ea = findMyAttendee( rsvpIncidence );
02486     }
02487     if ( ea &&
02488          ( ea->status() == Attendee::Accepted ||
02489            ea->status() == Attendee::Declined ||
02490            ea->status() == Attendee::Tentative ) ) {
02491       rsvpRec = true;
02492     }
02493   }
02494 
02495   // determine invitation role
02496   TQString role;
02497   bool isDelegated = false;
02498   Attendee *a = findMyAttendee( inc );
02499   if ( !a && inc ) {
02500     if ( !inc->attendees().isEmpty() ) {
02501       a = inc->attendees().first();
02502     }
02503   }
02504   if ( a ) {
02505     isDelegated = ( a->status() == Attendee::Delegated );
02506     role = Attendee::roleName( a->role() );
02507   }
02508 
02509   // determine if RSVP needed, not-needed, or response already recorded
02510   bool rsvpReq = rsvpRequested( inc );
02511   if ( !myInc && a ) {
02512     html += "<br/>";
02513     html += "<i><u>";
02514     if ( rsvpRec && inc ) {
02515       if ( inc->revision() == 0 ) {
02516         html += i18n( "Your <b>%1</b> response has already been recorded" ).
02517                 arg( ea->statusStr() );
02518       } else {
02519         html += i18n( "Your status for this invitation is <b>%1</b>" ).
02520                 arg( ea->statusStr() );
02521       }
02522       rsvpReq = false;
02523     } else if ( msg->method() == Scheduler::Cancel ) {
02524       html += i18n( "This invitation was declined" );
02525     } else if ( msg->method() == Scheduler::Add ) {
02526       html += i18n( "This invitation was accepted" );
02527     } else {
02528       if ( !isDelegated ) {
02529         html += rsvpRequestedStr( rsvpReq, role );
02530       } else {
02531         html += i18n( "Awaiting delegation response" );
02532       }
02533     }
02534     html += "</u></i>";
02535   }
02536 
02537   // Print if the organizer gave you a preset status
02538   if ( !myInc ) {
02539     if ( inc && inc->revision() == 0 ) {
02540       TQString statStr = myStatusStr( inc );
02541       if ( !statStr.isEmpty() ) {
02542         html += "<br/>";
02543         html += "<i>";
02544         html += statStr;
02545         html += "</i>";
02546       }
02547     }
02548   }
02549 
02550   // Add groupware links
02551 
02552   html += "<br><table border=\"0\" cellspacing=\"0\"><tr><td>&nbsp;</td></tr>";
02553 
02554   switch ( msg->method() ) {
02555     case Scheduler::Publish:
02556     case Scheduler::Request:
02557     case Scheduler::Refresh:
02558     case Scheduler::Add:
02559     {
02560       if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
02561         html += "<tr>";
02562         if ( inc->type() == "Todo" ) {
02563           html += "<td colspan=\"9\">";
02564           html += helper->makeLink( "reply", i18n( "[Record invitation in my task list]" ) );
02565         } else {
02566           html += "<td colspan=\"13\">";
02567           html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) );
02568         }
02569         html += "</td></tr>";
02570       }
02571 
02572       if ( !myInc && a ) {
02573         html += "<tr>" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + "</tr>";
02574       }
02575       break;
02576     }
02577 
02578     case Scheduler::Cancel:
02579       // Remove invitation
02580       if ( inc ) {
02581         html += "<tr>";
02582         if ( inc->type() == "Todo" ) {
02583           html += "<td colspan=\"9\">";
02584           html += helper->makeLink( "cancel", i18n( "[Remove invitation from my task list]" ) );
02585         } else {
02586           html += "<td colspan=\"13\">";
02587           html += helper->makeLink( "cancel", i18n( "[Remove invitation from my calendar]" ) );
02588         }
02589         html += "</td></tr>";
02590       }
02591       break;
02592 
02593     case Scheduler::Reply:
02594     {
02595       // Record invitation response
02596       Attendee *a = 0;
02597       Attendee *ea = 0;
02598       if ( inc ) {
02599         // First, determine if this reply is really a counter in disguise.
02600         if ( replyMeansCounter( inc ) ) {
02601           html += "<tr>" + counterButtons( inc, helper ) + "</tr>";
02602           break;
02603         }
02604 
02605         // Next, maybe this is a declined reply that was delegated from me?
02606         // find first attendee who is delegated-from me
02607         // look a their PARTSTAT response, if the response is declined,
02608         // then we need to start over which means putting all the action
02609         // buttons and NOT putting on the [Record response..] button
02610         a = findDelegatedFromMyAttendee( inc );
02611         if ( a ) {
02612           if ( a->status() != Attendee::Accepted ||
02613                a->status() != Attendee::Tentative ) {
02614             html += "<tr>" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + "</tr>";
02615             break;
02616           }
02617         }
02618 
02619         // Finally, simply allow a Record of the reply
02620         if ( !inc->attendees().isEmpty() ) {
02621           a = inc->attendees().first();
02622         }
02623         if ( a ) {
02624           ea = findAttendee( existingIncidence, a->email() );
02625         }
02626       }
02627       if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
02628         if ( inc && inc->revision() > 0 ) {
02629           html += "<br><u><i>";
02630           html += i18n( "The response has been recorded [%1]" ).arg( ea->statusStr() );
02631           html += "</i></u>";
02632         }
02633       } else {
02634         if ( inc ) {
02635           html += "<tr><td>";
02636           if ( inc->type() == "Todo" ) {
02637             html += helper->makeLink( "reply", i18n( "[Record response in my task list]" ) );
02638           } else {
02639             html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) );
02640           }
02641           html += "</td></tr>";
02642         }
02643       }
02644       break;
02645     }
02646 
02647     case Scheduler::Counter:
02648       // Counter proposal
02649       html += "<tr>" + counterButtons( inc, helper ) + "</tr>";
02650       break;
02651 
02652     case Scheduler::Declinecounter:
02653     case Scheduler::NoMethod:
02654       break;
02655   }
02656 
02657   // close the groupware table
02658   html += "</td></tr></table>";
02659 
02660   // Add the attendee list if I am the organizer
02661   if ( myInc && helper->calendar() ) {
02662     html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
02663   }
02664 
02665   // close the top-level table
02666   html += "</td></tr></table><br></div>";
02667 
02668   // Add the attachment list
02669   html += invitationAttachments( helper, inc );
02670 
02671   return html;
02672 }
02673 
02674 TQString IncidenceFormatter::formatICalInvitation( TQString invitation,
02675                                                   Calendar *mCalendar,
02676                                                   InvitationFormatterHelper *helper )
02677 {
02678   return formatICalInvitationHelper( invitation, mCalendar, helper, false, TQString() );
02679 }
02680 
02681 TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation,
02682                                                         Calendar *mCalendar,
02683                                                         InvitationFormatterHelper *helper )
02684 {
02685   return formatICalInvitationHelper( invitation, mCalendar, helper, true, TQString() );
02686 }
02687 
02688 TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation,
02689                                                         Calendar *mCalendar,
02690                                                         InvitationFormatterHelper *helper,
02691                                                         const TQString &sender )
02692 {
02693   return formatICalInvitationHelper( invitation, mCalendar, helper, true, sender );
02694 }
02695 
02696 
02697 /*******************************************************************
02698  *  Helper functions for the msTNEF -> VPart converter
02699  *******************************************************************/
02700 
02701 
02702 //-----------------------------------------------------------------------------
02703 
02704 static TQString stringProp( KTNEFMessage* tnefMsg, const TQ_UINT32& key,
02705                            const TQString& fallback = TQString())
02706 {
02707   return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16,
02708                             fallback );
02709 }
02710 
02711 static TQString sNamedProp( KTNEFMessage* tnefMsg, const TQString& name,
02712                            const TQString& fallback = TQString() )
02713 {
02714   return tnefMsg->findNamedProp( name, fallback );
02715 }
02716 
02717 struct save_tz { char* old_tz; char* tz_env_str; };
02718 
02719 /* temporarily go to a different timezone */
02720 static struct save_tz set_tz( const char* _tc )
02721 {
02722   const char *tc = _tc?_tc:"UTC";
02723 
02724   struct save_tz rv;
02725 
02726   rv.old_tz = 0;
02727   rv.tz_env_str = 0;
02728 
02729   //kdDebug(5006) << "set_tz(), timezone before = " << timezone << endl;
02730 
02731   char* tz_env = 0;
02732   if( getenv( "TZ" ) ) {
02733     tz_env = strdup( getenv( "TZ" ) );
02734     rv.old_tz = tz_env;
02735   }
02736   char* tmp_env = (char*)malloc( strlen( tc ) + 4 );
02737   strcpy( tmp_env, "TZ=" );
02738   strcpy( tmp_env+3, tc );
02739   putenv( tmp_env );
02740 
02741   rv.tz_env_str = tmp_env;
02742 
02743   /* tmp_env is not free'ed -- it is part of the environment */
02744 
02745   tzset();
02746   //kdDebug(5006) << "set_tz(), timezone after = " << timezone << endl;
02747 
02748   return rv;
02749 }
02750 
02751 /* restore previous timezone */
02752 static void unset_tz( struct save_tz old_tz )
02753 {
02754   if( old_tz.old_tz ) {
02755     char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 );
02756     strcpy( tmp_env, "TZ=" );
02757     strcpy( tmp_env+3, old_tz.old_tz );
02758     putenv( tmp_env );
02759     /* tmp_env is not free'ed -- it is part of the environment */
02760     free( old_tz.old_tz );
02761   } else {
02762     /* clear TZ from env */
02763     putenv( strdup("TZ") );
02764   }
02765   tzset();
02766 
02767   /* is this OK? */
02768   if( old_tz.tz_env_str ) free( old_tz.tz_env_str );
02769 }
02770 
02771 static TQDateTime utc2Local( const TQDateTime& utcdt )
02772 {
02773   struct tm tmL;
02774 
02775   save_tz tmp_tz = set_tz("UTC");
02776   time_t utc = utcdt.toTime_t();
02777   unset_tz( tmp_tz );
02778 
02779   localtime_r( &utc, &tmL );
02780   return TQDateTime( TQDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ),
02781                     TQTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) );
02782 }
02783 
02784 
02785 static TQDateTime pureISOToLocalTQDateTime( const TQString& dtStr,
02786                                           bool bDateOnly = false )
02787 {
02788   TQDate tmpDate;
02789   TQTime tmpTime;
02790   int year, month, day, hour, minute, second;
02791 
02792   if( bDateOnly ) {
02793     year = dtStr.left( 4 ).toInt();
02794     month = dtStr.mid( 4, 2 ).toInt();
02795     day = dtStr.mid( 6, 2 ).toInt();
02796     hour = 0;
02797     minute = 0;
02798     second = 0;
02799   } else {
02800     year = dtStr.left( 4 ).toInt();
02801     month = dtStr.mid( 4, 2 ).toInt();
02802     day = dtStr.mid( 6, 2 ).toInt();
02803     hour = dtStr.mid( 9, 2 ).toInt();
02804     minute = dtStr.mid( 11, 2 ).toInt();
02805     second = dtStr.mid( 13, 2 ).toInt();
02806   }
02807   tmpDate.setYMD( year, month, day );
02808   tmpTime.setHMS( hour, minute, second );
02809 
02810   if( tmpDate.isValid() && tmpTime.isValid() ) {
02811     TQDateTime dT = TQDateTime( tmpDate, tmpTime );
02812 
02813     if( !bDateOnly ) {
02814       // correct for GMT ( == Zulu time == UTC )
02815       if (dtStr.at(dtStr.length()-1) == 'Z') {
02816         //dT = dT.addSecs( 60 * KRFCDate::localUTCOffset() );
02817         //localUTCOffset( dT ) );
02818         dT = utc2Local( dT );
02819       }
02820     }
02821     return dT;
02822   } else
02823     return TQDateTime();
02824 }
02825 
02826 
02827 
02828 TQString IncidenceFormatter::msTNEFToVPart( const TQByteArray& tnef )
02829 {
02830   bool bOk = false;
02831 
02832   KTNEFParser parser;
02833   TQBuffer buf( tnef );
02834   CalendarLocal cal ( TQString::fromLatin1( "UTC" ) );
02835   TDEABC::Addressee addressee;
02836   TDEABC::VCardConverter cardConv;
02837   ICalFormat calFormat;
02838   Event* event = new Event();
02839 
02840   if( parser.openDevice( &TQT_TQIODEVICE_OBJECT(buf) ) ) {
02841     KTNEFMessage* tnefMsg = parser.message();
02842     //TQMap<int,KTNEFProperty*> props = parser.message()->properties();
02843 
02844     // Everything depends from property PR_MESSAGE_CLASS
02845     // (this is added by KTNEFParser):
02846     TQString msgClass = tnefMsg->findProp( 0x001A, TQString(), true )
02847       .upper();
02848     if( !msgClass.isEmpty() ) {
02849       // Match the old class names that might be used by Outlook for
02850       // compatibility with Microsoft Mail for Windows for Workgroups 3.1.
02851       bool bCompatClassAppointment = false;
02852       bool bCompatMethodRequest = false;
02853       bool bCompatMethodCancled = false;
02854       bool bCompatMethodAccepted = false;
02855       bool bCompatMethodAcceptedCond = false;
02856       bool bCompatMethodDeclined = false;
02857       if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) {
02858         bCompatClassAppointment = true;
02859         if( msgClass.endsWith( ".MTGREQ" ) )
02860           bCompatMethodRequest = true;
02861         if( msgClass.endsWith( ".MTGCNCL" ) )
02862           bCompatMethodCancled = true;
02863         if( msgClass.endsWith( ".MTGRESPP" ) )
02864           bCompatMethodAccepted = true;
02865         if( msgClass.endsWith( ".MTGRESPA" ) )
02866           bCompatMethodAcceptedCond = true;
02867         if( msgClass.endsWith( ".MTGRESPN" ) )
02868           bCompatMethodDeclined = true;
02869       }
02870       bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" );
02871 
02872       if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) {
02873         // Compose a vCal
02874         bool bIsReply = false;
02875         TQString prodID = "-//Microsoft Corporation//Outlook ";
02876         prodID += tnefMsg->findNamedProp( "0x8554", "9.0" );
02877         prodID += "MIMEDIR/EN\n";
02878         prodID += "VERSION:2.0\n";
02879         calFormat.setApplication( "Outlook", prodID );
02880 
02881         Scheduler::Method method;
02882         if( bCompatMethodRequest )
02883           method = Scheduler::Request;
02884         else if( bCompatMethodCancled )
02885           method = Scheduler::Cancel;
02886         else if( bCompatMethodAccepted || bCompatMethodAcceptedCond ||
02887                  bCompatMethodDeclined ) {
02888           method = Scheduler::Reply;
02889           bIsReply = true;
02890         } else {
02891           // pending(khz): verify whether "0x0c17" is the right tag ???
02892           //
02893           // at the moment we think there are REQUESTS and UPDATES
02894           //
02895           // but WHAT ABOUT REPLIES ???
02896           //
02897           //
02898 
02899           if( tnefMsg->findProp(0x0c17) == "1" )
02900             bIsReply = true;
02901           method = Scheduler::Request;
02902         }
02903 
02905         ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown );
02906 
02907         TQString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) );
02908 
02909         if( !sSenderSearchKeyEmail.isEmpty() ) {
02910           int colon = sSenderSearchKeyEmail.find( ':' );
02911           // May be e.g. "SMTP:KHZ@KDE.ORG"
02912           if( sSenderSearchKeyEmail.find( ':' ) == -1 )
02913             sSenderSearchKeyEmail.remove( 0, colon+1 );
02914         }
02915 
02916         TQString s( tnefMsg->findProp( 0x0e04 ) );
02917         TQStringList attendees = TQStringList::split( ';', s );
02918         if( attendees.count() ) {
02919           for( TQStringList::Iterator it = attendees.begin();
02920                it != attendees.end(); ++it ) {
02921             // Skip all entries that have no '@' since these are
02922             // no mail addresses
02923             if( (*it).find('@') == -1 ) {
02924               s = (*it).stripWhiteSpace();
02925 
02926               Attendee *attendee = new Attendee( s, s, true );
02927               if( bIsReply ) {
02928                 if( bCompatMethodAccepted )
02929                   attendee->setStatus( Attendee::Accepted );
02930                 if( bCompatMethodDeclined )
02931                   attendee->setStatus( Attendee::Declined );
02932                 if( bCompatMethodAcceptedCond )
02933                   attendee->setStatus(Attendee::Tentative);
02934               } else {
02935                 attendee->setStatus( Attendee::NeedsAction );
02936                 attendee->setRole( Attendee::ReqParticipant );
02937               }
02938               event->addAttendee(attendee);
02939             }
02940           }
02941         } else {
02942           // Oops, no attendees?
02943           // This must be old style, let us use the PR_SENDER_SEARCH_KEY.
02944           s = sSenderSearchKeyEmail;
02945           if( !s.isEmpty() ) {
02946             Attendee *attendee = new Attendee( TQString(), TQString(),
02947                                                true );
02948             if( bIsReply ) {
02949               if( bCompatMethodAccepted )
02950                 attendee->setStatus( Attendee::Accepted );
02951               if( bCompatMethodAcceptedCond )
02952                 attendee->setStatus( Attendee::Declined );
02953               if( bCompatMethodDeclined )
02954                 attendee->setStatus( Attendee::Tentative );
02955             } else {
02956               attendee->setStatus(Attendee::NeedsAction);
02957               attendee->setRole(Attendee::ReqParticipant);
02958             }
02959             event->addAttendee(attendee);
02960           }
02961         }
02962         s = tnefMsg->findProp( 0x0c1f ); // look for organizer property
02963         if( s.isEmpty() && !bIsReply )
02964           s = sSenderSearchKeyEmail;
02965         // TODO: Use the common name?
02966         if( !s.isEmpty() )
02967           event->setOrganizer( s );
02968 
02969         s = tnefMsg->findProp( 0x8516 ).replace( TQChar( '-' ), TQString() )
02970           .replace( TQChar( ':' ), TQString() );
02971         event->setDtStart( TQDateTime::fromString( s ) ); // ## Format??
02972 
02973         s = tnefMsg->findProp( 0x8517 ).replace( TQChar( '-' ), TQString() )
02974           .replace( TQChar( ':' ), TQString() );
02975         event->setDtEnd( TQDateTime::fromString( s ) );
02976 
02977         s = tnefMsg->findProp( 0x8208 );
02978         event->setLocation( s );
02979 
02980         // is it OK to set this to OPAQUE always ??
02981         //vPart += "TRANSP:OPAQUE\n"; ###FIXME, portme!
02982         //vPart += "SEQUENCE:0\n";
02983 
02984         // is "0x0023" OK  -  or should we look for "0x0003" ??
02985         s = tnefMsg->findProp( 0x0023 );
02986         event->setUid( s );
02987 
02988         // PENDING(khz): is this value in local timezone? Must it be
02989         // adjusted? Most likely this is a bug in the server or in
02990         // Outlook - we ignore it for now.
02991         s = tnefMsg->findProp( 0x8202 ).replace( TQChar( '-' ), TQString() )
02992           .replace( TQChar( ':' ), TQString() );
02993         // ### libkcal always uses currentDateTime()
02994         // event->setDtStamp(TQDateTime::fromString(s));
02995 
02996         s = tnefMsg->findNamedProp( "Keywords" );
02997         event->setCategories( s );
02998 
02999         s = tnefMsg->findProp( 0x1000 );
03000         event->setDescription( s );
03001 
03002         s = tnefMsg->findProp( 0x0070 );
03003         event->setSummary( s );
03004 
03005         s = tnefMsg->findProp( 0x0026 );
03006         event->setPriority( s.toInt() );
03007 
03008         // is reminder flag set ?
03009         if(!tnefMsg->findProp(0x8503).isEmpty()) {
03010           Alarm *alarm = new Alarm(event);
03011           TQDateTime highNoonTime =
03012             pureISOToLocalTQDateTime( tnefMsg->findProp( 0x8502 )
03013                                      .replace( TQChar( '-' ), "" )
03014                                      .replace( TQChar( ':' ), "" ) );
03015           TQDateTime wakeMeUpTime =
03016             pureISOToLocalTQDateTime( tnefMsg->findProp( 0x8560, "" )
03017                                      .replace( TQChar( '-' ), "" )
03018                                      .replace( TQChar( ':' ), "" ) );
03019           alarm->setTime(wakeMeUpTime);
03020 
03021           if( highNoonTime.isValid() && wakeMeUpTime.isValid() )
03022             alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) );
03023           else
03024             // default: wake them up 15 minutes before the appointment
03025             alarm->setStartOffset( Duration( 15*60 ) );
03026           alarm->setDisplayAlarm( i18n( "Reminder" ) );
03027 
03028           // Sorry: the different action types are not known (yet)
03029           //        so we always set 'DISPLAY' (no sounds, no images...)
03030           event->addAlarm( alarm );
03031         }
03032         cal.addEvent( event );
03033         bOk = true;
03034         // we finished composing a vCal
03035       } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) {
03036         addressee.setUid( stringProp( tnefMsg, attMSGID ) );
03037         addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) );
03038         addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true );
03039         addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false );
03040         addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false );
03041         addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) );
03042         addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) );
03043         addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) );
03044         addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) );
03045         addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) );
03046         addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) );
03047         addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) );
03048 
03049         TQString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY )
03050           .replace( TQChar( '-' ), TQString() )
03051           .replace( TQChar( ':' ), TQString() );
03052         if( !s.isEmpty() )
03053           addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s );
03054 
03055         addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE )  ) );
03056 
03057         // collect parts of Name entry
03058         addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) );
03059         addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) );
03060         addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) );
03061         addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) );
03062         addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) );
03063 
03064         addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) );
03065         addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) );
03066         addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) );
03067         /*
03068         the MAPI property ID of this (multiline) )field is unknown:
03069         vPart += stringProp(tnefMsg, "\n","NOTE", ... , "" );
03070         */
03071 
03072         TDEABC::Address adr;
03073         adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) );
03074         adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) );
03075         adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) );
03076         adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) );
03077         adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) );
03078         adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) );
03079         adr.setType(TDEABC::Address::Home);
03080         addressee.insertAddress(adr);
03081 
03082         adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) );
03083         adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) );
03084         adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) );
03085         adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) );
03086         adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) );
03087         adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) );
03088         adr.setType( TDEABC::Address::Work );
03089         addressee.insertAddress( adr );
03090 
03091         adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) );
03092         adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) );
03093         adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) );
03094         adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) );
03095         adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) );
03096         adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) );
03097         adr.setType( TDEABC::Address::Dom );
03098         addressee.insertAddress(adr);
03099 
03100         // problem: the 'other' address was stored by KOrganizer in
03101         //          a line looking like the following one:
03102         // 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
03103 
03104         TQString nr;
03105         nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER );
03106         addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Home ) );
03107         nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER );
03108         addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Work ) );
03109         nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER );
03110         addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Cell ) );
03111         nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER );
03112         addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Fax | TDEABC::PhoneNumber::Home ) );
03113         nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER );
03114         addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Fax | TDEABC::PhoneNumber::Work ) );
03115 
03116         s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY )
03117           .replace( TQChar( '-' ), TQString() )
03118           .replace( TQChar( ':' ), TQString() );
03119         if( !s.isEmpty() )
03120           addressee.setBirthday( TQDateTime::fromString( s ) );
03121 
03122         bOk = ( !addressee.isEmpty() );
03123       } else if( "IPM.NOTE" == msgClass ) {
03124 
03125       } // else if ... and so on ...
03126     }
03127   }
03128 
03129   // Compose return string
03130   TQString iCal = calFormat.toString( &cal );
03131   if( !iCal.isEmpty() )
03132     // This was an iCal
03133     return iCal;
03134 
03135   // Not an iCal - try a vCard
03136   TDEABC::VCardConverter converter;
03137   return converter.createVCard( addressee );
03138 }
03139 
03140 
03141 TQString IncidenceFormatter::formatTNEFInvitation( const TQByteArray& tnef,
03142         Calendar *mCalendar, InvitationFormatterHelper *helper )
03143 {
03144   TQString vPart = IncidenceFormatter::msTNEFToVPart( tnef );
03145   TQString iCal = IncidenceFormatter::formatICalInvitation( vPart, mCalendar, helper );
03146   if( !iCal.isEmpty() )
03147     return iCal;
03148   return vPart;
03149 }
03150 
03151 
03152 
03153 
03154 /*******************************************************************
03155  *  Helper functions for the Incidence tooltips
03156  *******************************************************************/
03157 
03158 class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
03159 {
03160   public:
03161     ToolTipVisitor()
03162       : mCalendar( 0 ), mRichText( true ), mResult( "" ) {}
03163 
03164     bool act( Calendar *calendar, IncidenceBase *incidence,
03165               const TQDate &date=TQDate(), bool richText=true )
03166     {
03167       mCalendar = calendar;
03168       mDate = date;
03169       mRichText = richText;
03170       mResult = "";
03171       return incidence ? incidence->accept( *this ) : false;
03172     }
03173     TQString result() const { return mResult; }
03174 
03175   protected:
03176     bool visit( Event *event );
03177     bool visit( Todo *todo );
03178     bool visit( Journal *journal );
03179     bool visit( FreeBusy *fb );
03180 
03181     TQString dateRangeText( Event *event, const TQDate &date );
03182     TQString dateRangeText( Todo *todo, const TQDate &date );
03183     TQString dateRangeText( Journal *journal );
03184     TQString dateRangeText( FreeBusy *fb );
03185 
03186     TQString generateToolTip( Incidence* incidence, TQString dtRangeText );
03187 
03188   protected:
03189     Calendar *mCalendar;
03190     TQDate mDate;
03191     bool mRichText;
03192     TQString mResult;
03193 };
03194 
03195 TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const TQDate &date )
03196 {
03197   TQString ret;
03198   TQString tmp;
03199 
03200   TQDateTime startDt = event->dtStart();
03201   TQDateTime endDt = event->dtEnd();
03202   if ( event->doesRecur() ) {
03203     if ( date.isValid() ) {
03204       TQDateTime dt( date, TQTime( 0, 0, 0 ) );
03205       int diffDays = startDt.daysTo( dt );
03206       dt = dt.addSecs( -1 );
03207       startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() );
03208       if ( event->hasEndDate() ) {
03209         endDt = endDt.addDays( diffDays );
03210         if ( startDt > endDt ) {
03211           startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() );
03212           endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
03213         }
03214       }
03215     }
03216   }
03217   if ( event->isMultiDay() ) {
03218 
03219     tmp = "<br>" + i18n("Event start", "<i>From:</i>&nbsp;%1");
03220     if (event->doesFloat())
03221       ret += tmp.arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", "&nbsp;") );
03222     else
03223       ret += tmp.arg( IncidenceFormatter::dateToString( startDt ).replace(" ", "&nbsp;") );
03224 
03225     tmp = "<br>" + i18n("Event end","<i>To:</i>&nbsp;%1");
03226     if (event->doesFloat())
03227       ret += tmp.arg( IncidenceFormatter::dateToString( endDt, false ).replace(" ", "&nbsp;") );
03228     else
03229       ret += tmp.arg( IncidenceFormatter::dateToString( endDt ).replace(" ", "&nbsp;") );
03230 
03231   } else {
03232 
03233     ret += "<br>"+i18n("<i>Date:</i>&nbsp;%1").
03234            arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", "&nbsp;") );
03235     if ( !event->doesFloat() ) {
03236       const TQString dtStartTime =
03237         IncidenceFormatter::timeToString( startDt, true ).replace( " ", "&nbsp;" );
03238       const TQString dtEndTime =
03239         IncidenceFormatter::timeToString( endDt, true ).replace( " ", "&nbsp;" );
03240       if ( dtStartTime == dtEndTime ) { // to prevent 'Time: 17:00 - 17:00'
03241         tmp = "<br>" + i18n("time for event, &nbsp; to prevent ugly line breaks",
03242         "<i>Time:</i>&nbsp;%1").
03243         arg( dtStartTime );
03244       } else {
03245         tmp = "<br>" + i18n("time range for event, &nbsp; to prevent ugly line breaks",
03246         "<i>Time:</i>&nbsp;%1&nbsp;-&nbsp;%2").
03247         arg( dtStartTime, dtEndTime );
03248       }
03249       ret += tmp;
03250     }
03251 
03252   }
03253   return ret;
03254 }
03255 
03256 TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const TQDate &date )
03257 {
03258   TQString ret;
03259   bool floats( todo->doesFloat() );
03260 
03261   if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
03262     TQDateTime startDt = todo->dtStart();
03263     if ( todo->doesRecur() ) {
03264       if ( date.isValid() ) {
03265         startDt.setDate( date );
03266       }
03267     }
03268     ret += "<br>" +
03269            i18n("<i>Start:</i>&nbsp;%1").
03270            arg( IncidenceFormatter::dateTimeToString( startDt, floats, false ).
03271                 replace( " ", "&nbsp;" ) );
03272   }
03273 
03274   if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
03275     TQDateTime dueDt = todo->dtDue();
03276     if ( todo->doesRecur() ) {
03277       if ( date.isValid() ) {
03278         TQDateTime dt( date, TQTime( 0, 0, 0 ) );
03279         dt = dt.addSecs( -1 );
03280         dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() );
03281       }
03282     }
03283     ret += "<br>" +
03284            i18n("<i>Due:</i>&nbsp;%1").
03285            arg( IncidenceFormatter::dateTimeToString( dueDt, floats, false ).
03286                 replace( " ", "&nbsp;" ) );
03287   }
03288 
03289   // Print priority and completed info here, for lack of a better place
03290 
03291   if ( todo->priority() > 0 ) {
03292     ret += "<br>";
03293     ret += "<i>" + i18n( "Priority:" ) + "</i>" + "&nbsp;";
03294     ret += TQString::number( todo->priority() );
03295   }
03296 
03297   ret += "<br>";
03298   if ( todo->isCompleted() ) {
03299     ret += "<i>" + i18n( "Completed:" ) + "</i>" + "&nbsp;";
03300     ret += todo->completedStr().replace( " ", "&nbsp;" );
03301   } else {
03302     ret += "<i>" + i18n( "Percent Done:" ) + "</i>" + "&nbsp;";
03303     ret += i18n( "%1%" ).arg( todo->percentComplete() );
03304   }
03305 
03306   return ret;
03307 }
03308 
03309 TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal )
03310 {
03311   TQString ret;
03312   if (journal->dtStart().isValid() ) {
03313     ret += "<br>" +
03314            i18n("<i>Date:</i>&nbsp;%1").
03315            arg( IncidenceFormatter::dateToString( journal->dtStart(), false ) );
03316   }
03317   return ret;
03318 }
03319 
03320 TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
03321 {
03322   TQString tmp( "<br>" + i18n("<i>Period start:</i>&nbsp;%1") );
03323   TQString ret = tmp.arg( TDEGlobal::locale()->formatDateTime( fb->dtStart() ) );
03324   tmp = "<br>" + i18n("<i>Period start:</i>&nbsp;%1");
03325   ret += tmp.arg( TDEGlobal::locale()->formatDateTime( fb->dtEnd() ) );
03326   return ret;
03327 }
03328 
03329 
03330 
03331 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
03332 {
03333   mResult = generateToolTip( event, dateRangeText( event, mDate ) );
03334   return !mResult.isEmpty();
03335 }
03336 
03337 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
03338 {
03339   mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
03340   return !mResult.isEmpty();
03341 }
03342 
03343 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
03344 {
03345   mResult = generateToolTip( journal, dateRangeText( journal ) );
03346   return !mResult.isEmpty();
03347 }
03348 
03349 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
03350 {
03351   mResult = "<qt><b>" + i18n("Free/Busy information for %1")
03352         .arg(fb->organizer().fullName()) + "</b>";
03353   mResult += dateRangeText( fb );
03354   mResult += "</qt>";
03355   return !mResult.isEmpty();
03356 }
03357 
03358 static TQString tooltipPerson( const TQString& email, TQString name )
03359 {
03360   // Make the search, if there is an email address to search on,
03361   // and name is missing
03362   if ( name.isEmpty() && !email.isEmpty() ) {
03363     TDEABC::AddressBook *add_book = TDEABC::StdAddressBook::self( true );
03364     TDEABC::Addressee::List addressList = add_book->findByEmail( email );
03365     if ( !addressList.isEmpty() ) {
03366       TDEABC::Addressee o = addressList.first();
03367       if ( !o.isEmpty() && addressList.size() < 2 ) {
03368         // use the name from the addressbook
03369         name = o.formattedName();
03370       }
03371     }
03372   }
03373 
03374   // Show the attendee
03375   TQString tmpString = ( name.isEmpty() ? email : name );
03376 
03377   return tmpString;
03378 }
03379 
03380 static TQString etc = i18n( "elipsis", "..." );
03381 static TQString tooltipFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
03382 {
03383   int maxNumAtts = 8; // maximum number of people to print per attendee role
03384   TQString sep = i18n( "separator for lists of people names", ", " );
03385   int sepLen = sep.length();
03386 
03387   int i = 0;
03388   TQString tmpStr;
03389   Attendee::List::ConstIterator it;
03390   Attendee::List attendees = incidence->attendees();
03391 
03392   for( it = attendees.begin(); it != attendees.end(); ++it ) {
03393     Attendee *a = *it;
03394     if ( a->role() != role ) {
03395       // skip not this role
03396       continue;
03397     }
03398     if ( a->email() == incidence->organizer().email() ) {
03399       // skip attendee that is also the organizer
03400       continue;
03401     }
03402     if ( i == maxNumAtts ) {
03403       tmpStr += etc;
03404       break;
03405     }
03406     tmpStr += tooltipPerson( a->email(), a->name() );
03407     if ( !a->delegator().isEmpty() ) {
03408       tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
03409     }
03410     if ( !a->delegate().isEmpty() ) {
03411       tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
03412     }
03413     tmpStr += sep;
03414     i++;
03415   }
03416   if ( tmpStr.endsWith( sep ) ) {
03417     tmpStr.truncate( tmpStr.length() - sepLen );
03418   }
03419   return tmpStr;
03420 }
03421 
03422 static TQString tooltipFormatAttendees( Incidence *incidence )
03423 {
03424   TQString tmpStr, str;
03425 
03426   // Add organizer link
03427   int attendeeCount = incidence->attendees().count();
03428   if ( attendeeCount > 1 ||
03429        ( attendeeCount == 1 &&
03430          incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
03431     tmpStr += "<i>" + i18n( "Organizer:" ) + "</i>" + "&nbsp;";
03432     tmpStr += tooltipPerson( incidence->organizer().email(),
03433                              incidence->organizer().name() );
03434   }
03435 
03436   // Add "chair"
03437   str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair );
03438   if ( !str.isEmpty() ) {
03439     tmpStr += "<br><i>" + i18n( "Chair:" ) + "</i>" + "&nbsp;";
03440     tmpStr += str;
03441   }
03442 
03443   // Add required participants
03444   str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
03445   if ( !str.isEmpty() ) {
03446     tmpStr += "<br><i>" + i18n( "Required Participants:" ) + "</i>" + "&nbsp;";
03447     tmpStr += str;
03448   }
03449 
03450   // Add optional participants
03451   str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
03452   if ( !str.isEmpty() ) {
03453     tmpStr += "<br><i>" + i18n( "Optional Participants:" ) + "</i>" + "&nbsp;";
03454     tmpStr += str;
03455   }
03456 
03457   // Add observers
03458   str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
03459   if ( !str.isEmpty() ) {
03460     tmpStr += "<br><i>" + i18n( "Observers:" ) + "</i>" + "&nbsp;";
03461     tmpStr += str;
03462   }
03463 
03464   return tmpStr;
03465 }
03466 
03467 TQString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, TQString dtRangeText )
03468 {
03469   uint maxDescLen = 120; // maximum description chars to print (before elipsis)
03470 
03471   if ( !incidence ) {
03472     return TQString();
03473   }
03474 
03475   TQString tmp = "<qt>";
03476 
03477   // header
03478   tmp += "<b>" + incidence->summary().replace( "\n", "<br>" ) + "</b>";
03479   //NOTE: using <hr> seems to confuse TQt3 tooltips in some cases so use "-----"
03480   tmp += "<br>----------<br>";
03481 
03482   if ( mCalendar ) {
03483     TQString calStr = IncidenceFormatter::resourceString( mCalendar, incidence );
03484     if ( !calStr.isEmpty() ) {
03485       tmp += "<i>" + i18n( "Calendar:" ) + "</i>" + "&nbsp;";
03486       tmp += calStr;
03487     }
03488   }
03489 
03490   tmp += dtRangeText;
03491 
03492   if ( !incidence->location().isEmpty() ) {
03493     tmp += "<br>";
03494     tmp += "<i>" + i18n( "Location:" ) + "</i>" + "&nbsp;";
03495     tmp += incidence->location().replace( "\n", "<br>" );
03496   }
03497 
03498   TQString durStr = IncidenceFormatter::durationString( incidence );
03499   if ( !durStr.isEmpty() ) {
03500     tmp += "<br>";
03501     tmp += "<i>" + i18n( "Duration:" ) + "</i>" + "&nbsp;";
03502     tmp += durStr;
03503   }
03504 
03505   if ( incidence->doesRecur() ) {
03506     tmp += "<br>";
03507     tmp += "<i>" + i18n( "Recurrence:" ) + "</i>" + "&nbsp;";
03508     tmp += IncidenceFormatter::recurrenceString( incidence );
03509   }
03510 
03511   if ( !incidence->description().isEmpty() ) {
03512     TQString desc( incidence->description() );
03513     if ( desc.length() > maxDescLen ) {
03514       desc = desc.left( maxDescLen ) + etc;
03515     }
03516     tmp += "<br>----------<br>";
03517     tmp += "<i>" + i18n( "Description:" ) + "</i>" + "<br>";
03518     tmp += desc.replace( "\n", "<br>" );
03519     tmp += "<br>----------";
03520   }
03521 
03522   int reminderCount = incidence->alarms().count();
03523   if ( reminderCount > 0 && incidence->isAlarmEnabled() ) {
03524     tmp += "<br>";
03525     tmp += "<i>" + i18n( "Reminder:", "%n Reminders:", reminderCount ) + "</i>" + "&nbsp;";
03526     tmp += IncidenceFormatter::reminderStringList( incidence ).join( ", " );
03527   }
03528 
03529   tmp += "<br>";
03530   tmp += tooltipFormatAttendees( incidence );
03531 
03532   int categoryCount = incidence->categories().count();
03533   if ( categoryCount > 0 ) {
03534     tmp += "<br>";
03535     tmp += "<i>" + i18n( "Category:", "%n Categories:", categoryCount ) + "</i>" + "&nbsp;";
03536     tmp += incidence->categories().join( ", " );
03537   }
03538 
03539   tmp += "</qt>";
03540   return tmp;
03541 }
03542 
03543 TQString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText )
03544 {
03545   return toolTipStr( 0, incidence, TQDate(), richText );
03546 }
03547 
03548 TQString IncidenceFormatter::toolTipStr( Calendar *calendar,
03549                                         IncidenceBase *incidence,
03550                                         const TQDate &date,
03551                                         bool richText )
03552 {
03553   ToolTipVisitor v;
03554   if ( v.act( calendar, incidence, date, richText ) ) {
03555     return v.result();
03556   } else {
03557     return TQString();
03558   }
03559 }
03560 
03561 /*******************************************************************
03562  *  Helper functions for the Incidence tooltips
03563  *******************************************************************/
03564 
03565 class IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor
03566 {
03567   public:
03568     MailBodyVisitor() : mResult( "" ) {}
03569 
03570     bool act( IncidenceBase *incidence )
03571     {
03572       mResult = "";
03573       return incidence ? incidence->accept( *this ) : false;
03574     }
03575     TQString result() const { return mResult; }
03576 
03577   protected:
03578     bool visit( Event *event );
03579     bool visit( Todo *todo );
03580     bool visit( Journal *journal );
03581     bool visit( FreeBusy * ) { mResult = i18n("This is a Free Busy Object"); return !mResult.isEmpty(); }
03582   protected:
03583     TQString mResult;
03584 };
03585 
03586 
03587 static TQString mailBodyIncidence( Incidence *incidence )
03588 {
03589   TQString body;
03590   if ( !incidence->summary().isEmpty() ) {
03591     body += i18n("Summary: %1\n").arg( incidence->summary() );
03592   }
03593   if ( !incidence->organizer().isEmpty() ) {
03594     body += i18n("Organizer: %1\n").arg( incidence->organizer().fullName() );
03595   }
03596   if ( !incidence->location().isEmpty() ) {
03597     body += i18n("Location: %1\n").arg( incidence->location() );
03598   }
03599   return body;
03600 }
03601 
03602 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
03603 {
03604   TQString recurrence[]= {i18n("no recurrence", "None"),
03605     i18n("Minutely"), i18n("Hourly"), i18n("Daily"),
03606     i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"),
03607     i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
03608 
03609   mResult = mailBodyIncidence( event );
03610   mResult += i18n("Start Date: %1\n").
03611              arg( IncidenceFormatter::dateToString( event->dtStart(), true ) );
03612   if ( !event->doesFloat() ) {
03613     mResult += i18n("Start Time: %1\n").
03614                arg( IncidenceFormatter::timeToString( event->dtStart(), true ) );
03615   }
03616   if ( event->dtStart() != event->dtEnd() ) {
03617     mResult += i18n("End Date: %1\n").
03618                arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) );
03619   }
03620   if ( !event->doesFloat() ) {
03621     mResult += i18n("End Time: %1\n").
03622                arg( IncidenceFormatter::timeToString( event->dtEnd(), true ) );
03623   }
03624   if ( event->doesRecur() ) {
03625     Recurrence *recur = event->recurrence();
03626     // TODO: Merge these two to one of the form "Recurs every 3 days"
03627     mResult += i18n("Recurs: %1\n")
03628              .arg( recurrence[ recur->recurrenceType() ] );
03629     mResult += i18n("Frequency: %1\n")
03630              .arg( event->recurrence()->frequency() );
03631 
03632     if ( recur->duration() > 0 ) {
03633       mResult += i18n ("Repeats once", "Repeats %n times", recur->duration());
03634       mResult += '\n';
03635     } else {
03636       if ( recur->duration() != -1 ) {
03637 // TODO_Recurrence: What to do with floating
03638         TQString endstr;
03639         if ( event->doesFloat() ) {
03640           endstr = TDEGlobal::locale()->formatDate( recur->endDate() );
03641         } else {
03642           endstr = TDEGlobal::locale()->formatDateTime( recur->endDateTime() );
03643         }
03644         mResult += i18n("Repeat until: %1\n").arg( endstr );
03645       } else {
03646         mResult += i18n("Repeats forever\n");
03647       }
03648     }
03649 
03650     DateList exceptions = recur->exDates();
03651     if (exceptions.isEmpty() == false) {
03652       mResult += i18n("This recurring meeting has been cancelled on the following days:\n");
03653       DateList::ConstIterator ex_iter;
03654       for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) {
03655         mResult += i18n("  %1\n").arg( TDEGlobal::locale()->formatDate(* ex_iter ) );
03656       }
03657     }
03658   }
03659   TQString details = event->description();
03660   if ( !details.isEmpty() ) {
03661     mResult += i18n("Details:\n%1\n").arg( details );
03662   }
03663   return !mResult.isEmpty();
03664 }
03665 
03666 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
03667 {
03668   mResult = mailBodyIncidence( todo );
03669 
03670   if ( todo->hasStartDate() ) {
03671     mResult += i18n("Start Date: %1\n").
03672                arg( IncidenceFormatter::dateToString( todo->dtStart( false ), true ) );
03673     if ( !todo->doesFloat() ) {
03674       mResult += i18n("Start Time: %1\n").
03675                  arg( IncidenceFormatter::timeToString( todo->dtStart( false ),true ) );
03676     }
03677   }
03678   if ( todo->hasDueDate() ) {
03679     mResult += i18n("Due Date: %1\n").
03680                arg( IncidenceFormatter::dateToString( todo->dtDue(), true ) );
03681     if ( !todo->doesFloat() ) {
03682       mResult += i18n("Due Time: %1\n").
03683                  arg( IncidenceFormatter::timeToString( todo->dtDue(), true ) );
03684     }
03685   }
03686   TQString details = todo->description();
03687   if ( !details.isEmpty() ) {
03688     mResult += i18n("Details:\n%1\n").arg( details );
03689   }
03690   return !mResult.isEmpty();
03691 }
03692 
03693 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
03694 {
03695   mResult = mailBodyIncidence( journal );
03696   mResult += i18n("Date: %1\n").
03697              arg( IncidenceFormatter::dateToString( journal->dtStart(), true ) );
03698   if ( !journal->doesFloat() ) {
03699     mResult += i18n("Time: %1\n").
03700                arg( IncidenceFormatter::timeToString( journal->dtStart(), true ) );
03701   }
03702   if ( !journal->description().isEmpty() )
03703     mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() );
03704   return !mResult.isEmpty();
03705 }
03706 
03707 
03708 
03709 TQString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
03710 {
03711   if ( !incidence )
03712     return TQString();
03713 
03714   MailBodyVisitor v;
03715   if ( v.act( incidence ) ) {
03716     return v.result();
03717   }
03718   return TQString();
03719 }
03720 
03721 static TQString recurEnd( Incidence *incidence )
03722 {
03723   TQString endstr;
03724   if ( incidence->doesFloat() ) {
03725     endstr = TDEGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
03726   } else {
03727     endstr = TDEGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
03728   }
03729   return endstr;
03730 }
03731 
03732 /************************************
03733  *  More static formatting functions
03734  ************************************/
03735 TQString IncidenceFormatter::recurrenceString( Incidence *incidence )
03736 {
03737   if ( !incidence->doesRecur() ) {
03738     return i18n( "No recurrence" );
03739   }
03740   TQStringList dayList;
03741   dayList.append( i18n( "31st Last" ) );
03742   dayList.append( i18n( "30th Last" ) );
03743   dayList.append( i18n( "29th Last" ) );
03744   dayList.append( i18n( "28th Last" ) );
03745   dayList.append( i18n( "27th Last" ) );
03746   dayList.append( i18n( "26th Last" ) );
03747   dayList.append( i18n( "25th Last" ) );
03748   dayList.append( i18n( "24th Last" ) );
03749   dayList.append( i18n( "23rd Last" ) );
03750   dayList.append( i18n( "22nd Last" ) );
03751   dayList.append( i18n( "21st Last" ) );
03752   dayList.append( i18n( "20th Last" ) );
03753   dayList.append( i18n( "19th Last" ) );
03754   dayList.append( i18n( "18th Last" ) );
03755   dayList.append( i18n( "17th Last" ) );
03756   dayList.append( i18n( "16th Last" ) );
03757   dayList.append( i18n( "15th Last" ) );
03758   dayList.append( i18n( "14th Last" ) );
03759   dayList.append( i18n( "13th Last" ) );
03760   dayList.append( i18n( "12th Last" ) );
03761   dayList.append( i18n( "11th Last" ) );
03762   dayList.append( i18n( "10th Last" ) );
03763   dayList.append( i18n( "9th Last" ) );
03764   dayList.append( i18n( "8th Last" ) );
03765   dayList.append( i18n( "7th Last" ) );
03766   dayList.append( i18n( "6th Last" ) );
03767   dayList.append( i18n( "5th Last" ) );
03768   dayList.append( i18n( "4th Last" ) );
03769   dayList.append( i18n( "3rd Last" ) );
03770   dayList.append( i18n( "2nd Last" ) );
03771   dayList.append( i18n( "last day of the month", "Last" ) );
03772   dayList.append( i18n( "unknown day of the month", "unknown" ) ); //#31 - zero offset from UI
03773   dayList.append( i18n( "1st" ) );
03774   dayList.append( i18n( "2nd" ) );
03775   dayList.append( i18n( "3rd" ) );
03776   dayList.append( i18n( "4th" ) );
03777   dayList.append( i18n( "5th" ) );
03778   dayList.append( i18n( "6th" ) );
03779   dayList.append( i18n( "7th" ) );
03780   dayList.append( i18n( "8th" ) );
03781   dayList.append( i18n( "9th" ) );
03782   dayList.append( i18n( "10th" ) );
03783   dayList.append( i18n( "11th" ) );
03784   dayList.append( i18n( "12th" ) );
03785   dayList.append( i18n( "13th" ) );
03786   dayList.append( i18n( "14th" ) );
03787   dayList.append( i18n( "15th" ) );
03788   dayList.append( i18n( "16th" ) );
03789   dayList.append( i18n( "17th" ) );
03790   dayList.append( i18n( "18th" ) );
03791   dayList.append( i18n( "19th" ) );
03792   dayList.append( i18n( "20th" ) );
03793   dayList.append( i18n( "21st" ) );
03794   dayList.append( i18n( "22nd" ) );
03795   dayList.append( i18n( "23rd" ) );
03796   dayList.append( i18n( "24th" ) );
03797   dayList.append( i18n( "25th" ) );
03798   dayList.append( i18n( "26th" ) );
03799   dayList.append( i18n( "27th" ) );
03800   dayList.append( i18n( "28th" ) );
03801   dayList.append( i18n( "29th" ) );
03802   dayList.append( i18n( "30th" ) );
03803   dayList.append( i18n( "31st" ) );
03804   int weekStart = TDEGlobal::locale()->weekStartDay();
03805   TQString dayNames;
03806   TQString recurStr, txt;
03807   const KCalendarSystem *calSys = TDEGlobal::locale()->calendar();
03808   Recurrence *recur = incidence->recurrence();
03809   switch ( recur->recurrenceType() ) {
03810   case Recurrence::rNone:
03811     return i18n( "No recurrence" );
03812 
03813   case Recurrence::rMinutely:
03814     recurStr = i18n( "Recurs every minute", "Recurs every %n minutes", recur->frequency() );
03815     if ( recur->duration() != -1 ) {
03816       txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) );
03817       if ( recur->duration() >  0 ) {
03818         txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03819       }
03820       return txt;
03821     }
03822     return recurStr;
03823 
03824   case Recurrence::rHourly:
03825     recurStr = i18n( "Recurs hourly", "Recurs every %n hours", recur->frequency() );
03826     if ( recur->duration() != -1 ) {
03827       txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) );
03828       if ( recur->duration() >  0 ) {
03829         txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03830       }
03831       return txt;
03832     }
03833     return recurStr;
03834 
03835   case Recurrence::rDaily:
03836     recurStr = i18n( "Recurs daily", "Recurs every %n days", recur->frequency() );
03837     if ( recur->duration() != -1 ) {
03838 
03839       txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) );
03840       if ( recur->duration() >  0 ) {
03841         txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03842       }
03843       return txt;
03844     }
03845     return recurStr;
03846 
03847   case Recurrence::rWeekly:
03848   {
03849     recurStr = i18n( "Recurs weekly", "Recurs every %n weeks", recur->frequency() );
03850 
03851     bool addSpace = false;
03852     for ( int i = 0; i < 7; ++i ) {
03853       if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
03854         if ( addSpace ) {
03855           dayNames.append( i18n( "separator for list of days", ", " ) );
03856         }
03857         dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1, true ) );
03858         addSpace = true;
03859       }
03860     }
03861     if ( dayNames.isEmpty() ) {
03862       dayNames = i18n( "Recurs weekly on no days", "no days" );
03863     }
03864     if ( recur->duration() != -1 ) {
03865       txt = i18n( "%1 on %2 until %3" ).
03866             arg( recurStr ).arg( dayNames ).arg( recurEnd( incidence ) );
03867       if ( recur->duration() >  0 ) {
03868         txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03869       }
03870       return txt;
03871     }
03872     txt = i18n( "%1 on %2" ).arg( recurStr ).arg( dayNames );
03873     return txt;
03874   }
03875   case Recurrence::rMonthlyPos:
03876   {
03877     recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() );
03878 
03879     if ( !recur->monthPositions().isEmpty() ) {
03880       KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0];
03881       if ( recur->duration() != -1 ) {
03882         txt = i18n( "%1 on the %2 %3 until %4" ).
03883               arg( recurStr ).
03884               arg( dayList[rule.pos() + 31] ).
03885               arg( calSys->weekDayName( rule.day(), false ) ).
03886               arg( recurEnd( incidence ) );
03887         if ( recur->duration() >  0 ) {
03888           txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03889         }
03890         return txt;
03891       }
03892       txt = i18n( "%1 on the %2 %3" ).
03893             arg( recurStr ).
03894             arg( dayList[rule.pos() + 31] ).
03895             arg( calSys->weekDayName( rule.day(), false ) );
03896       return txt;
03897     } else {
03898       return recurStr;
03899     }
03900     break;
03901   }
03902   case Recurrence::rMonthlyDay:
03903   {
03904     recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() );
03905 
03906     if ( !recur->monthDays().isEmpty() ) {
03907       int days = recur->monthDays()[0];
03908       if ( recur->duration() != -1 ) {
03909         txt = i18n( "%1 on the %2 day until %3" ).
03910               arg( recurStr ).
03911               arg( dayList[days + 31] ).
03912               arg( recurEnd( incidence ) );
03913         if ( recur->duration() >  0 ) {
03914           txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03915         }
03916         return txt;
03917       }
03918       txt = i18n( "%1 on the %2 day" ).arg( recurStr ).arg( dayList[days + 31] );
03919       return txt;
03920     } else {
03921       return recurStr;
03922     }
03923     break;
03924   }
03925   case Recurrence::rYearlyMonth:
03926   {
03927     recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() );
03928 
03929     if ( recur->duration() != -1 ) {
03930       if ( !recur->yearDates().isEmpty() ) {
03931         txt = i18n( "%1 on %2 %3 until %4" ).
03932               arg( recurStr ).
03933               arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ).
03934               arg( dayList[ recur->yearDates()[0] + 31 ] ).
03935               arg( recurEnd( incidence ) );
03936         if ( recur->duration() >  0 ) {
03937           txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03938         }
03939         return txt;
03940       }
03941     }
03942     if ( !recur->yearDates().isEmpty() ) {
03943       txt = i18n( "%1 on %2 %3" ).
03944             arg( recurStr ).
03945             arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ).
03946             arg( dayList[ recur->yearDates()[0] + 31 ] );
03947       return txt;
03948     } else {
03949       if ( !recur->yearMonths().isEmpty() ) {
03950         txt = i18n( "Recurs yearly on %1 %2" ).
03951               arg( calSys->monthName( recur->yearMonths()[0],
03952                                       recur->startDate().year() ) ).
03953               arg( dayList[ recur->startDate().day() + 31 ] );
03954       } else {
03955         txt = i18n( "Recurs yearly on %1 %2" ).
03956               arg( calSys->monthName( recur->startDate().month(),
03957                                       recur->startDate().year() ) ).
03958               arg( dayList[ recur->startDate().day() + 31 ] );
03959       }
03960       return txt;
03961     }
03962     break;
03963   }
03964   case Recurrence::rYearlyDay:
03965   {
03966     recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() );
03967     if ( !recur->yearDays().isEmpty() ) {
03968       if ( recur->duration() != -1 ) {
03969         txt = i18n( "%1 on day %2 until %3" ).
03970               arg( recurStr ).
03971               arg( recur->yearDays()[0] ).
03972               arg( recurEnd( incidence ) );
03973         if ( recur->duration() >  0 ) {
03974           txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03975         }
03976         return txt;
03977       }
03978       txt = i18n( "%1 on day %2" ).arg( recurStr ).arg( recur->yearDays()[0] );
03979       return txt;
03980     } else {
03981       return recurStr;
03982     }
03983     break;
03984   }
03985   case Recurrence::rYearlyPos:
03986   {
03987     recurStr = i18n( "Every year", "Every %n years", recur->frequency() );
03988     if ( !recur->yearPositions().isEmpty() && !recur->yearMonths().isEmpty() ) {
03989       KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0];
03990       if ( recur->duration() != -1 ) {
03991         txt = i18n( "%1 on the %2 %3 of %4 until %5" ).
03992               arg( recurStr ).
03993               arg( dayList[rule.pos() + 31] ).
03994               arg( calSys->weekDayName( rule.day(), false ) ).
03995               arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ).
03996               arg( recurEnd( incidence ) );
03997       if ( recur->duration() >  0 ) {
03998         txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03999       }
04000       return txt;
04001       }
04002       txt = i18n( "%1 on the %2 %3 of %4" ).
04003             arg( recurStr ).
04004             arg( dayList[rule.pos() + 31] ).
04005             arg( calSys->weekDayName( rule.day(), false ) ).
04006             arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) );
04007       return txt;
04008     } else {
04009       return recurStr;
04010     }
04011    break;
04012   }
04013   }
04014 
04015   return i18n( "Incidence recurs" );
04016 }
04017 
04018 TQString IncidenceFormatter::timeToString( const TQDateTime &date, bool shortfmt )
04019 {
04020   return TDEGlobal::locale()->formatTime( date.time(), !shortfmt );
04021 }
04022 
04023 TQString IncidenceFormatter::dateToString( const TQDateTime &date, bool shortfmt )
04024 {
04025   return
04026     TDEGlobal::locale()->formatDate( date.date(), shortfmt );
04027 }
04028 
04029 TQString IncidenceFormatter::dateTimeToString( const TQDateTime &date,
04030                                               bool allDay, bool shortfmt )
04031 {
04032   if ( allDay ) {
04033     return dateToString( date, shortfmt );
04034   }
04035 
04036   return  TDEGlobal::locale()->formatDateTime( date, shortfmt );
04037 }
04038 
04039 TQString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence )
04040 {
04041   if ( !calendar || !incidence ) {
04042     return TQString();
04043   }
04044 
04045   CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar );
04046   if ( !calendarResource ) {
04047     return TQString();
04048   }
04049 
04050   ResourceCalendar *resourceCalendar = calendarResource->resource( incidence );
04051   if ( resourceCalendar ) {
04052     if ( !resourceCalendar->subresources().isEmpty() ) {
04053       TQString subRes = resourceCalendar->subresourceIdentifier( incidence );
04054       if ( subRes.isEmpty() ) {
04055         return resourceCalendar->resourceName();
04056       } else {
04057         return resourceCalendar->labelForSubresource( subRes );
04058       }
04059     }
04060     return resourceCalendar->resourceName();
04061   }
04062 
04063   return TQString();
04064 }
04065 
04066 static TQString secs2Duration( int secs )
04067 {
04068   TQString tmp;
04069   int days = secs / 86400;
04070   if ( days > 0 ) {
04071     tmp += i18n( "1 day", "%n days", days );
04072     tmp += ' ';
04073     secs -= ( days * 86400 );
04074   }
04075   int hours = secs / 3600;
04076   if ( hours > 0 ) {
04077     tmp += i18n( "1 hour", "%n hours", hours );
04078     tmp += ' ';
04079     secs -= ( hours * 3600 );
04080   }
04081   int mins = secs / 60;
04082   if ( mins > 0 ) {
04083     tmp += i18n( "1 minute", "%n minutes",  mins );
04084   }
04085   return tmp;
04086 }
04087 
04088 TQString IncidenceFormatter::durationString( Incidence *incidence )
04089 {
04090   TQString tmp;
04091   if ( incidence->type() == "Event" ) {
04092     Event *event = static_cast<Event *>( incidence );
04093     if ( event->hasEndDate() ) {
04094       if ( !event->doesFloat() ) {
04095         tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) );
04096       } else {
04097         tmp = i18n( "1 day", "%n days",
04098                     event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 );
04099       }
04100     } else {
04101       tmp = i18n( "forever" );
04102     }
04103   } else if ( incidence->type() == "Todo" ) {
04104     Todo *todo = static_cast<Todo *>( incidence );
04105     if ( todo->hasDueDate() ) {
04106       if ( todo->hasStartDate() ) {
04107         if ( !todo->doesFloat() ) {
04108           tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) );
04109         } else {
04110           tmp = i18n( "1 day", "%n days",
04111                       todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 );
04112         }
04113       }
04114     }
04115   }
04116   return tmp;
04117 }
04118 
04119 TQStringList IncidenceFormatter::reminderStringList( Incidence *incidence, bool shortfmt )
04120 {
04121   //TODO: implement shortfmt=false
04122   Q_UNUSED( shortfmt );
04123 
04124   TQStringList reminderStringList;
04125 
04126   if ( incidence ) {
04127     Alarm::List alarms = incidence->alarms();
04128     Alarm::List::ConstIterator it;
04129     for ( it = alarms.begin(); it != alarms.end(); ++it ) {
04130       Alarm *alarm = *it;
04131       int offset = 0;
04132       TQString remStr, atStr, offsetStr;
04133       if ( alarm->hasTime() ) {
04134         offset = 0;
04135         if ( alarm->time().isValid() ) {
04136           atStr = TDEGlobal::locale()->formatDateTime( alarm->time() );
04137         }
04138       } else if ( alarm->hasStartOffset() ) {
04139         offset = alarm->startOffset().asSeconds();
04140         if ( offset < 0 ) {
04141           offset = -offset;
04142           offsetStr = i18n( "N days/hours/minutes before the start datetime",
04143                             "%1 before the start" );
04144         } else if ( offset > 0 ) {
04145           offsetStr = i18n( "N days/hours/minutes after the start datetime",
04146                             "%1 after the start" );
04147         } else { //offset is 0
04148           if ( incidence->dtStart().isValid() ) {
04149             atStr = TDEGlobal::locale()->formatDateTime( incidence->dtStart() );
04150           }
04151         }
04152       } else if ( alarm->hasEndOffset() ) {
04153         offset = alarm->endOffset().asSeconds();
04154         if ( offset < 0 ) {
04155           offset = -offset;
04156           if ( incidence->type() == "Todo" ) {
04157             offsetStr = i18n( "N days/hours/minutes before the due datetime",
04158                               "%1 before the to-do is due" );
04159           } else {
04160             offsetStr = i18n( "N days/hours/minutes before the end datetime",
04161                               "%1 before the end" );
04162           }
04163         } else if ( offset > 0 ) {
04164           if ( incidence->type() == "Todo" ) {
04165             offsetStr = i18n( "N days/hours/minutes after the due datetime",
04166                               "%1 after the to-do is due" );
04167           } else {
04168             offsetStr = i18n( "N days/hours/minutes after the end datetime",
04169                               "%1 after the end" );
04170           }
04171         } else { //offset is 0
04172           if ( incidence->type() == "Todo" ) {
04173             Todo *t = static_cast<Todo *>( incidence );
04174             if ( t->dtDue().isValid() ) {
04175               atStr = TDEGlobal::locale()->formatDateTime( t->dtDue() );
04176             }
04177           } else {
04178             Event *e = static_cast<Event *>( incidence );
04179             if ( e->dtEnd().isValid() ) {
04180               atStr = TDEGlobal::locale()->formatDateTime( e->dtEnd() );
04181             }
04182           }
04183         }
04184       }
04185       if ( offset == 0 ) {
04186         if ( !atStr.isEmpty() ) {
04187           remStr = i18n( "reminder occurs at datetime", "at %1" ).arg( atStr );
04188         }
04189       } else {
04190         remStr = offsetStr.arg( secs2Duration( offset ) );
04191       }
04192 
04193       if ( alarm->repeatCount() > 0 ) {
04194         TQString countStr = i18n( "repeats once", "repeats %n times", alarm->repeatCount() );
04195         TQString intervalStr = i18n( "interval is N days/hours/minutes", "interval is %1" ).
04196                               arg( secs2Duration( alarm->snoozeTime().asSeconds() ) );
04197         TQString repeatStr = i18n( "(repeat string, interval string)", "(%1, %2)" ).
04198                             arg( countStr, intervalStr );
04199         remStr = remStr + ' ' + repeatStr;
04200 
04201       }
04202       reminderStringList << remStr;
04203     }
04204   }
04205 
04206   return reminderStringList;
04207 }