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 tmp += ' ';
01097 secs -= ( days * 86400 );
01098 }
01099 int hours = secs / 3600;
01100 if ( hours > 0 ) {
01101 tmp += i18n( "1 hour", "%n hours", hours );
01102 tmp += ' ';
01103 secs -= ( hours * 3600 );
01104 }
01105 int mins = secs / 60;
01106 if ( mins > 0 ) {
01107 tmp += i18n( "1 minute", "%n minutes", mins );
01108 tmp += ' ';
01109 }
01110 html += invitationRow( i18n( "Duration:" ), tmp );
01111 }
01112
01113 if ( event->doesRecur() )
01114 html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) );
01115
01116 html += "</table>\n";
01117 html += invitationsDetailsIncidence( event );
01118 html += "</div>\n";
01119
01120 return html;
01121 }
01122
01123 static QString invitationDetailsTodo( Todo *todo )
01124 {
01125
01126 if ( !todo )
01127 return QString::null;
01128
01129 QString sSummary = i18n( "Summary unspecified" );
01130 if ( ! todo->summary().isEmpty() ) {
01131 sSummary = QStyleSheet::escape( todo->summary() );
01132 }
01133
01134 QString sLocation = i18n( "Location unspecified" );
01135 if ( ! todo->location().isEmpty() ) {
01136 sLocation = QStyleSheet::escape( todo->location() );
01137 }
01138
01139 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
01140 QString html = QString("<div dir=\"%1\">\n").arg(dir);
01141
01142 html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
01143
01144
01145 html += invitationRow( i18n( "What:" ), sSummary );
01146 html += invitationRow( i18n( "Where:" ), sLocation );
01147
01148 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
01149 html += invitationRow( i18n( "Start Date:" ),
01150 IncidenceFormatter::dateToString( todo->dtStart(), false ) );
01151 }
01152 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
01153 html += invitationRow( i18n( "Due Date:" ),
01154 IncidenceFormatter::dateToString( todo->dtDue(), false ) );
01155 if ( !todo->doesFloat() ) {
01156 html += invitationRow( i18n( "Due Time:" ),
01157 KGlobal::locale()->formatTime( todo->dtDue().time() ) );
01158 }
01159
01160 } else {
01161 html += invitationRow( i18n( "Due Date:" ),
01162 i18n( "Due Date: None", "None" ) );
01163 }
01164
01165 html += "</table></div>\n";
01166 html += invitationsDetailsIncidence( todo );
01167
01168 return html;
01169 }
01170
01171 static QString invitationDetailsJournal( Journal *journal )
01172 {
01173 if ( !journal )
01174 return QString::null;
01175
01176 QString sSummary = i18n( "Summary unspecified" );
01177 QString sDescr = i18n( "Description unspecified" );
01178 if ( ! journal->summary().isEmpty() ) {
01179 sSummary = journal->summary();
01180 }
01181 if ( ! journal->description().isEmpty() ) {
01182 sDescr = journal->description();
01183 }
01184 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01185 html += invitationRow( i18n( "Summary:" ), sSummary );
01186 html += invitationRow( i18n( "Date:" ),
01187 IncidenceFormatter::dateToString( journal->dtStart(), false ) );
01188 html += invitationRow( i18n( "Description:" ), sDescr );
01189 html += "</table>\n";
01190 html += invitationsDetailsIncidence( journal );
01191
01192 return html;
01193 }
01194
01195 static QString invitationDetailsFreeBusy( FreeBusy *fb )
01196 {
01197 if ( !fb )
01198 return QString::null;
01199 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01200
01201 html += invitationRow( i18n("Person:"), fb->organizer().fullName() );
01202 html += invitationRow( i18n("Start date:"),
01203 IncidenceFormatter::dateToString( fb->dtStart(), true ) );
01204 html += invitationRow( i18n("End date:"),
01205 KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) );
01206 html += "<tr><td colspan=2><hr></td></tr>\n";
01207 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
01208
01209 QValueList<Period> periods = fb->busyPeriods();
01210
01211 QValueList<Period>::iterator it;
01212 for ( it = periods.begin(); it != periods.end(); ++it ) {
01213 Period per = *it;
01214 if ( per.hasDuration() ) {
01215 int dur = per.duration().asSeconds();
01216 QString cont;
01217 if ( dur >= 3600 ) {
01218 cont += i18n("1 hour ", "%n hours ", dur / 3600);
01219 dur %= 3600;
01220 }
01221 if ( dur >= 60 ) {
01222 cont += i18n("1 minute", "%n minutes ", dur / 60);
01223 dur %= 60;
01224 }
01225 if ( dur > 0 ) {
01226 cont += i18n("1 second", "%n seconds", dur);
01227 }
01228 html += invitationRow( QString::null, i18n("startDate for duration", "%1 for %2")
01229 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
01230 .arg(cont) );
01231 } else {
01232 QString cont;
01233 if ( per.start().date() == per.end().date() ) {
01234 cont = i18n("date, fromTime - toTime ", "%1, %2 - %3")
01235 .arg( KGlobal::locale()->formatDate( per.start().date() ) )
01236 .arg( KGlobal::locale()->formatTime( per.start().time() ) )
01237 .arg( KGlobal::locale()->formatTime( per.end().time() ) );
01238 } else {
01239 cont = i18n("fromDateTime - toDateTime", "%1 - %2")
01240 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
01241 .arg( KGlobal::locale()->formatDateTime( per.end(), false ) );
01242 }
01243
01244 html += invitationRow( QString::null, cont );
01245 }
01246 }
01247
01248 html += "</table>\n";
01249 return html;
01250 }
01251
01252 static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
01253 {
01254 if ( !msg || !event )
01255 return QString::null;
01256
01257 switch ( msg->method() ) {
01258 case Scheduler::Publish:
01259 return i18n( "This event has been published" );
01260 case Scheduler::Request:
01261 if ( event->revision() > 0 ) {
01262 return i18n( "This invitation has been updated" );
01263 }
01264 if ( iamOrganizer( event ) ) {
01265 return i18n( "I sent this invitation" );
01266 } else {
01267 if ( !event->organizer().fullName().isEmpty() ) {
01268 return i18n( "You received an invitation from %1" ).
01269 arg( event->organizer().fullName() );
01270 } else {
01271 return i18n( "You received an invitation" );
01272 }
01273 }
01274 case Scheduler::Refresh:
01275 return i18n( "This invitation was refreshed" );
01276 case Scheduler::Cancel:
01277 return i18n( "This invitation has been canceled" );
01278 case Scheduler::Add:
01279 return i18n( "Addition to the invitation" );
01280 case Scheduler::Reply: {
01281 Attendee::List attendees = event->attendees();
01282 if( attendees.count() == 0 ) {
01283 kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01284 return QString::null;
01285 }
01286 if( attendees.count() != 1 ) {
01287 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01288 << "but is " << attendees.count() << endl;
01289 }
01290 Attendee* attendee = *attendees.begin();
01291 QString attendeeName = attendee->name();
01292 if ( attendeeName.isEmpty() ) {
01293 attendeeName = attendee->email();
01294 }
01295 if ( attendeeName.isEmpty() ) {
01296 attendeeName = i18n( "Sender" );
01297 }
01298
01299 QString delegatorName, dummy;
01300 KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
01301 if ( delegatorName.isEmpty() ) {
01302 delegatorName = attendee->delegator();
01303 }
01304
01305 switch( attendee->status() ) {
01306 case Attendee::NeedsAction:
01307 return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName );
01308 case Attendee::Accepted:
01309 if ( delegatorName.isEmpty() ) {
01310 return i18n( "%1 accepts this invitation" ).arg( attendeeName );
01311 } else {
01312 return i18n( "%1 accepts this invitation on behalf of %2" ).
01313 arg( attendeeName ).arg( delegatorName );
01314 }
01315 case Attendee::Tentative:
01316 if ( delegatorName.isEmpty() ) {
01317 return i18n( "%1 tentatively accepts this invitation" ).
01318 arg( attendeeName );
01319 } else {
01320 return i18n( "%1 tentatively accepts this invitation on behalf of %2" ).
01321 arg( attendeeName ).arg( delegatorName );
01322 }
01323 case Attendee::Declined:
01324 if ( delegatorName.isEmpty() ) {
01325 return i18n( "%1 declines this invitation" ).arg( attendeeName );
01326 } else {
01327 return i18n( "%1 declines this invitation on behalf of %2" ).
01328 arg( attendeeName ).arg( delegatorName );
01329 }
01330 case Attendee::Delegated: {
01331 QString delegate, dummy;
01332 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01333 if ( delegate.isEmpty() ) {
01334 delegate = attendee->delegate();
01335 }
01336 if ( !delegate.isEmpty() ) {
01337 return i18n( "%1 has delegated this invitation to %2" ).
01338 arg( attendeeName ) .arg( delegate );
01339 } else {
01340 return i18n( "%1 has delegated this invitation" ).arg( attendeeName );
01341 }
01342 }
01343 case Attendee::Completed:
01344 return i18n( "This invitation is now completed" );
01345 case Attendee::InProcess:
01346 return i18n( "%1 is still processing the invitation" ).
01347 arg( attendeeName );
01348 default:
01349 return i18n( "Unknown response to this invitation" );
01350 }
01351 break; }
01352 case Scheduler::Counter:
01353 return i18n( "Sender makes this counter proposal" );
01354 case Scheduler::Declinecounter:
01355 return i18n( "Sender declines the counter proposal" );
01356 case Scheduler::NoMethod:
01357 return i18n("Error: iMIP message with unknown method: '%1'").
01358 arg( msg->method() );
01359 }
01360 return QString::null;
01361 }
01362
01363 static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
01364 {
01365 if ( !msg || !todo ) {
01366 return QString::null;
01367 }
01368
01369 switch ( msg->method() ) {
01370 case Scheduler::Publish:
01371 return i18n("This task has been published");
01372 case Scheduler::Request:
01373 if ( todo->revision() > 0 ) {
01374 return i18n( "This task has been updated" );
01375 } else {
01376 return i18n( "You have been assigned this task" );
01377 }
01378 case Scheduler::Refresh:
01379 return i18n( "This task was refreshed" );
01380 case Scheduler::Cancel:
01381 return i18n( "This task was canceled" );
01382 case Scheduler::Add:
01383 return i18n( "Addition to the task" );
01384 case Scheduler::Reply: {
01385 Attendee::List attendees = todo->attendees();
01386 if( attendees.count() == 0 ) {
01387 kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01388 return QString::null;
01389 }
01390 if( attendees.count() != 1 ) {
01391 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01392 << "but is " << attendees.count() << endl;
01393 }
01394 Attendee* attendee = *attendees.begin();
01395
01396 switch( attendee->status() ) {
01397 case Attendee::NeedsAction:
01398 return i18n( "Sender indicates this task assignment still needs some action" );
01399 case Attendee::Accepted:
01400 return i18n( "Sender accepts this task" );
01401 case Attendee::Tentative:
01402 return i18n( "Sender tentatively accepts this task" );
01403 case Attendee::Declined:
01404 return i18n( "Sender declines this task" );
01405 case Attendee::Delegated: {
01406 QString delegate, dummy;
01407 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01408 if ( delegate.isEmpty() ) {
01409 delegate = attendee->delegate();
01410 }
01411 if ( !delegate.isEmpty() ) {
01412 return i18n( "Sender has delegated this request for the task to %1" ).arg( delegate );
01413 } else {
01414 return i18n( "Sender has delegated this request for the task " );
01415 }
01416 }
01417 case Attendee::Completed:
01418 return i18n( "The request for this task is now completed" );
01419 case Attendee::InProcess:
01420 return i18n( "Sender is still processing the invitation" );
01421 default:
01422 return i18n( "Unknown response to this task" );
01423 }
01424 break; }
01425 case Scheduler::Counter:
01426 return i18n( "Sender makes this counter proposal" );
01427 case Scheduler::Declinecounter:
01428 return i18n( "Sender declines the counter proposal" );
01429 case Scheduler::NoMethod:
01430 return i18n("Error: iMIP message with unknown method: '%1'").
01431 arg( msg->method() );
01432 }
01433 return QString::null;
01434 }
01435
01436 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
01437 {
01438 if ( !msg || !journal ) {
01439 return QString::null;
01440 }
01441
01442 switch ( msg->method() ) {
01443 case Scheduler::Publish:
01444 return i18n("This journal has been published");
01445 case Scheduler::Request:
01446 return i18n( "You have been assigned this journal" );
01447 case Scheduler::Refresh:
01448 return i18n( "This journal was refreshed" );
01449 case Scheduler::Cancel:
01450 return i18n( "This journal was canceled" );
01451 case Scheduler::Add:
01452 return i18n( "Addition to the journal" );
01453 case Scheduler::Reply: {
01454 Attendee::List attendees = journal->attendees();
01455 if( attendees.count() == 0 ) {
01456 kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01457 return QString::null;
01458 }
01459 if( attendees.count() != 1 ) {
01460 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01461 << "but is " << attendees.count() << endl;
01462 }
01463 Attendee* attendee = *attendees.begin();
01464
01465 switch( attendee->status() ) {
01466 case Attendee::NeedsAction:
01467 return i18n( "Sender indicates this journal assignment still needs some action" );
01468 case Attendee::Accepted:
01469 return i18n( "Sender accepts this journal" );
01470 case Attendee::Tentative:
01471 return i18n( "Sender tentatively accepts this journal" );
01472 case Attendee::Declined:
01473 return i18n( "Sender declines this journal" );
01474 case Attendee::Delegated:
01475 return i18n( "Sender has delegated this request for the journal" );
01476 case Attendee::Completed:
01477 return i18n( "The request for this journal is now completed" );
01478 case Attendee::InProcess:
01479 return i18n( "Sender is still processing the invitation" );
01480 default:
01481 return i18n( "Unknown response to this journal" );
01482 }
01483 break;
01484 }
01485 case Scheduler::Counter:
01486 return i18n( "Sender makes this counter proposal" );
01487 case Scheduler::Declinecounter:
01488 return i18n( "Sender declines the counter proposal" );
01489 case Scheduler::NoMethod:
01490 return i18n("Error: iMIP message with unknown method: '%1'").
01491 arg( msg->method() );
01492 }
01493 return QString::null;
01494 }
01495
01496 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01497 {
01498 if ( !msg || !fb ) {
01499 return QString::null;
01500 }
01501
01502 switch ( msg->method() ) {
01503 case Scheduler::Publish:
01504 return i18n("This free/busy list has been published");
01505 case Scheduler::Request:
01506 return i18n( "The free/busy list has been requested" );
01507 case Scheduler::Refresh:
01508 return i18n( "This free/busy list was refreshed" );
01509 case Scheduler::Cancel:
01510 return i18n( "This free/busy list was canceled" );
01511 case Scheduler::Add:
01512 return i18n( "Addition to the free/busy list" );
01513 case Scheduler::NoMethod:
01514 default:
01515 return i18n("Error: Free/Busy iMIP message with unknown method: '%1'").
01516 arg( msg->method() );
01517 }
01518 }
01519
01520 static QString invitationAttendees( Incidence *incidence )
01521 {
01522 QString tmpStr;
01523 if ( !incidence ) {
01524 return tmpStr;
01525 }
01526
01527 tmpStr += htmlAddTag( "u", i18n( "Attendee List" ) );
01528 tmpStr += "<br/>";
01529
01530 int count=0;
01531 Attendee::List attendees = incidence->attendees();
01532 if ( !attendees.isEmpty() ) {
01533
01534 Attendee::List::ConstIterator it;
01535 for( it = attendees.begin(); it != attendees.end(); ++it ) {
01536 Attendee *a = *it;
01537 if ( !iamAttendee( a ) ) {
01538 count++;
01539 if ( count == 1 ) {
01540 tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\" columns=\"2\">";
01541 }
01542 tmpStr += "<tr>";
01543 tmpStr += "<td>";
01544 tmpStr += invitationPerson( a->email(), a->name(), QString::null );
01545 if ( !a->delegator().isEmpty() ) {
01546 tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
01547 }
01548 if ( !a->delegate().isEmpty() ) {
01549 tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
01550 }
01551 tmpStr += "</td>";
01552 tmpStr += "<td>" + a->statusStr() + "</td>";
01553 tmpStr += "</tr>";
01554 }
01555 }
01556 }
01557 if ( count ) {
01558 tmpStr += "</table>";
01559 } else {
01560 tmpStr += "<i>" + i18n( "No attendee", "None" ) + "</i>";
01561 }
01562
01563 return tmpStr;
01564 }
01565
01566 static QString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence )
01567 {
01568 QString tmpStr;
01569 if ( !incidence ) {
01570 return tmpStr;
01571 }
01572
01573 Attachment::List attachments = incidence->attachments();
01574 if ( !attachments.isEmpty() ) {
01575 tmpStr += i18n( "Attached Documents:" ) + "<ol>";
01576
01577 Attachment::List::ConstIterator it;
01578 for( it = attachments.begin(); it != attachments.end(); ++it ) {
01579 Attachment *a = *it;
01580 tmpStr += "<li>";
01581
01582 KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() );
01583 QString iconStr = mimeType->icon( a->uri(), false );
01584 QString iconPath = KGlobal::iconLoader()->iconPath( iconStr, KIcon::Small );
01585 if ( !iconPath.isEmpty() ) {
01586 tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
01587 }
01588 tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() );
01589 tmpStr += "</li>";
01590 }
01591 tmpStr += "</ol>";
01592 }
01593
01594 return tmpStr;
01595 }
01596
01597 class IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor
01598 {
01599 public:
01600 ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
01601 bool act( IncidenceBase *incidence, ScheduleMessage *msg ) { mMessage = msg; return incidence->accept( *this ); }
01602 QString result() const { return mResult; }
01603
01604 protected:
01605 QString mResult;
01606 ScheduleMessage *mMessage;
01607 };
01608
01609 class IncidenceFormatter::InvitationHeaderVisitor :
01610 public IncidenceFormatter::ScheduleMessageVisitor
01611 {
01612 protected:
01613 bool visit( Event *event )
01614 {
01615 mResult = invitationHeaderEvent( event, mMessage );
01616 return !mResult.isEmpty();
01617 }
01618 bool visit( Todo *todo )
01619 {
01620 mResult = invitationHeaderTodo( todo, mMessage );
01621 return !mResult.isEmpty();
01622 }
01623 bool visit( Journal *journal )
01624 {
01625 mResult = invitationHeaderJournal( journal, mMessage );
01626 return !mResult.isEmpty();
01627 }
01628 bool visit( FreeBusy *fb )
01629 {
01630 mResult = invitationHeaderFreeBusy( fb, mMessage );
01631 return !mResult.isEmpty();
01632 }
01633 };
01634
01635 class IncidenceFormatter::InvitationBodyVisitor :
01636 public IncidenceFormatter::ScheduleMessageVisitor
01637 {
01638 protected:
01639 bool visit( Event *event )
01640 {
01641 mResult = invitationDetailsEvent( event );
01642 return !mResult.isEmpty();
01643 }
01644 bool visit( Todo *todo )
01645 {
01646 mResult = invitationDetailsTodo( todo );
01647 return !mResult.isEmpty();
01648 }
01649 bool visit( Journal *journal )
01650 {
01651 mResult = invitationDetailsJournal( journal );
01652 return !mResult.isEmpty();
01653 }
01654 bool visit( FreeBusy *fb )
01655 {
01656 mResult = invitationDetailsFreeBusy( fb );
01657 return !mResult.isEmpty();
01658 }
01659 };
01660
01661 class IncidenceFormatter::IncidenceCompareVisitor :
01662 public IncidenceBase::Visitor
01663 {
01664 public:
01665 IncidenceCompareVisitor() : mExistingIncidence(0) {}
01666 bool act( IncidenceBase *incidence, Incidence* existingIncidence )
01667 {
01668 Incidence *inc = dynamic_cast<Incidence*>( incidence );
01669 if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() )
01670 return false;
01671 mExistingIncidence = existingIncidence;
01672 return incidence->accept( *this );
01673 }
01674
01675 QString result() const
01676 {
01677 if ( mChanges.isEmpty() ) {
01678 return QString::null;
01679 }
01680 QString html = "<div align=\"left\"><ul><li>";
01681 html += mChanges.join( "</li><li>" );
01682 html += "</li><ul></div>";
01683 return html;
01684 }
01685
01686 protected:
01687 bool visit( Event *event )
01688 {
01689 compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
01690 compareIncidences( event, mExistingIncidence );
01691 return !mChanges.isEmpty();
01692 }
01693 bool visit( Todo *todo )
01694 {
01695 compareIncidences( todo, mExistingIncidence );
01696 return !mChanges.isEmpty();
01697 }
01698 bool visit( Journal *journal )
01699 {
01700 compareIncidences( journal, mExistingIncidence );
01701 return !mChanges.isEmpty();
01702 }
01703 bool visit( FreeBusy *fb )
01704 {
01705 Q_UNUSED( fb );
01706 return !mChanges.isEmpty();
01707 }
01708
01709 private:
01710 void compareEvents( Event *newEvent, Event *oldEvent )
01711 {
01712 if ( !oldEvent || !newEvent )
01713 return;
01714 if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() )
01715 mChanges += i18n( "The invitation starting time has been changed from %1 to %2" )
01716 .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) );
01717 if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() )
01718 mChanges += i18n( "The invitation ending time has been changed from %1 to %2" )
01719 .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) );
01720 }
01721
01722 void compareIncidences( Incidence *newInc, Incidence *oldInc )
01723 {
01724 if ( !oldInc || !newInc )
01725 return;
01726 if ( oldInc->summary() != newInc->summary() )
01727 mChanges += i18n( "The summary has been changed to: \"%1\"" ).arg( newInc->summary() );
01728 if ( oldInc->location() != newInc->location() )
01729 mChanges += i18n( "The location has been changed to: \"%1\"" ).arg( newInc->location() );
01730 if ( oldInc->description() != newInc->description() )
01731 mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() );
01732 Attendee::List oldAttendees = oldInc->attendees();
01733 Attendee::List newAttendees = newInc->attendees();
01734 for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); it != newAttendees.constEnd(); ++it ) {
01735 Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
01736 if ( !oldAtt ) {
01737 mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() );
01738 } else {
01739 if ( oldAtt->status() != (*it)->status() )
01740 mChanges += i18n( "The status of attendee %1 has been changed to: %2" ).arg( (*it)->fullName() )
01741 .arg( (*it)->statusStr() );
01742 }
01743 }
01744 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); it != oldAttendees.constEnd(); ++it ) {
01745 Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
01746 if ( !newAtt )
01747 mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() );
01748 }
01749 }
01750
01751 private:
01752 Incidence* mExistingIncidence;
01753 QStringList mChanges;
01754 };
01755
01756
01757 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
01758 {
01759 if ( !id.startsWith( "ATTACH:" ) ) {
01760 QString res = QString( "<a href=\"%1\"><b>%2</b></a>" ).
01761 arg( generateLinkURL( id ), text );
01762 return res;
01763 } else {
01764
01765 QString res = QString( "<a href=\"%1\">%2</a>" ).
01766 arg( generateLinkURL( id ), text );
01767 return res;
01768 }
01769 }
01770
01771
01772
01773 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
01774 {
01775 CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
01776 if ( !cal || !incidence ) {
01777 return true;
01778 }
01779 ResourceCalendar *res = cal->resource( incidence );
01780 if ( !res ) {
01781 return true;
01782 }
01783 const QString subRes = res->subresourceIdentifier( incidence );
01784 if ( !subRes.contains( "/.INBOX.directory/" ) ) {
01785 return false;
01786 }
01787 return true;
01788 }
01789
01790 QString IncidenceFormatter::formatICalInvitation( QString invitation, Calendar *mCalendar,
01791 InvitationFormatterHelper *helper )
01792 {
01793 if ( invitation.isEmpty() ) return QString::null;
01794
01795 ICalFormat format;
01796
01797 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
01798
01799 if( !msg ) {
01800 kdDebug( 5850 ) << "Failed to parse the scheduling message" << endl;
01801 Q_ASSERT( format.exception() );
01802 kdDebug( 5850 ) << format.exception()->message() << endl;
01803 return QString::null;
01804 }
01805
01806 IncidenceBase *incBase = msg->event();
01807
01808
01809 Incidence *existingIncidence = 0;
01810 if ( incBase && helper->calendar() ) {
01811 existingIncidence = helper->calendar()->incidence( incBase->uid() );
01812 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
01813 existingIncidence = 0;
01814 }
01815 if ( !existingIncidence ) {
01816 const Incidence::List list = helper->calendar()->incidences();
01817 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
01818 if ( (*it)->schedulingID() == incBase->uid() &&
01819 incidenceOwnedByMe( helper->calendar(), *it ) ) {
01820 existingIncidence = *it;
01821 break;
01822 }
01823 }
01824 }
01825 }
01826
01827
01828 QString html;
01829
01830 QString tableStyle = QString::fromLatin1(
01831 "style=\"border: solid 1px; margin: 0em;\"" );
01832 QString tableHead = QString::fromLatin1(
01833 "<div align=\"center\">"
01834 "<table width=\"80%\" cellpadding=\"1\" cellspacing=\"0\" %1>"
01835 "<tr><td>").arg(tableStyle);
01836
01837 html += tableHead;
01838 InvitationHeaderVisitor headerVisitor;
01839
01840 if ( !headerVisitor.act( incBase, msg ) )
01841 return QString::null;
01842 html += "<b>" + headerVisitor.result() + "</b>";
01843
01844 InvitationBodyVisitor bodyVisitor;
01845 if ( !bodyVisitor.act( incBase, msg ) )
01846 return QString::null;
01847 html += bodyVisitor.result();
01848
01849 if ( msg->method() == Scheduler::Request ) {
01850 IncidenceCompareVisitor compareVisitor;
01851 if ( compareVisitor.act( incBase, existingIncidence ) ) {
01852 html += i18n("<p align=\"left\">The following changes have been made by the organizer:</p>");
01853 html += compareVisitor.result();
01854 }
01855 }
01856
01857 Incidence *inc = dynamic_cast<Incidence*>( incBase );
01858
01859
01860 bool myInc = iamOrganizer( inc );
01861
01862
01863 bool rsvpRec = false;
01864 Attendee *ea = 0;
01865 if ( !myInc ) {
01866 if ( existingIncidence ) {
01867 ea = findMyAttendee( existingIncidence );
01868 }
01869 if ( ea && ( ea->status() == Attendee::Accepted || ea->status() == Attendee::Declined ) ) {
01870 rsvpRec = true;
01871 }
01872 }
01873
01874
01875 bool rsvpReq = rsvpRequested( inc );
01876 if ( !myInc ) {
01877 html += "<br/>";
01878 html += "<i><u>";
01879 if ( rsvpRec && ( inc && inc->revision() == 0 ) ) {
01880 html += i18n( "Your response has already been recorded [%1]" ).
01881 arg( ea->statusStr() );
01882 rsvpReq = false;
01883 } else if ( msg->method() == Scheduler::Cancel ) {
01884 html += i18n( "This invitation was declined" );
01885 } else if ( msg->method() == Scheduler::Add ) {
01886 html += i18n( "This invitation was accepted" );
01887 } else {
01888 html += rsvpRequestedStr( rsvpReq );
01889 }
01890 html += "</u></i><br>";
01891 }
01892
01893
01894
01895 html += "<table border=\"0\" cellspacing=\"0\"><tr><td> </td></tr><tr>";
01896
01897 switch ( msg->method() ) {
01898 case Scheduler::Publish:
01899 case Scheduler::Request:
01900 case Scheduler::Refresh:
01901 case Scheduler::Add:
01902 {
01903 if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
01904 if ( inc->type() == "Todo" ) {
01905 html += "<td colspan=\"9\">";
01906 html += helper->makeLink( "reply", i18n( "[Record invitation in my task list]" ) );
01907 } else {
01908 html += "<td colspan=\"13\">";
01909 html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) );
01910 }
01911 html += "</td></tr><tr>";
01912 }
01913 html += "<td>";
01914
01915 if ( !myInc ) {
01916 if ( rsvpReq ) {
01917
01918 html += helper->makeLink( "accept", i18n( "[Accept]" ) );
01919 html += "</td><td> </td><td>";
01920 html += helper->makeLink( "accept_conditionally",
01921 i18n( "Accept conditionally", "[Accept cond.]" ) );
01922 html += "</td><td> </td><td>";
01923 }
01924
01925 if ( rsvpReq ) {
01926
01927 html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
01928 html += "</td><td> </td><td>";
01929 }
01930
01931 if ( rsvpReq ) {
01932
01933 html += helper->makeLink( "decline", i18n( "[Decline]" ) );
01934 html += "</td><td> </td><td>";
01935 }
01936
01937 if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
01938
01939 html += helper->makeLink( "delegate", i18n( "[Delegate]" ) );
01940 html += "</td><td> </td><td>";
01941
01942
01943 html += helper->makeLink( "forward", i18n( "[Forward]" ) );
01944
01945
01946 if ( inc && inc->type() == "Event" ) {
01947 html += "</td><td> </td><td>";
01948 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
01949 }
01950 }
01951 }
01952 break;
01953 }
01954
01955 case Scheduler::Cancel:
01956
01957 if ( inc->type() == "Todo" ) {
01958 html += helper->makeLink( "cancel", i18n( "[Remove invitation from my task list]" ) );
01959 } else {
01960 html += helper->makeLink( "cancel", i18n( "[Remove invitation from my calendar]" ) );
01961 }
01962 break;
01963
01964 case Scheduler::Reply:
01965 {
01966
01967 Attendee *a = 0;
01968 Attendee *ea = 0;
01969 if ( inc ) {
01970 a = inc->attendees().first();
01971 if ( a ) {
01972 ea = findAttendee( existingIncidence, a->email() );
01973 }
01974 }
01975 if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
01976 if ( inc && inc->revision() > 0 ) {
01977 html += "<br><u><i>";
01978 html += i18n( "The response has been recorded [%1]" ).arg( ea->statusStr() );
01979 html += "</i></u>";
01980 }
01981 } else {
01982 if ( inc ) {
01983 if ( inc->type() == "Todo" ) {
01984 html += helper->makeLink( "reply", i18n( "[Record response in my task list]" ) );
01985 } else {
01986 html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) );
01987 }
01988 }
01989 }
01990 break;
01991 }
01992
01993 case Scheduler::Counter:
01994
01995 html += helper->makeLink( "accept_counter", i18n("[Accept]") );
01996 html += " ";
01997 html += helper->makeLink( "decline_counter", i18n("[Decline]") );
01998 html += " ";
01999 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
02000 break;
02001
02002 case Scheduler::Declinecounter:
02003 case Scheduler::NoMethod:
02004 break;
02005 }
02006
02007
02008 html += "</td></tr></table>";
02009
02010
02011 if ( myInc && helper->calendar() ) {
02012 html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
02013 }
02014
02015
02016 html += "</td></tr></table><br></div>";
02017
02018
02019 html += invitationAttachments( helper, inc );
02020
02021 return html;
02022 }
02023
02024
02025
02026
02027
02028
02029
02030
02031
02032
02033
02034 static QString stringProp( KTNEFMessage* tnefMsg, const Q_UINT32& key,
02035 const QString& fallback = QString::null)
02036 {
02037 return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16,
02038 fallback );
02039 }
02040
02041 static QString sNamedProp( KTNEFMessage* tnefMsg, const QString& name,
02042 const QString& fallback = QString::null )
02043 {
02044 return tnefMsg->findNamedProp( name, fallback );
02045 }
02046
02047 struct save_tz { char* old_tz; char* tz_env_str; };
02048
02049
02050 static struct save_tz set_tz( const char* _tc )
02051 {
02052 const char *tc = _tc?_tc:"UTC";
02053
02054 struct save_tz rv;
02055
02056 rv.old_tz = 0;
02057 rv.tz_env_str = 0;
02058
02059
02060
02061 char* tz_env = 0;
02062 if( getenv( "TZ" ) ) {
02063 tz_env = strdup( getenv( "TZ" ) );
02064 rv.old_tz = tz_env;
02065 }
02066 char* tmp_env = (char*)malloc( strlen( tc ) + 4 );
02067 strcpy( tmp_env, "TZ=" );
02068 strcpy( tmp_env+3, tc );
02069 putenv( tmp_env );
02070
02071 rv.tz_env_str = tmp_env;
02072
02073
02074
02075 tzset();
02076
02077
02078 return rv;
02079 }
02080
02081
02082 static void unset_tz( struct save_tz old_tz )
02083 {
02084 if( old_tz.old_tz ) {
02085 char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 );
02086 strcpy( tmp_env, "TZ=" );
02087 strcpy( tmp_env+3, old_tz.old_tz );
02088 putenv( tmp_env );
02089
02090 free( old_tz.old_tz );
02091 } else {
02092
02093 putenv( strdup("TZ") );
02094 }
02095 tzset();
02096
02097
02098 if( old_tz.tz_env_str ) free( old_tz.tz_env_str );
02099 }
02100
02101 static QDateTime utc2Local( const QDateTime& utcdt )
02102 {
02103 struct tm tmL;
02104
02105 save_tz tmp_tz = set_tz("UTC");
02106 time_t utc = utcdt.toTime_t();
02107 unset_tz( tmp_tz );
02108
02109 localtime_r( &utc, &tmL );
02110 return QDateTime( QDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ),
02111 QTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) );
02112 }
02113
02114
02115 static QDateTime pureISOToLocalQDateTime( const QString& dtStr,
02116 bool bDateOnly = false )
02117 {
02118 QDate tmpDate;
02119 QTime tmpTime;
02120 int year, month, day, hour, minute, second;
02121
02122 if( bDateOnly ) {
02123 year = dtStr.left( 4 ).toInt();
02124 month = dtStr.mid( 4, 2 ).toInt();
02125 day = dtStr.mid( 6, 2 ).toInt();
02126 hour = 0;
02127 minute = 0;
02128 second = 0;
02129 } else {
02130 year = dtStr.left( 4 ).toInt();
02131 month = dtStr.mid( 4, 2 ).toInt();
02132 day = dtStr.mid( 6, 2 ).toInt();
02133 hour = dtStr.mid( 9, 2 ).toInt();
02134 minute = dtStr.mid( 11, 2 ).toInt();
02135 second = dtStr.mid( 13, 2 ).toInt();
02136 }
02137 tmpDate.setYMD( year, month, day );
02138 tmpTime.setHMS( hour, minute, second );
02139
02140 if( tmpDate.isValid() && tmpTime.isValid() ) {
02141 QDateTime dT = QDateTime( tmpDate, tmpTime );
02142
02143 if( !bDateOnly ) {
02144
02145 if (dtStr.at(dtStr.length()-1) == 'Z') {
02146
02147
02148 dT = utc2Local( dT );
02149 }
02150 }
02151 return dT;
02152 } else
02153 return QDateTime();
02154 }
02155
02156
02157
02158 QString IncidenceFormatter::msTNEFToVPart( const QByteArray& tnef )
02159 {
02160 bool bOk = false;
02161
02162 KTNEFParser parser;
02163 QBuffer buf( tnef );
02164 CalendarLocal cal ( QString::fromLatin1( "UTC" ) );
02165 KABC::Addressee addressee;
02166 KABC::VCardConverter cardConv;
02167 ICalFormat calFormat;
02168 Event* event = new Event();
02169
02170 if( parser.openDevice( &buf ) ) {
02171 KTNEFMessage* tnefMsg = parser.message();
02172
02173
02174
02175
02176 QString msgClass = tnefMsg->findProp( 0x001A, QString::null, true )
02177 .upper();
02178 if( !msgClass.isEmpty() ) {
02179
02180
02181 bool bCompatClassAppointment = false;
02182 bool bCompatMethodRequest = false;
02183 bool bCompatMethodCancled = false;
02184 bool bCompatMethodAccepted = false;
02185 bool bCompatMethodAcceptedCond = false;
02186 bool bCompatMethodDeclined = false;
02187 if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) {
02188 bCompatClassAppointment = true;
02189 if( msgClass.endsWith( ".MTGREQ" ) )
02190 bCompatMethodRequest = true;
02191 if( msgClass.endsWith( ".MTGCNCL" ) )
02192 bCompatMethodCancled = true;
02193 if( msgClass.endsWith( ".MTGRESPP" ) )
02194 bCompatMethodAccepted = true;
02195 if( msgClass.endsWith( ".MTGRESPA" ) )
02196 bCompatMethodAcceptedCond = true;
02197 if( msgClass.endsWith( ".MTGRESPN" ) )
02198 bCompatMethodDeclined = true;
02199 }
02200 bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" );
02201
02202 if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) {
02203
02204 bool bIsReply = false;
02205 QString prodID = "-//Microsoft Corporation//Outlook ";
02206 prodID += tnefMsg->findNamedProp( "0x8554", "9.0" );
02207 prodID += "MIMEDIR/EN\n";
02208 prodID += "VERSION:2.0\n";
02209 calFormat.setApplication( "Outlook", prodID );
02210
02211 Scheduler::Method method;
02212 if( bCompatMethodRequest )
02213 method = Scheduler::Request;
02214 else if( bCompatMethodCancled )
02215 method = Scheduler::Cancel;
02216 else if( bCompatMethodAccepted || bCompatMethodAcceptedCond ||
02217 bCompatMethodDeclined ) {
02218 method = Scheduler::Reply;
02219 bIsReply = true;
02220 } else {
02221
02222
02223
02224
02225
02226
02227
02228
02229 if( tnefMsg->findProp(0x0c17) == "1" )
02230 bIsReply = true;
02231 method = Scheduler::Request;
02232 }
02233
02235 ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown );
02236
02237 QString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) );
02238
02239 if( !sSenderSearchKeyEmail.isEmpty() ) {
02240 int colon = sSenderSearchKeyEmail.find( ':' );
02241
02242 if( sSenderSearchKeyEmail.find( ':' ) == -1 )
02243 sSenderSearchKeyEmail.remove( 0, colon+1 );
02244 }
02245
02246 QString s( tnefMsg->findProp( 0x0e04 ) );
02247 QStringList attendees = QStringList::split( ';', s );
02248 if( attendees.count() ) {
02249 for( QStringList::Iterator it = attendees.begin();
02250 it != attendees.end(); ++it ) {
02251
02252
02253 if( (*it).find('@') == -1 ) {
02254 s = (*it).stripWhiteSpace();
02255
02256 Attendee *attendee = new Attendee( s, s, true );
02257 if( bIsReply ) {
02258 if( bCompatMethodAccepted )
02259 attendee->setStatus( Attendee::Accepted );
02260 if( bCompatMethodDeclined )
02261 attendee->setStatus( Attendee::Declined );
02262 if( bCompatMethodAcceptedCond )
02263 attendee->setStatus(Attendee::Tentative);
02264 } else {
02265 attendee->setStatus( Attendee::NeedsAction );
02266 attendee->setRole( Attendee::ReqParticipant );
02267 }
02268 event->addAttendee(attendee);
02269 }
02270 }
02271 } else {
02272
02273
02274 s = sSenderSearchKeyEmail;
02275 if( !s.isEmpty() ) {
02276 Attendee *attendee = new Attendee( QString::null, QString::null,
02277 true );
02278 if( bIsReply ) {
02279 if( bCompatMethodAccepted )
02280 attendee->setStatus( Attendee::Accepted );
02281 if( bCompatMethodAcceptedCond )
02282 attendee->setStatus( Attendee::Declined );
02283 if( bCompatMethodDeclined )
02284 attendee->setStatus( Attendee::Tentative );
02285 } else {
02286 attendee->setStatus(Attendee::NeedsAction);
02287 attendee->setRole(Attendee::ReqParticipant);
02288 }
02289 event->addAttendee(attendee);
02290 }
02291 }
02292 s = tnefMsg->findProp( 0x0c1f );
02293 if( s.isEmpty() && !bIsReply )
02294 s = sSenderSearchKeyEmail;
02295
02296 if( !s.isEmpty() )
02297 event->setOrganizer( s );
02298
02299 s = tnefMsg->findProp( 0x8516 ).replace( QChar( '-' ), QString::null )
02300 .replace( QChar( ':' ), QString::null );
02301 event->setDtStart( QDateTime::fromString( s ) );
02302
02303 s = tnefMsg->findProp( 0x8517 ).replace( QChar( '-' ), QString::null )
02304 .replace( QChar( ':' ), QString::null );
02305 event->setDtEnd( QDateTime::fromString( s ) );
02306
02307 s = tnefMsg->findProp( 0x8208 );
02308 event->setLocation( s );
02309
02310
02311
02312
02313
02314
02315 s = tnefMsg->findProp( 0x0023 );
02316 event->setUid( s );
02317
02318
02319
02320
02321 s = tnefMsg->findProp( 0x8202 ).replace( QChar( '-' ), QString::null )
02322 .replace( QChar( ':' ), QString::null );
02323
02324
02325
02326 s = tnefMsg->findNamedProp( "Keywords" );
02327 event->setCategories( s );
02328
02329 s = tnefMsg->findProp( 0x1000 );
02330 event->setDescription( s );
02331
02332 s = tnefMsg->findProp( 0x0070 );
02333 event->setSummary( s );
02334
02335 s = tnefMsg->findProp( 0x0026 );
02336 event->setPriority( s.toInt() );
02337
02338
02339 if(!tnefMsg->findProp(0x8503).isEmpty()) {
02340 Alarm *alarm = new Alarm(event);
02341 QDateTime highNoonTime =
02342 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8502 )
02343 .replace( QChar( '-' ), "" )
02344 .replace( QChar( ':' ), "" ) );
02345 QDateTime wakeMeUpTime =
02346 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8560, "" )
02347 .replace( QChar( '-' ), "" )
02348 .replace( QChar( ':' ), "" ) );
02349 alarm->setTime(wakeMeUpTime);
02350
02351 if( highNoonTime.isValid() && wakeMeUpTime.isValid() )
02352 alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) );
02353 else
02354
02355 alarm->setStartOffset( Duration( 15*60 ) );
02356 alarm->setDisplayAlarm( i18n( "Reminder" ) );
02357
02358
02359
02360 event->addAlarm( alarm );
02361 }
02362 cal.addEvent( event );
02363 bOk = true;
02364
02365 } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) {
02366 addressee.setUid( stringProp( tnefMsg, attMSGID ) );
02367 addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) );
02368 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true );
02369 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false );
02370 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false );
02371 addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) );
02372 addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) );
02373 addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) );
02374 addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) );
02375 addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) );
02376 addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) );
02377 addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) );
02378
02379 QString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY )
02380 .replace( QChar( '-' ), QString::null )
02381 .replace( QChar( ':' ), QString::null );
02382 if( !s.isEmpty() )
02383 addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s );
02384
02385 addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE ) ) );
02386
02387
02388 addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) );
02389 addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) );
02390 addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) );
02391 addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) );
02392 addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) );
02393
02394 addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) );
02395 addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) );
02396 addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) );
02397
02398
02399
02400
02401
02402 KABC::Address adr;
02403 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) );
02404 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) );
02405 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) );
02406 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) );
02407 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) );
02408 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) );
02409 adr.setType(KABC::Address::Home);
02410 addressee.insertAddress(adr);
02411
02412 adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) );
02413 adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) );
02414 adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) );
02415 adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) );
02416 adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) );
02417 adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) );
02418 adr.setType( KABC::Address::Work );
02419 addressee.insertAddress( adr );
02420
02421 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) );
02422 adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) );
02423 adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) );
02424 adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) );
02425 adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) );
02426 adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) );
02427 adr.setType( KABC::Address::Dom );
02428 addressee.insertAddress(adr);
02429
02430
02431
02432
02433
02434 QString nr;
02435 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER );
02436 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) );
02437 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER );
02438 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) );
02439 nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER );
02440 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) );
02441 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER );
02442 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) );
02443 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER );
02444 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) );
02445
02446 s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY )
02447 .replace( QChar( '-' ), QString::null )
02448 .replace( QChar( ':' ), QString::null );
02449 if( !s.isEmpty() )
02450 addressee.setBirthday( QDateTime::fromString( s ) );
02451
02452 bOk = ( !addressee.isEmpty() );
02453 } else if( "IPM.NOTE" == msgClass ) {
02454
02455 }
02456 }
02457 }
02458
02459
02460 QString iCal = calFormat.toString( &cal );
02461 if( !iCal.isEmpty() )
02462
02463 return iCal;
02464
02465
02466 KABC::VCardConverter converter;
02467 return converter.createVCard( addressee );
02468 }
02469
02470
02471 QString IncidenceFormatter::formatTNEFInvitation( const QByteArray& tnef,
02472 Calendar *mCalendar, InvitationFormatterHelper *helper )
02473 {
02474 QString vPart = IncidenceFormatter::msTNEFToVPart( tnef );
02475 QString iCal = IncidenceFormatter::formatICalInvitation( vPart, mCalendar, helper );
02476 if( !iCal.isEmpty() )
02477 return iCal;
02478 return vPart;
02479 }
02480
02481
02482
02483
02484
02485
02486
02487
02488 class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
02489 {
02490 public:
02491 ToolTipVisitor()
02492 : mCalendar( 0 ), mRichText( true ), mResult( "" ) {}
02493
02494 bool act( Calendar *calendar, IncidenceBase *incidence,
02495 const QDate &date=QDate(), bool richText=true )
02496 {
02497 mCalendar = calendar;
02498 mDate = date;
02499 mRichText = richText;
02500 mResult = "";
02501 return incidence ? incidence->accept( *this ) : false;
02502 }
02503 QString result() const { return mResult; }
02504
02505 protected:
02506 bool visit( Event *event );
02507 bool visit( Todo *todo );
02508 bool visit( Journal *journal );
02509 bool visit( FreeBusy *fb );
02510
02511 QString dateRangeText( Event *event, const QDate &date );
02512 QString dateRangeText( Todo *todo, const QDate &date );
02513 QString dateRangeText( Journal *journal );
02514 QString dateRangeText( FreeBusy *fb );
02515
02516 QString generateToolTip( Incidence* incidence, QString dtRangeText );
02517
02518 protected:
02519 Calendar *mCalendar;
02520 QDate mDate;
02521 bool mRichText;
02522 QString mResult;
02523 };
02524
02525 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const QDate &date )
02526 {
02527 QString ret;
02528 QString tmp;
02529
02530 QDateTime startDt = event->dtStart();
02531 QDateTime endDt = event->dtEnd();
02532 if ( event->doesRecur() ) {
02533 if ( date.isValid() ) {
02534 QDateTime dt( date, QTime( 0, 0, 0 ) );
02535 int diffDays = startDt.daysTo( dt );
02536 dt = dt.addSecs( -1 );
02537 startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() );
02538 if ( event->hasEndDate() ) {
02539 endDt = endDt.addDays( diffDays );
02540 if ( startDt > endDt ) {
02541 startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() );
02542 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
02543 }
02544 }
02545 }
02546 }
02547 if ( event->isMultiDay() ) {
02548
02549 tmp = "<br>" + i18n("Event start", "<i>From:</i> %1");
02550 if (event->doesFloat())
02551 ret += tmp.arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") );
02552 else
02553 ret += tmp.arg( IncidenceFormatter::dateToString( startDt ).replace(" ", " ") );
02554
02555 tmp = "<br>" + i18n("Event end","<i>To:</i> %1");
02556 if (event->doesFloat())
02557 ret += tmp.arg( IncidenceFormatter::dateToString( endDt, false ).replace(" ", " ") );
02558 else
02559 ret += tmp.arg( IncidenceFormatter::dateToString( endDt ).replace(" ", " ") );
02560
02561 } else {
02562
02563 ret += "<br>"+i18n("<i>Date:</i> %1").
02564 arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") );
02565 if ( !event->doesFloat() ) {
02566 const QString dtStartTime =
02567 IncidenceFormatter::timeToString( startDt, true ).replace( " ", " " );
02568 const QString dtEndTime =
02569 IncidenceFormatter::timeToString( endDt, true ).replace( " ", " " );
02570 if ( dtStartTime == dtEndTime ) {
02571 tmp = "<br>" + i18n("time for event, to prevent ugly line breaks",
02572 "<i>Time:</i> %1").
02573 arg( dtStartTime );
02574 } else {
02575 tmp = "<br>" + i18n("time range for event, to prevent ugly line breaks",
02576 "<i>Time:</i> %1 - %2").
02577 arg( dtStartTime, dtEndTime );
02578 }
02579 ret += tmp;
02580 }
02581
02582 }
02583 return ret;
02584 }
02585
02586 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const QDate &date )
02587 {
02588 QString ret;
02589 bool floats( todo->doesFloat() );
02590
02591 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
02592 QDateTime startDt = todo->dtStart();
02593 if ( todo->doesRecur() ) {
02594 if ( date.isValid() ) {
02595 startDt.setDate( date );
02596 }
02597 }
02598 ret += "<br>" +
02599 i18n("<i>Start:</i> %1").
02600 arg( IncidenceFormatter::dateTimeToString( startDt, floats, false ).
02601 replace( " ", " " ) );
02602 }
02603
02604 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
02605 QDateTime dueDt = todo->dtDue();
02606 if ( todo->doesRecur() ) {
02607 if ( date.isValid() ) {
02608 dueDt.addDays( todo->dtDue().date().daysTo( date ) );
02609 }
02610 }
02611 ret += "<br>" +
02612 i18n("<i>Due:</i> %1").
02613 arg( IncidenceFormatter::dateTimeToString( todo->dtDue(), floats, false ).
02614 replace( " ", " " ) );
02615 }
02616
02617 if ( todo->isCompleted() ) {
02618 ret += "<br>" +
02619 i18n("<i>Completed:</i> %1").
02620 arg( todo->completedStr().replace( " ", " " ) );
02621 } else {
02622 ret += "<br>" +
02623 i18n( "%1% completed" ).arg(todo->percentComplete() );
02624 }
02625
02626 return ret;
02627 }
02628
02629 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal )
02630 {
02631 QString ret;
02632 if (journal->dtStart().isValid() ) {
02633 ret += "<br>" +
02634 i18n("<i>Date:</i> %1").
02635 arg( IncidenceFormatter::dateToString( journal->dtStart(), false ) );
02636 }
02637 return ret;
02638 }
02639
02640 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
02641 {
02642 QString tmp( "<br>" + i18n("<i>Period start:</i> %1") );
02643 QString ret = tmp.arg( KGlobal::locale()->formatDateTime( fb->dtStart() ) );
02644 tmp = "<br>" + i18n("<i>Period start:</i> %1");
02645 ret += tmp.arg( KGlobal::locale()->formatDateTime( fb->dtEnd() ) );
02646 return ret;
02647 }
02648
02649
02650
02651 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
02652 {
02653 mResult = generateToolTip( event, dateRangeText( event, mDate ) );
02654 return !mResult.isEmpty();
02655 }
02656
02657 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
02658 {
02659 mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
02660 return !mResult.isEmpty();
02661 }
02662
02663 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
02664 {
02665 mResult = generateToolTip( journal, dateRangeText( journal ) );
02666 return !mResult.isEmpty();
02667 }
02668
02669 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
02670 {
02671 mResult = "<qt><b>" + i18n("Free/Busy information for %1")
02672 .arg(fb->organizer().fullName()) + "</b>";
02673 mResult += dateRangeText( fb );
02674 mResult += "</qt>";
02675 return !mResult.isEmpty();
02676 }
02677
02678 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, QString dtRangeText )
02679 {
02680 if ( !incidence )
02681 return QString::null;
02682
02683 QString tmp = "<qt><b>"+ incidence->summary().replace("\n", "<br>")+"</b>";
02684
02685 if ( mCalendar ) {
02686 QString calStr = IncidenceFormatter::resourceString( mCalendar, incidence );
02687 if ( !calStr.isEmpty() ) {
02688 tmp += "<br>" + i18n( "<i>Calendar:</i> %1" ).arg( calStr );
02689 }
02690 }
02691
02692 tmp += dtRangeText;
02693
02694 if (!incidence->location().isEmpty()) {
02695 tmp += "<br>"+i18n("<i>Location:</i> %1").
02696 arg( incidence->location().replace("\n", "<br>") );
02697 }
02698 if (!incidence->description().isEmpty()) {
02699 QString desc(incidence->description());
02700 if (desc.length()>120) {
02701 desc = desc.left(120) + "...";
02702 }
02703 tmp += "<br>----------<br>" + i18n("<i>Description:</i><br>") + desc.replace("\n", "<br>");
02704 }
02705 tmp += "</qt>";
02706 return tmp;
02707 }
02708
02709 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText )
02710 {
02711 return toolTipStr( 0, incidence, QDate(), richText );
02712 }
02713
02714 QString IncidenceFormatter::toolTipStr( Calendar *calendar,
02715 IncidenceBase *incidence,
02716 const QDate &date,
02717 bool richText )
02718 {
02719 ToolTipVisitor v;
02720 if ( v.act( calendar, incidence, date, richText ) ) {
02721 return v.result();
02722 } else {
02723 return QString::null;
02724 }
02725 }
02726
02727
02728
02729
02730
02731 class IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor
02732 {
02733 public:
02734 MailBodyVisitor() : mResult( "" ) {}
02735
02736 bool act( IncidenceBase *incidence )
02737 {
02738 mResult = "";
02739 return incidence ? incidence->accept( *this ) : false;
02740 }
02741 QString result() const { return mResult; }
02742
02743 protected:
02744 bool visit( Event *event );
02745 bool visit( Todo *todo );
02746 bool visit( Journal *journal );
02747 bool visit( FreeBusy * ) { mResult = i18n("This is a Free Busy Object"); return !mResult.isEmpty(); }
02748 protected:
02749 QString mResult;
02750 };
02751
02752
02753 static QString mailBodyIncidence( Incidence *incidence )
02754 {
02755 QString body;
02756 if ( !incidence->summary().isEmpty() ) {
02757 body += i18n("Summary: %1\n").arg( incidence->summary() );
02758 }
02759 if ( !incidence->organizer().isEmpty() ) {
02760 body += i18n("Organizer: %1\n").arg( incidence->organizer().fullName() );
02761 }
02762 if ( !incidence->location().isEmpty() ) {
02763 body += i18n("Location: %1\n").arg( incidence->location() );
02764 }
02765 return body;
02766 }
02767
02768 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
02769 {
02770 QString recurrence[]= {i18n("no recurrence", "None"),
02771 i18n("Minutely"), i18n("Hourly"), i18n("Daily"),
02772 i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"),
02773 i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
02774
02775 mResult = mailBodyIncidence( event );
02776 mResult += i18n("Start Date: %1\n").
02777 arg( IncidenceFormatter::dateToString( event->dtStart(), true ) );
02778 if ( !event->doesFloat() ) {
02779 mResult += i18n("Start Time: %1\n").
02780 arg( IncidenceFormatter::timeToString( event->dtStart(), true ) );
02781 }
02782 if ( event->dtStart() != event->dtEnd() ) {
02783 mResult += i18n("End Date: %1\n").
02784 arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) );
02785 }
02786 if ( !event->doesFloat() ) {
02787 mResult += i18n("End Time: %1\n").
02788 arg( IncidenceFormatter::timeToString( event->dtEnd(), true ) );
02789 }
02790 if ( event->doesRecur() ) {
02791 Recurrence *recur = event->recurrence();
02792
02793 mResult += i18n("Recurs: %1\n")
02794 .arg( recurrence[ recur->recurrenceType() ] );
02795 mResult += i18n("Frequency: %1\n")
02796 .arg( event->recurrence()->frequency() );
02797
02798 if ( recur->duration() > 0 ) {
02799 mResult += i18n ("Repeats once", "Repeats %n times", recur->duration());
02800 mResult += '\n';
02801 } else {
02802 if ( recur->duration() != -1 ) {
02803
02804 QString endstr;
02805 if ( event->doesFloat() ) {
02806 endstr = KGlobal::locale()->formatDate( recur->endDate() );
02807 } else {
02808 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime() );
02809 }
02810 mResult += i18n("Repeat until: %1\n").arg( endstr );
02811 } else {
02812 mResult += i18n("Repeats forever\n");
02813 }
02814 }
02815 }
02816 QString details = event->description();
02817 if ( !details.isEmpty() ) {
02818 mResult += i18n("Details:\n%1\n").arg( details );
02819 }
02820 return !mResult.isEmpty();
02821 }
02822
02823 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
02824 {
02825 mResult = mailBodyIncidence( todo );
02826
02827 if ( todo->hasStartDate() ) {
02828 mResult += i18n("Start Date: %1\n").
02829 arg( IncidenceFormatter::dateToString( todo->dtStart( false ), true ) );
02830 if ( !todo->doesFloat() ) {
02831 mResult += i18n("Start Time: %1\n").
02832 arg( IncidenceFormatter::timeToString( todo->dtStart( false ),true ) );
02833 }
02834 }
02835 if ( todo->hasDueDate() ) {
02836 mResult += i18n("Due Date: %1\n").
02837 arg( IncidenceFormatter::dateToString( todo->dtDue(), true ) );
02838 if ( !todo->doesFloat() ) {
02839 mResult += i18n("Due Time: %1\n").
02840 arg( IncidenceFormatter::timeToString( todo->dtDue(), true ) );
02841 }
02842 }
02843 QString details = todo->description();
02844 if ( !details.isEmpty() ) {
02845 mResult += i18n("Details:\n%1\n").arg( details );
02846 }
02847 return !mResult.isEmpty();
02848 }
02849
02850 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
02851 {
02852 mResult = mailBodyIncidence( journal );
02853 mResult += i18n("Date: %1\n").
02854 arg( IncidenceFormatter::dateToString( journal->dtStart(), true ) );
02855 if ( !journal->doesFloat() ) {
02856 mResult += i18n("Time: %1\n").
02857 arg( IncidenceFormatter::timeToString( journal->dtStart(), true ) );
02858 }
02859 if ( !journal->description().isEmpty() )
02860 mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() );
02861 return !mResult.isEmpty();
02862 }
02863
02864
02865
02866 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
02867 {
02868 if ( !incidence )
02869 return QString::null;
02870
02871 MailBodyVisitor v;
02872 if ( v.act( incidence ) ) {
02873 return v.result();
02874 }
02875 return QString::null;
02876 }
02877
02878
02879
02880
02881
02882 QString IncidenceFormatter::recurrenceString(Incidence * incidence)
02883 {
02884 if ( !incidence->doesRecur() )
02885 return i18n( "No recurrence" );
02886
02887
02888 QStringList dayList;
02889 dayList.append( i18n( "31st Last" ) );
02890 dayList.append( i18n( "30th Last" ) );
02891 dayList.append( i18n( "29th Last" ) );
02892 dayList.append( i18n( "28th Last" ) );
02893 dayList.append( i18n( "27th Last" ) );
02894 dayList.append( i18n( "26th Last" ) );
02895 dayList.append( i18n( "25th Last" ) );
02896 dayList.append( i18n( "24th Last" ) );
02897 dayList.append( i18n( "23rd Last" ) );
02898 dayList.append( i18n( "22nd Last" ) );
02899 dayList.append( i18n( "21st Last" ) );
02900 dayList.append( i18n( "20th Last" ) );
02901 dayList.append( i18n( "19th Last" ) );
02902 dayList.append( i18n( "18th Last" ) );
02903 dayList.append( i18n( "17th Last" ) );
02904 dayList.append( i18n( "16th Last" ) );
02905 dayList.append( i18n( "15th Last" ) );
02906 dayList.append( i18n( "14th Last" ) );
02907 dayList.append( i18n( "13th Last" ) );
02908 dayList.append( i18n( "12th Last" ) );
02909 dayList.append( i18n( "11th Last" ) );
02910 dayList.append( i18n( "10th Last" ) );
02911 dayList.append( i18n( "9th Last" ) );
02912 dayList.append( i18n( "8th Last" ) );
02913 dayList.append( i18n( "7th Last" ) );
02914 dayList.append( i18n( "6th Last" ) );
02915 dayList.append( i18n( "5th Last" ) );
02916 dayList.append( i18n( "4th Last" ) );
02917 dayList.append( i18n( "3rd Last" ) );
02918 dayList.append( i18n( "2nd Last" ) );
02919 dayList.append( i18n( "last day of the month", "Last" ) );
02920 dayList.append( i18n( "unknown day of the month", "unknown" ) );
02921 dayList.append( i18n( "1st" ) );
02922 dayList.append( i18n( "2nd" ) );
02923 dayList.append( i18n( "3rd" ) );
02924 dayList.append( i18n( "4th" ) );
02925 dayList.append( i18n( "5th" ) );
02926
02927 QString recurString;
02928 const KCalendarSystem *calSys = KGlobal::locale()->calendar();;
02929
02930 Recurrence *recurs = incidence->recurrence();
02931 switch ( recurs->recurrenceType() ) {
02932
02933 case Recurrence::rNone:
02934 recurString = i18n( "no recurrence", "None" );
02935 break;
02936 case Recurrence::rDaily:
02937 recurString = i18n( "Every day", "Every %1 days", recurs->frequency() );
02938 break;
02939 case Recurrence::rWeekly:
02940 {
02941 QString dayNames;
02942
02943 int weekStart = KGlobal::locale()->weekStartDay();
02944 bool addSpace = false;
02945 for ( int i = 0; i < 7; ++i ) {
02946 if ( recurs->days().testBit( (i+weekStart+6)%7 )) {
02947 if (addSpace) dayNames.append(" ");
02948 dayNames.append( calSys->weekDayName( ((i+weekStart+6)%7)+1, true ) );
02949 addSpace=true;
02950 }
02951 }
02952 recurString = i18n( "Every week on %1",
02953 "Every %n weeks on %1",
02954 recurs->frequency()).arg( dayNames );
02955 break;
02956 }
02957 case Recurrence::rMonthlyPos:
02958 {
02959 KCal::RecurrenceRule::WDayPos rule = recurs->monthPositions()[0];
02960 recurString = i18n( "Every month on the %1 %2",
02961 "Every %n months on the %1 %2",
02962 recurs->frequency() ).arg(dayList[rule.pos() + 31]).arg(
02963 calSys->weekDayName( rule.day(),false ) );
02964 break;
02965 }
02966 case Recurrence::rMonthlyDay:
02967 {
02968 int days = recurs->monthDays()[0];
02969 if (days < 0) {
02970 recurString = i18n( "Every month on the %1 day",
02971 "Every %n months on the %1 day",
02972 recurs->frequency() ).arg( dayList[days + 31] );
02973 } else {
02974 recurString = i18n( "Every month on day %1",
02975 "Every %n months on day %1",
02976 recurs->frequency() ).arg( recurs->monthDays()[0] );
02977 }
02978 break;
02979 }
02980
02981 case Recurrence::rYearlyMonth:
02982 {
02983 recurString = i18n( "Every year on day %1 of %2",
02984 "Every %n years on day %1 of %2",
02985 recurs->frequency() )
02986 .arg(recurs->yearDates()[0])
02987 .arg(calSys->monthName( recurs->yearMonths()[0], recurs->startDate().year() ) );
02988 break;
02989 }
02990 case Recurrence::rYearlyPos:
02991 {
02992 KCal::RecurrenceRule::WDayPos rule = recurs->yearPositions()[0];
02993 recurString = i18n( "Every year on the %1 %2 of %3",
02994 "Every %n years on the %1 %2of %3",
02995 recurs->frequency()).arg( dayList[rule.pos() + 31] )
02996 .arg( calSys->weekDayName( rule.day(), false ))
02997 .arg( calSys->monthName( recurs->yearMonths()[0], recurs->startDate().year() ) );
02998 break;
02999 }
03000 case Recurrence::rYearlyDay:
03001 {
03002 recurString = i18n( "Every year on day %1",
03003 "Every %n years on day %1",
03004 recurs->frequency()).arg( recurs->yearDays()[0] );
03005 break;
03006 }
03007
03008 default:
03009 return i18n( "Incidence recurs" );
03010 }
03011
03012 return recurString;
03013 }
03014
03015 QString IncidenceFormatter::timeToString( const QDateTime &date, bool shortfmt )
03016 {
03017 return KGlobal::locale()->formatTime( date.time(), !shortfmt );
03018 }
03019
03020 QString IncidenceFormatter::dateToString( const QDateTime &date, bool shortfmt )
03021 {
03022 return
03023 KGlobal::locale()->formatDate( date.date(), shortfmt );
03024 }
03025
03026 QString IncidenceFormatter::dateTimeToString( const QDateTime &date,
03027 bool allDay, bool shortfmt )
03028 {
03029 if ( allDay ) {
03030 return dateToString( date, shortfmt );
03031 }
03032
03033 return KGlobal::locale()->formatDateTime( date, shortfmt );
03034 }
03035
03036 QString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence )
03037 {
03038 if ( !calendar || !incidence ) {
03039 return QString::null;
03040 }
03041
03042 CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar );
03043 if ( !calendarResource ) {
03044 return QString::null;
03045 }
03046
03047 ResourceCalendar *resourceCalendar = calendarResource->resource( incidence );
03048 if ( resourceCalendar ) {
03049 if ( !resourceCalendar->subresources().isEmpty() ) {
03050 QString subRes = resourceCalendar->subresourceIdentifier( incidence );
03051 if ( subRes.isEmpty() ) {
03052 return resourceCalendar->resourceName();
03053 } else {
03054 return resourceCalendar->labelForSubresource( subRes );
03055 }
03056 }
03057 return resourceCalendar->resourceName();
03058 }
03059
03060 return QString::null;
03061 }