libkcal

incidenceformatter.cpp

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