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