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