00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
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
00101
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
00109 name = o.formattedName();
00110 uid = o.uid();
00111 } else
00112
00113 uid = QString::null;
00114 }
00115 kdDebug(5850) << "formatAttendees: uid = " << uid << endl;
00116
00117
00118 QString tmpString = "<li>";
00119 if ( !uid.isEmpty() ) {
00120
00121 if ( name.isEmpty() )
00122
00123 tmpString += eventViewerAddLink( "uid:" + uid, email );
00124 else
00125 tmpString += eventViewerAddLink( "uid:" + uid, name );
00126 } else {
00127
00128 tmpString += ( name.isEmpty() ? email : name );
00129 }
00130 tmpString += '\n';
00131
00132
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
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
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
00200
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
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
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> </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> </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
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
00679 html += invitationRow( i18n( "What:" ), sSummary );
00680 html += invitationRow( i18n( "Where:" ), sLocation );
00681
00682
00683 html += invitationRow( i18n( "Start Time:" ), eventStartTimeStr( event ) );
00684
00685
00686 html += invitationRow( i18n( "End Time:" ), eventEndTimeStr( event ) );
00687
00688
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
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
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
01212
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
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
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
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 ) {
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> </td></tr><tr>";
01293
01294 #if 0
01295 html += helper->makeLinkURL( "accept", i18n("[Enter this into my calendar]") );
01296 html += "</td><td> </td><td>";
01297 #endif
01298
01299
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 ( inc && !existingIncidence ) {
01321
01322 html += helper->makeLink( "accept", i18n( "[Accept]" ) );
01323 html += "</td><td> </td><td>";
01324 html += helper->makeLink( "accept_conditionally",
01325 i18n( "Accept conditionally", "[Accept cond.]" ) );
01326 html += "</td><td> </td><td>";
01327
01328 html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
01329 html += "</td><td> </td><td>";
01330
01331 html += helper->makeLink( "decline", i18n( "[Decline]" ) );
01332 html += "</td><td> </td><td>";
01333
01334
01335 html += helper->makeLink( "delegate", i18n( "[Delegate]" ) );
01336 html += "</td><td> </td><td>";
01337
01338
01339 html += helper->makeLink( "forward", i18n( "[Forward]" ) );
01340
01341 if ( incBase->type() == "Event" ) {
01342 html += "</b></a></td><td> </td><td>";
01343 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01344 }
01345 }
01346 break;
01347 }
01348
01349 case Scheduler::Cancel:
01350
01351 html += helper->makeLink( "cancel", i18n( "[Remove this from my calendar]" ) );
01352 break;
01353
01354 case Scheduler::Reply:
01355
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 += " ";
01366 html += helper->makeLink( "decline_counter", i18n("[Decline]") );
01367 html += " ";
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
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
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
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
01432
01433 tzset();
01434
01435
01436 return rv;
01437 }
01438
01439
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
01448 free( old_tz.old_tz );
01449 } else {
01450
01451 putenv( strdup("TZ") );
01452 }
01453 tzset();
01454
01455
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
01503 if (dtStr.at(dtStr.length()-1) == 'Z') {
01504
01505
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
01531
01532
01533
01534 QString msgClass = tnefMsg->findProp( 0x001A, QString::null, true )
01535 .upper();
01536 if( !msgClass.isEmpty() ) {
01537
01538
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
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
01580
01581
01582
01583
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
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
01610
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
01631
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 );
01651 if( s.isEmpty() && !bIsReply )
01652 s = sSenderSearchKeyEmail;
01653
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 ) );
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
01669
01670
01671
01672
01673 s = tnefMsg->findProp( 0x0023 );
01674 event->setUid( s );
01675
01676
01677
01678
01679 s = tnefMsg->findProp( 0x8202 ).replace( QChar( '-' ), QString::null )
01680 .replace( QChar( ':' ), QString::null );
01681
01682
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
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
01713 alarm->setStartOffset( Duration( 15*60 ) );
01714 alarm->setDisplayAlarm( i18n( "Reminder" ) );
01715
01716
01717
01718 event->addAlarm( alarm );
01719 }
01720 cal.addEvent( event );
01721 bOk = true;
01722
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
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
01757
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
01789
01790
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 }
01814 }
01815 }
01816
01817
01818 QString iCal = calFormat.toString( &cal );
01819 if( !iCal.isEmpty() )
01820
01821 return iCal;
01822
01823
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
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> %1");
01884 if (event->doesFloat())
01885 ret += tmp.arg( event->dtStartDateStr().replace(" ", " ") );
01886 else
01887 ret += tmp.arg( event->dtStartStr().replace(" ", " ") );
01888
01889 tmp = "<br>" + i18n("Event end","<i>To:</i> %1");
01890 if (event->doesFloat())
01891 ret += tmp.arg( event->dtEndDateStr().replace(" ", " ") );
01892 else
01893 ret += tmp.arg( event->dtEndStr().replace(" ", " ") );
01894
01895 } else {
01896
01897 ret += "<br>"+i18n("<i>Date:</i> %1").
01898 arg( event->dtStartDateStr().replace(" ", " ") );
01899 if ( !event->doesFloat() ) {
01900 const QString dtStartTime = event->dtStartTimeStr().replace( " ", " " );
01901 const QString dtEndTime = event->dtEndTimeStr().replace( " ", " " );
01902 if ( dtStartTime == dtEndTime ) {
01903 tmp = "<br>" + i18n("time for event, to prevent ugly line breaks",
01904 "<i>Time:</i> %1").
01905 arg( dtStartTime );
01906 } else {
01907 tmp = "<br>" + i18n("time range for event, to prevent ugly line breaks",
01908 "<i>Time:</i> %1 - %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
01924
01925
01926 ret += "<br>" + i18n("<i>Start:</i> %1").arg(
01927 (floats)
01928 ?(todo->dtStartDateStr().replace(" ", " "))
01929 :(todo->dtStartStr().replace(" ", " ")) ) ;
01930 if (todo->hasDueDate())
01931 ret += "<br>" + i18n("<i>Due:</i> %1").arg(
01932 (floats)
01933 ?(todo->dtDueDateStr().replace(" ", " "))
01934 :(todo->dtDueStr().replace(" ", " ")) );
01935 if (todo->isCompleted())
01936 ret += "<br>" + i18n("<i>Completed:</i> %1").arg( todo->completedStr().replace(" ", " ") );
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> %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> %1") );
01955 QString ret = tmp.arg( KGlobal::locale()->formatDateTime( fb->dtStart() ) );
01956 tmp = "<br>" + i18n("<i>Period start:</i> %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
02001 tmp += "<br>"+i18n("<i>Location:</i> %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
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
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
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
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" ) );
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
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 }