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