00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "incidenceformatter.h"
00025
00026 #include <libkcal/attachment.h>
00027 #include <libkcal/event.h>
00028 #include <libkcal/todo.h>
00029 #include <libkcal/journal.h>
00030 #include <libkcal/calendar.h>
00031 #include <libkcal/calendarlocal.h>
00032 #include <libkcal/icalformat.h>
00033 #include <libkcal/freebusy.h>
00034 #include <libkcal/calendarresources.h>
00035
00036 #include <libemailfunctions/email.h>
00037
00038 #include <ktnef/ktnefparser.h>
00039 #include <ktnef/ktnefmessage.h>
00040 #include <ktnef/ktnefdefs.h>
00041 #include <kabc/phonenumber.h>
00042 #include <kabc/vcardconverter.h>
00043 #include <kabc/stdaddressbook.h>
00044
00045 #include <kapplication.h>
00046 #include <kemailsettings.h>
00047
00048 #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 #include <qregexp.h>
00058
00059 #include <time.h>
00060
00061 using namespace KCal;
00062
00063
00064
00065
00066
00067 static QString htmlAddLink( const QString &ref, const QString &text,
00068 bool newline = true )
00069 {
00070 QString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
00071 if ( newline ) tmpStr += "\n";
00072 return tmpStr;
00073 }
00074
00075 static QString htmlAddTag( const QString & tag, const QString & text )
00076 {
00077 int numLineBreaks = text.contains( "\n" );
00078 QString str = "<" + tag + ">";
00079 QString tmpText = text;
00080 QString tmpStr = str;
00081 if( numLineBreaks >= 0 ) {
00082 if ( numLineBreaks > 0) {
00083 int pos = 0;
00084 QString tmp;
00085 for( int i = 0; i <= numLineBreaks; i++ ) {
00086 pos = tmpText.find( "\n" );
00087 tmp = tmpText.left( pos );
00088 tmpText = tmpText.right( tmpText.length() - pos - 1 );
00089 tmpStr += tmp + "<br>";
00090 }
00091 } else {
00092 tmpStr += tmpText;
00093 }
00094 }
00095 tmpStr += "</" + tag + ">";
00096 return tmpStr;
00097 }
00098
00099 static bool iamAttendee( Attendee *attendee )
00100 {
00101
00102
00103 bool iam = false;
00104 KEMailSettings settings;
00105 QStringList profiles = settings.profiles();
00106 for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00107 settings.setProfile( *it );
00108 if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
00109 iam = true;
00110 break;
00111 }
00112 }
00113 return iam;
00114 }
00115
00116 static bool iamOrganizer( Incidence *incidence )
00117 {
00118
00119
00120 if ( !incidence ) {
00121 return false;
00122 }
00123
00124 bool iam = false;
00125 KEMailSettings settings;
00126 QStringList profiles = settings.profiles();
00127 for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
00128 settings.setProfile( *it );
00129 if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) {
00130 iam = true;
00131 break;
00132 }
00133 }
00134 return iam;
00135 }
00136
00137 static bool senderIsOrganizer( Incidence *incidence, const QString &sender )
00138 {
00139
00140
00141 if ( !incidence || sender.isEmpty() ) {
00142 return true;
00143 }
00144
00145 bool isorg = true;
00146 QString senderName, senderEmail;
00147 if ( KPIM::getNameAndMail( sender, senderName, senderEmail ) ) {
00148
00149 if ( incidence->organizer().email() != senderEmail &&
00150 incidence->organizer().name() != senderName ) {
00151 isorg = false;
00152 }
00153 }
00154 return isorg;
00155 }
00156
00157 static QString firstAttendeeName( Incidence *incidence, const QString &defName )
00158 {
00159 QString name;
00160 if ( !incidence ) {
00161 return name;
00162 }
00163
00164 Attendee::List attendees = incidence->attendees();
00165 if( attendees.count() > 0 ) {
00166 Attendee *attendee = *attendees.begin();
00167 name = attendee->name();
00168 if ( name.isEmpty() ) {
00169 name = attendee->email();
00170 }
00171 if ( name.isEmpty() ) {
00172 name = defName;
00173 }
00174 }
00175 return name;
00176 }
00177
00178
00179
00180
00181
00182 static QString displayViewLinkPerson( const QString& email, QString name, QString uid )
00183 {
00184
00185
00186 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00187 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00188 KABC::Addressee::List addressList = add_book->findByEmail( email );
00189 if ( !addressList.isEmpty() ) {
00190 KABC::Addressee o = addressList.first();
00191 if ( !o.isEmpty() && addressList.size() < 2 ) {
00192 if ( name.isEmpty() ) {
00193
00194 name = o.formattedName();
00195 }
00196 uid = o.uid();
00197 } else {
00198
00199 uid = QString::null;
00200 }
00201 }
00202 }
00203
00204
00205 QString tmpString;
00206 if ( !uid.isEmpty() ) {
00207
00208 if ( name.isEmpty() ) {
00209
00210 tmpString += htmlAddLink( "uid:" + uid, email );
00211 } else {
00212 tmpString += htmlAddLink( "uid:" + uid, name );
00213 }
00214 } else {
00215
00216 tmpString += ( name.isEmpty() ? email : name );
00217 }
00218
00219
00220 if ( !email.isEmpty() ) {
00221 KURL mailto;
00222 mailto.setProtocol( "mailto" );
00223 mailto.setPath( email );
00224 const QString iconPath =
00225 KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small );
00226 tmpString += " " +
00227 htmlAddLink( mailto.url(),
00228 "<img valign=\"top\" src=\"" + iconPath + "\">" );
00229 }
00230
00231 return tmpString;
00232 }
00233
00234 static QString displayViewFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
00235 {
00236 QString tmpStr;
00237 Attendee::List::ConstIterator it;
00238 Attendee::List attendees = incidence->attendees();
00239
00240 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00241 Attendee *a = *it;
00242 if ( a->role() != role ) {
00243
00244 continue;
00245 }
00246 if ( a->email() == incidence->organizer().email() ) {
00247
00248 continue;
00249 }
00250 tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid() );
00251 if ( !a->delegator().isEmpty() ) {
00252 tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
00253 }
00254 if ( !a->delegate().isEmpty() ) {
00255 tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
00256 }
00257 tmpStr += "<br>";
00258 }
00259 if ( tmpStr.endsWith( "<br>" ) ) {
00260 tmpStr.truncate( tmpStr.length() - 4 );
00261 }
00262 return tmpStr;
00263 }
00264
00265 static QString displayViewFormatAttendees( Incidence *incidence )
00266 {
00267 QString tmpStr, str;
00268
00269
00270 int attendeeCount = incidence->attendees().count();
00271 if ( attendeeCount > 1 ||
00272 ( attendeeCount == 1 &&
00273 incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
00274 tmpStr += "<tr>";
00275 tmpStr += "<td><b>" + i18n( "Organizer:" ) + "</b></td>";
00276 tmpStr += "<td>" +
00277 displayViewLinkPerson( incidence->organizer().email(),
00278 incidence->organizer().name(),
00279 QString::null ) +
00280 "</td>";
00281 tmpStr += "</tr>";
00282 }
00283
00284
00285 str = displayViewFormatAttendeeRoleList( incidence, Attendee::Chair );
00286 if ( !str.isEmpty() ) {
00287 tmpStr += "<tr>";
00288 tmpStr += "<td><b>" + i18n( "Chair:" ) + "</b></td>";
00289 tmpStr += "<td>" + str + "</td>";
00290 tmpStr += "</tr>";
00291 }
00292
00293
00294 str = displayViewFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
00295 if ( !str.isEmpty() ) {
00296 tmpStr += "<tr>";
00297 tmpStr += "<td><b>" + i18n( "Required Participants:" ) + "</b></td>";
00298 tmpStr += "<td>" + str + "</td>";
00299 tmpStr += "</tr>";
00300 }
00301
00302
00303 str = displayViewFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
00304 if ( !str.isEmpty() ) {
00305 tmpStr += "<tr>";
00306 tmpStr += "<td><b>" + i18n( "Optional Participants:" ) + "</b></td>";
00307 tmpStr += "<td>" + str + "</td>";
00308 tmpStr += "</tr>";
00309 }
00310
00311
00312 str = displayViewFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
00313 if ( !str.isEmpty() ) {
00314 tmpStr += "<tr>";
00315 tmpStr += "<td><b>" + i18n( "Observers:" ) + "</b></td>";
00316 tmpStr += "<td>" + str + "</td>";
00317 tmpStr += "</tr>";
00318 }
00319
00320 return tmpStr;
00321 }
00322
00323 static QString displayViewFormatAttachments( Incidence *incidence )
00324 {
00325 QString tmpStr;
00326 Attachment::List as = incidence->attachments();
00327 Attachment::List::ConstIterator it;
00328 uint count = 0;
00329 for( it = as.begin(); it != as.end(); ++it ) {
00330 count++;
00331 if ( (*it)->isUri() ) {
00332 QString name;
00333 if ( (*it)->uri().startsWith( "kmail:" ) ) {
00334 name = i18n( "Show mail" );
00335 } else {
00336 if ( (*it)->label().isEmpty() ) {
00337 name = (*it)->uri();
00338 } else {
00339 name = (*it)->label();
00340 }
00341 }
00342 tmpStr += htmlAddLink( (*it)->uri(), name );
00343 } else {
00344 tmpStr += htmlAddLink( "ATTACH:" + incidence->uid() + ':' + (*it)->label(),
00345 (*it)->label(), false );
00346 }
00347 if ( count < as.count() ) {
00348 tmpStr += "<br>";
00349 }
00350 }
00351 return tmpStr;
00352 }
00353
00354 static QString displayViewFormatCategories( Incidence *incidence )
00355 {
00356
00357 return incidence->categories().join( ", " );
00358 }
00359
00360 static QString displayViewFormatCreationDate( Incidence *incidence )
00361 {
00362 return i18n( "Creation date: %1" ).
00363 arg( IncidenceFormatter::dateTimeToString( incidence->created(), false, true ) );
00364 }
00365
00366 static QString displayViewFormatBirthday( Event *event )
00367 {
00368 if ( !event ) {
00369 return QString::null;
00370 }
00371 if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) {
00372 return QString::null;
00373 }
00374
00375 QString uid = event->customProperty("KABC","UID-1");
00376 QString name = event->customProperty("KABC","NAME-1");
00377 QString email= event->customProperty("KABC","EMAIL-1");
00378
00379 QString tmpStr = displayViewLinkPerson( email, name, uid );
00380
00381 if ( event->customProperty( "KABC", "ANNIVERSARY") == "YES" ) {
00382 uid = event->customProperty("KABC","UID-2");
00383 name = event->customProperty("KABC","NAME-2");
00384 email= event->customProperty("KABC","EMAIL-2");
00385 tmpStr += "<br>";
00386 tmpStr += displayViewLinkPerson( email, name, uid );
00387 }
00388
00389 return tmpStr;
00390 }
00391
00392 static QString displayViewFormatHeader( Incidence *incidence )
00393 {
00394 QString tmpStr = "<table><tr>";
00395
00396
00397 {
00398 tmpStr += "<td>";
00399
00400 if ( incidence->type() == "Event" ) {
00401 QString iconPath;
00402 if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00403 if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00404 iconPath =
00405 KGlobal::iconLoader()->iconPath( "calendaranniversary", KIcon::Small );
00406 } else {
00407 iconPath = KGlobal::iconLoader()->iconPath( "calendarbirthday", KIcon::Small );
00408 }
00409 } else {
00410 iconPath = KGlobal::iconLoader()->iconPath( "appointment", KIcon::Small );
00411 }
00412 tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
00413 }
00414 if ( incidence->type() == "Todo" ) {
00415 tmpStr += "<img valign=\"top\" src=\"" +
00416 KGlobal::iconLoader()->iconPath( "todo", KIcon::Small ) +
00417 "\">";
00418 }
00419 if ( incidence->type() == "Journal" ) {
00420 tmpStr += "<img valign=\"top\" src=\"" +
00421 KGlobal::iconLoader()->iconPath( "journal", KIcon::Small ) +
00422 "\">";
00423 }
00424 if ( incidence->isAlarmEnabled() ) {
00425 tmpStr += "<img valign=\"top\" src=\"" +
00426 KGlobal::iconLoader()->iconPath( "bell", KIcon::Small ) +
00427 "\">";
00428 }
00429 if ( incidence->doesRecur() ) {
00430 tmpStr += "<img valign=\"top\" src=\"" +
00431 KGlobal::iconLoader()->iconPath( "recur", KIcon::Small ) +
00432 "\">";
00433 }
00434 if ( incidence->isReadOnly() ) {
00435 tmpStr += "<img valign=\"top\" src=\"" +
00436 KGlobal::iconLoader()->iconPath( "readonlyevent", KIcon::Small ) +
00437 "\">";
00438 }
00439
00440 tmpStr += "</td>";
00441 }
00442
00443 tmpStr += "<td>";
00444 tmpStr += "<b><u>" + incidence->summary() + "</u></b>";
00445 tmpStr += "</td>";
00446
00447 tmpStr += "</tr></table>";
00448
00449 return tmpStr;
00450 }
00451
00452 static QString displayViewFormatEvent( Calendar *calendar, Event *event,
00453 const QDate &date )
00454 {
00455 if ( !event ) {
00456 return QString::null;
00457 }
00458
00459 QString tmpStr = displayViewFormatHeader( event );
00460
00461 tmpStr += "<table>";
00462 tmpStr += "<col width=\"25%\"/>";
00463 tmpStr += "<col width=\"75%\"/>";
00464
00465 if ( calendar ) {
00466 QString calStr = IncidenceFormatter::resourceString( calendar, event );
00467 if ( !calStr.isEmpty() ) {
00468 tmpStr += "<tr>";
00469 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00470 tmpStr += "<td>" + calStr + "</td>";
00471 tmpStr += "</tr>";
00472 }
00473 }
00474
00475 if ( !event->location().isEmpty() ) {
00476 tmpStr += "<tr>";
00477 tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00478 tmpStr += "<td>" + event->location() + "</td>";
00479 tmpStr += "</tr>";
00480 }
00481
00482 QDateTime startDt = event->dtStart();
00483 QDateTime endDt = event->dtEnd();
00484 if ( event->doesRecur() ) {
00485 if ( date.isValid() ) {
00486 QDateTime dt( date, QTime( 0, 0, 0 ) );
00487 int diffDays = startDt.daysTo( dt );
00488 dt = dt.addSecs( -1 );
00489 startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() );
00490 if ( event->hasEndDate() ) {
00491 endDt = endDt.addDays( diffDays );
00492 if ( startDt > endDt ) {
00493 startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() );
00494 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
00495 }
00496 }
00497 }
00498 }
00499
00500 tmpStr += "<tr>";
00501 if ( event->doesFloat() ) {
00502 if ( event->isMultiDay() ) {
00503 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00504 tmpStr += "<td>" +
00505 i18n("<beginDate> - <endDate>","%1 - %2").
00506 arg( IncidenceFormatter::dateToString( startDt, false ) ).
00507 arg( IncidenceFormatter::dateToString( endDt, false ) ) +
00508 "</td>";
00509 } else {
00510 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00511 tmpStr += "<td>" +
00512 i18n("date as string","%1").
00513 arg( IncidenceFormatter::dateToString( startDt, false ) ) +
00514 "</td>";
00515 }
00516 } else {
00517 if ( event->isMultiDay() ) {
00518 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00519 tmpStr += "<td>" +
00520 i18n("<beginDate> - <endDate>","%1 - %2").
00521 arg( IncidenceFormatter::dateToString( startDt, false ) ).
00522 arg( IncidenceFormatter::dateToString( endDt, false ) ) +
00523 "</td>";
00524 } else {
00525 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00526 tmpStr += "<td>" +
00527 i18n("date as string","%1").
00528 arg( IncidenceFormatter::dateToString( startDt, false ) ) +
00529 "</td>";
00530
00531 tmpStr += "</tr><tr>";
00532 tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>";
00533 if ( event->hasEndDate() && startDt != endDt ) {
00534 tmpStr += "<td>" +
00535 i18n("<beginTime> - <endTime>","%1 - %2").
00536 arg( IncidenceFormatter::timeToString( startDt, true ) ).
00537 arg( IncidenceFormatter::timeToString( endDt, true ) ) +
00538 "</td>";
00539 } else {
00540 tmpStr += "<td>" +
00541 IncidenceFormatter::timeToString( startDt, true ) +
00542 "</td>";
00543 }
00544 }
00545 }
00546 tmpStr += "</tr>";
00547
00548 QString durStr = IncidenceFormatter::durationString( event );
00549 if ( !durStr.isEmpty() ) {
00550 tmpStr += "<tr>";
00551 tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
00552 tmpStr += "<td>" + durStr + "</td>";
00553 tmpStr += "</tr>";
00554 }
00555
00556 if ( event->doesRecur() ) {
00557 tmpStr += "<tr>";
00558 tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
00559 tmpStr += "<td>" +
00560 IncidenceFormatter::recurrenceString( event ) +
00561 "</td>";
00562 tmpStr += "</tr>";
00563 }
00564
00565 if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) {
00566 tmpStr += "<tr>";
00567 if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00568 tmpStr += "<td><b>" + i18n( "Anniversary:" ) + "</b></td>";
00569 } else {
00570 tmpStr += "<td><b>" + i18n( "Birthday:" ) + "</b></td>";
00571 }
00572 tmpStr += "<td>" + displayViewFormatBirthday( event ) + "</td>";
00573 tmpStr += "</tr>";
00574 tmpStr += "</table>";
00575 return tmpStr;
00576 }
00577
00578 if ( !event->description().isEmpty() ) {
00579 tmpStr += "<tr>";
00580 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00581 tmpStr += "<td>" + event->description() + "</td>";
00582 tmpStr += "</tr>";
00583 }
00584
00585
00586
00587 int reminderCount = event->alarms().count();
00588 if ( reminderCount > 0 && event->isAlarmEnabled() ) {
00589 tmpStr += "<tr>";
00590 tmpStr += "<td><b>" +
00591 i18n( "Reminder:", "%n Reminders:", reminderCount ) +
00592 "</b></td>";
00593 tmpStr += "<td>" + IncidenceFormatter::reminderStringList( event ).join( "<br>" ) + "</td>";
00594 tmpStr += "</tr>";
00595 }
00596
00597 tmpStr += displayViewFormatAttendees( event );
00598
00599 int categoryCount = event->categories().count();
00600 if ( categoryCount > 0 ) {
00601 tmpStr += "<tr>";
00602 tmpStr += "<td><b>" +
00603 i18n( "Category:", "%n Categories:", categoryCount ) +
00604 "</b></td>";
00605 tmpStr += "<td>" + displayViewFormatCategories( event ) + "</td>";
00606 tmpStr += "</tr>";
00607 }
00608
00609 int attachmentCount = event->attachments().count();
00610 if ( attachmentCount > 0 ) {
00611 tmpStr += "<tr>";
00612 tmpStr += "<td><b>" +
00613 i18n( "Attachment:", "%n Attachments:", attachmentCount ) +
00614 "</b></td>";
00615 tmpStr += "<td>" + displayViewFormatAttachments( event ) + "</td>";
00616 tmpStr += "</tr>";
00617 }
00618 tmpStr += "</table>";
00619
00620 tmpStr += "<em>" + displayViewFormatCreationDate( event ) + "</em>";
00621
00622 return tmpStr;
00623 }
00624
00625 static QString displayViewFormatTodo( Calendar *calendar, Todo *todo,
00626 const QDate &date )
00627 {
00628 if ( !todo ) {
00629 return QString::null;
00630 }
00631
00632 QString tmpStr = displayViewFormatHeader( todo );
00633
00634 tmpStr += "<table>";
00635 tmpStr += "<col width=\"25%\"/>";
00636 tmpStr += "<col width=\"75%\"/>";
00637
00638 if ( calendar ) {
00639 QString calStr = IncidenceFormatter::resourceString( calendar, todo );
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 if ( !todo->location().isEmpty() ) {
00649 tmpStr += "<tr>";
00650 tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00651 tmpStr += "<td>" + todo->location() + "</td>";
00652 tmpStr += "</tr>";
00653 }
00654
00655 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
00656 QDateTime startDt = todo->dtStart();
00657 if ( todo->doesRecur() ) {
00658 if ( date.isValid() ) {
00659 startDt.setDate( date );
00660 }
00661 }
00662 tmpStr += "<tr>";
00663 tmpStr += "<td><b>" + i18n( "Start:" ) + "</b></td>";
00664 tmpStr += "<td>" +
00665 IncidenceFormatter::dateTimeToString( startDt,
00666 todo->doesFloat(), false ) +
00667 "</td>";
00668 tmpStr += "</tr>";
00669 }
00670
00671 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
00672 QDateTime dueDt = todo->dtDue();
00673 if ( todo->doesRecur() ) {
00674 if ( date.isValid() ) {
00675 QDateTime dt( date, QTime( 0, 0, 0 ) );
00676 dt = dt.addSecs( -1 );
00677 dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() );
00678 }
00679 }
00680 tmpStr += "<tr>";
00681 tmpStr += "<td><b>" + i18n( "Due:" ) + "</b></td>";
00682 tmpStr += "<td>" +
00683 IncidenceFormatter::dateTimeToString( dueDt,
00684 todo->doesFloat(), false ) +
00685 "</td>";
00686 tmpStr += "</tr>";
00687 }
00688
00689 QString durStr = IncidenceFormatter::durationString( todo );
00690 if ( !durStr.isEmpty() ) {
00691 tmpStr += "<tr>";
00692 tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
00693 tmpStr += "<td>" + durStr + "</td>";
00694 tmpStr += "</tr>";
00695 }
00696
00697 if ( todo->doesRecur() ) {
00698 tmpStr += "<tr>";
00699 tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
00700 tmpStr += "<td>" +
00701 IncidenceFormatter::recurrenceString( todo ) +
00702 "</td>";
00703 tmpStr += "</tr>";
00704 }
00705
00706 if ( !todo->description().isEmpty() ) {
00707 tmpStr += "<tr>";
00708 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00709 tmpStr += "<td>" + todo->description() + "</td>";
00710 tmpStr += "</tr>";
00711 }
00712
00713
00714
00715 int reminderCount = todo->alarms().count();
00716 if ( reminderCount > 0 && todo->isAlarmEnabled() ) {
00717 tmpStr += "<tr>";
00718 tmpStr += "<td><b>" +
00719 i18n( "Reminder:", "%n Reminders:", reminderCount ) +
00720 "</b></td>";
00721 tmpStr += "<td>" + IncidenceFormatter::reminderStringList( todo ).join( "<br>" ) + "</td>";
00722 tmpStr += "</tr>";
00723 }
00724
00725 tmpStr += displayViewFormatAttendees( todo );
00726
00727 int categoryCount = todo->categories().count();
00728 if ( categoryCount > 0 ) {
00729 tmpStr += "<tr>";
00730 tmpStr += "<td><b>" +
00731 i18n( "Category:", "%n Categories:", categoryCount ) +
00732 "</b></td>";
00733 tmpStr += "<td>" + displayViewFormatCategories( todo ) + "</td>";
00734 tmpStr += "</tr>";
00735 }
00736
00737 if ( todo->priority() > 0 ) {
00738 tmpStr += "<tr>";
00739 tmpStr += "<td><b>" + i18n( "Priority:" ) + "</b></td>";
00740 tmpStr += "<td>";
00741 tmpStr += QString::number( todo->priority() );
00742 tmpStr += "</td>";
00743 tmpStr += "</tr>";
00744 }
00745
00746 tmpStr += "<tr>";
00747 if ( todo->isCompleted() ) {
00748 tmpStr += "<td><b>" + i18n( "Completed:" ) + "</b></td>";
00749 tmpStr += "<td>";
00750 tmpStr += todo->completedStr();
00751 } else {
00752 tmpStr += "<td><b>" + i18n( "Percent Done:" ) + "</b></td>";
00753 tmpStr += "<td>";
00754 tmpStr += i18n( "%1%" ).arg( todo->percentComplete() );
00755 }
00756 tmpStr += "</td>";
00757 tmpStr += "</tr>";
00758
00759 int attachmentCount = todo->attachments().count();
00760 if ( attachmentCount > 0 ) {
00761 tmpStr += "<tr>";
00762 tmpStr += "<td><b>" +
00763 i18n( "Attachment:", "Attachments:", attachmentCount ) +
00764 "</b></td>";
00765 tmpStr += "<td>" + displayViewFormatAttachments( todo ) + "</td>";
00766 tmpStr += "</tr>";
00767 }
00768
00769 tmpStr += "</table>";
00770
00771 tmpStr += "<em>" + displayViewFormatCreationDate( todo ) + "</em>";
00772
00773 return tmpStr;
00774 }
00775
00776 static QString displayViewFormatJournal( Calendar *calendar, Journal *journal )
00777 {
00778 if ( !journal ) {
00779 return QString::null;
00780 }
00781
00782 QString tmpStr = displayViewFormatHeader( journal );
00783
00784 tmpStr += "<table>";
00785 tmpStr += "<col width=\"25%\"/>";
00786 tmpStr += "<col width=\"75%\"/>";
00787
00788 if ( calendar ) {
00789 QString calStr = IncidenceFormatter::resourceString( calendar, journal );
00790 if ( !calStr.isEmpty() ) {
00791 tmpStr += "<tr>";
00792 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00793 tmpStr += "<td>" + calStr + "</td>";
00794 tmpStr += "</tr>";
00795 }
00796 }
00797
00798 tmpStr += "<tr>";
00799 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00800 tmpStr += "<td>" +
00801 IncidenceFormatter::dateToString( journal->dtStart(), false ) +
00802 "</td>";
00803 tmpStr += "</tr>";
00804
00805 if ( !journal->description().isEmpty() ) {
00806 tmpStr += "<tr>";
00807 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00808 tmpStr += "<td>" + journal->description() + "</td>";
00809 tmpStr += "</tr>";
00810 }
00811
00812 int categoryCount = journal->categories().count();
00813 if ( categoryCount > 0 ) {
00814 tmpStr += "<tr>";
00815 tmpStr += "<td><b>" +
00816 i18n( "Category:", "%n Categories:", categoryCount ) +
00817 "</b></td>";
00818 tmpStr += "<td>" + displayViewFormatCategories( journal ) + "</td>";
00819 tmpStr += "</tr>";
00820 }
00821 tmpStr += "</table>";
00822
00823 tmpStr += "<em>" + displayViewFormatCreationDate( journal ) + "</em>";
00824
00825 return tmpStr;
00826 }
00827
00828 static QString displayViewFormatFreeBusy( Calendar * , FreeBusy *fb )
00829 {
00830 if ( !fb ) {
00831 return QString::null;
00832 }
00833
00834 QString tmpStr = htmlAddTag( "h2",
00835 htmlAddTag( "b",
00836 i18n("Free/Busy information for %1").
00837 arg( fb->organizer().fullName() ) ) );
00838
00839 tmpStr += htmlAddTag( "h4", i18n("Busy times in date range %1 - %2:").
00840 arg( IncidenceFormatter::dateToString( fb->dtStart(), true ) ).
00841 arg( IncidenceFormatter::dateToString( fb->dtEnd(), true ) ) );
00842
00843 QValueList<Period> periods = fb->busyPeriods();
00844
00845 QString text = htmlAddTag( "em", htmlAddTag( "b", i18n("Busy:") ) );
00846 QValueList<Period>::iterator it;
00847 for ( it = periods.begin(); it != periods.end(); ++it ) {
00848 Period per = *it;
00849 if ( per.hasDuration() ) {
00850 int dur = per.duration().asSeconds();
00851 QString cont;
00852 if ( dur >= 3600 ) {
00853 cont += i18n("1 hour ", "%n hours ", dur / 3600 );
00854 dur %= 3600;
00855 }
00856 if ( dur >= 60 ) {
00857 cont += i18n("1 minute ", "%n minutes ", dur / 60);
00858 dur %= 60;
00859 }
00860 if ( dur > 0 ) {
00861 cont += i18n("1 second", "%n seconds", dur);
00862 }
00863 text += i18n("startDate for duration", "%1 for %2").
00864 arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ).
00865 arg( cont );
00866 text += "<br>";
00867 } else {
00868 if ( per.start().date() == per.end().date() ) {
00869 text += i18n("date, fromTime - toTime ", "%1, %2 - %3").
00870 arg( IncidenceFormatter::dateToString( per.start().date(), true ) ).
00871 arg( IncidenceFormatter::timeToString( per.start(), true ) ).
00872 arg( IncidenceFormatter::timeToString( per.end(), true ) );
00873 } else {
00874 text += i18n("fromDateTime - toDateTime", "%1 - %2").
00875 arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ).
00876 arg( IncidenceFormatter::dateTimeToString( per.end(), false, true ) );
00877 }
00878 text += "<br>";
00879 }
00880 }
00881 tmpStr += htmlAddTag( "p", text );
00882 return tmpStr;
00883 }
00884
00885 class IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor
00886 {
00887 public:
00888 EventViewerVisitor()
00889 : mCalendar( 0 ), mResult( "" ) {}
00890
00891 bool act( Calendar *calendar, IncidenceBase *incidence, const QDate &date )
00892 {
00893 mCalendar = calendar;
00894 mDate = date;
00895 mResult = "";
00896 return incidence->accept( *this );
00897 }
00898 QString result() const { return mResult; }
00899
00900 protected:
00901 bool visit( Event *event )
00902 {
00903 mResult = displayViewFormatEvent( mCalendar, event, mDate );
00904 return !mResult.isEmpty();
00905 }
00906 bool visit( Todo *todo )
00907 {
00908 mResult = displayViewFormatTodo( mCalendar, todo, mDate );
00909 return !mResult.isEmpty();
00910 }
00911 bool visit( Journal *journal )
00912 {
00913 mResult = displayViewFormatJournal( mCalendar, journal );
00914 return !mResult.isEmpty();
00915 }
00916 bool visit( FreeBusy *fb )
00917 {
00918 mResult = displayViewFormatFreeBusy( mCalendar, fb );
00919 return !mResult.isEmpty();
00920 }
00921
00922 protected:
00923 Calendar *mCalendar;
00924 QDate mDate;
00925 QString mResult;
00926 };
00927
00928 QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
00929 {
00930 return extensiveDisplayStr( 0, incidence, QDate() );
00931 }
00932
00933 QString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar,
00934 IncidenceBase *incidence,
00935 const QDate &date )
00936 {
00937 if ( !incidence ) {
00938 return QString::null;
00939 }
00940
00941 EventViewerVisitor v;
00942 if ( v.act( calendar, incidence, date ) ) {
00943 return v.result();
00944 } else {
00945 return QString::null;
00946 }
00947 }
00948
00949
00950
00951
00952
00953 static QString string2HTML( const QString& str )
00954 {
00955 return QStyleSheet::convertFromPlainText(str, QStyleSheetItem::WhiteSpaceNormal);
00956 }
00957
00958 static QString cleanHtml( const QString &html )
00959 {
00960 QRegExp rx( "<body[^>]*>(.*)</body>" );
00961 rx.setCaseSensitive( false );
00962 rx.search( html );
00963 QString body = rx.cap( 1 );
00964
00965 return QStyleSheet::escape( body.remove( QRegExp( "<[^>]*>" ) ).stripWhiteSpace() );
00966 }
00967
00968 static QString eventStartTimeStr( Event *event )
00969 {
00970 QString tmp;
00971 if ( !event->doesFloat() ) {
00972 tmp = i18n( "%1: Start Date, %2: Start Time", "%1 %2" ).
00973 arg( IncidenceFormatter::dateToString( event->dtStart(), true ),
00974 IncidenceFormatter::timeToString( event->dtStart(), true ) );
00975 } else {
00976 tmp = i18n( "%1: Start Date", "%1 (all day)" ).
00977 arg( IncidenceFormatter::dateToString( event->dtStart(), true ) );
00978 }
00979 return tmp;
00980 }
00981
00982 static QString eventEndTimeStr( Event *event )
00983 {
00984 QString tmp;
00985 if ( event->hasEndDate() && event->dtEnd().isValid() ) {
00986 if ( !event->doesFloat() ) {
00987 tmp = i18n( "%1: End Date, %2: End Time", "%1 %2" ).
00988 arg( IncidenceFormatter::dateToString( event->dtEnd(), true ),
00989 IncidenceFormatter::timeToString( event->dtEnd(), true ) );
00990 } else {
00991 tmp = i18n( "%1: End Date", "%1 (all day)" ).
00992 arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) );
00993 }
00994 }
00995 return tmp;
00996 }
00997
00998 static QString invitationRow( const QString &cell1, const QString &cell2 )
00999 {
01000 return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
01001 }
01002
01003 static Attendee *findDelegatedFromMyAttendee( Incidence *incidence )
01004 {
01005
01006
01007 Attendee *attendee = 0;
01008 if ( !incidence ) {
01009 return attendee;
01010 }
01011
01012 KEMailSettings settings;
01013 QStringList profiles = settings.profiles();
01014 for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
01015 settings.setProfile( *it );
01016
01017 QString delegatorName, delegatorEmail;
01018 Attendee::List attendees = incidence->attendees();
01019 Attendee::List::ConstIterator it2;
01020 for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) {
01021 Attendee *a = *it2;
01022 KPIM::getNameAndMail( a->delegator(), delegatorName, delegatorEmail );
01023 if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) {
01024 attendee = a;
01025 break;
01026 }
01027 }
01028 }
01029 return attendee;
01030 }
01031
01032 static Attendee *findMyAttendee( Incidence *incidence )
01033 {
01034
01035
01036 Attendee *attendee = 0;
01037 if ( !incidence ) {
01038 return attendee;
01039 }
01040
01041 KEMailSettings settings;
01042 QStringList profiles = settings.profiles();
01043 for( QStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) {
01044 settings.setProfile( *it );
01045
01046 Attendee::List attendees = incidence->attendees();
01047 Attendee::List::ConstIterator it2;
01048 for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) {
01049 Attendee *a = *it2;
01050 if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
01051 attendee = a;
01052 break;
01053 }
01054 }
01055 }
01056 return attendee;
01057 }
01058
01059 static Attendee *findAttendee( Incidence *incidence, const QString &email )
01060 {
01061
01062
01063 Attendee *attendee = 0;
01064 if ( !incidence ) {
01065 return attendee;
01066 }
01067
01068 Attendee::List attendees = incidence->attendees();
01069 Attendee::List::ConstIterator it;
01070 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
01071 Attendee *a = *it;
01072 if ( email == a->email() ) {
01073 attendee = a;
01074 break;
01075 }
01076 }
01077 return attendee;
01078 }
01079
01080 static bool rsvpRequested( Incidence *incidence )
01081 {
01082 if ( !incidence ) {
01083 return false;
01084 }
01085
01086
01087
01088 bool rsvp = true;
01089 Attendee::List attendees = incidence->attendees();
01090 Attendee::List::ConstIterator it;
01091 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
01092 if ( it == attendees.begin() ) {
01093 rsvp = (*it)->RSVP();
01094 } else {
01095 if ( (*it)->RSVP() != rsvp ) {
01096 rsvp = true;
01097 break;
01098 }
01099 }
01100 }
01101 return rsvp;
01102 }
01103
01104 static QString rsvpRequestedStr( bool rsvpRequested, const QString &role )
01105 {
01106 if ( rsvpRequested ) {
01107 if ( role.isEmpty() ) {
01108 return i18n( "Your response is requested" );
01109 } else {
01110 return i18n( "Your response as <b>%1</b> is requested" ).arg( role );
01111 }
01112 } else {
01113 if ( role.isEmpty() ) {
01114 return i18n( "No response is necessary" );
01115 } else {
01116 return i18n( "No response as <b>%1</b> is necessary" ).arg( role );
01117 }
01118 }
01119 }
01120
01121 static QString myStatusStr( Incidence *incidence )
01122 {
01123 QString ret;
01124 Attendee *a = findMyAttendee( incidence );
01125 if ( a &&
01126 a->status() != Attendee::NeedsAction && a->status() != Attendee::Delegated ) {
01127 ret = i18n( "(<b>Note</b>: the Organizer preset your response to <b>%1</b>)" ).
01128 arg( Attendee::statusName( a->status() ) );
01129 }
01130 return ret;
01131 }
01132
01133 static QString invitationPerson( const QString& email, QString name, QString uid )
01134 {
01135
01136
01137 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
01138 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
01139 KABC::Addressee::List addressList = add_book->findByEmail( email );
01140 if ( !addressList.isEmpty() ) {
01141 KABC::Addressee o = addressList.first();
01142 if ( !o.isEmpty() && addressList.size() < 2 ) {
01143 if ( name.isEmpty() ) {
01144
01145 name = o.formattedName();
01146 }
01147 uid = o.uid();
01148 } else {
01149
01150 uid = QString::null;
01151 }
01152 }
01153 }
01154
01155
01156 QString tmpString;
01157 if ( !uid.isEmpty() ) {
01158
01159 if ( name.isEmpty() ) {
01160
01161 tmpString += htmlAddLink( "uid:" + uid, email );
01162 } else {
01163 tmpString += htmlAddLink( "uid:" + uid, name );
01164 }
01165 } else {
01166
01167 tmpString += ( name.isEmpty() ? email : name );
01168 }
01169 tmpString += '\n';
01170
01171
01172 if ( !email.isEmpty() ) {
01173 KCal::Person person( name, email );
01174 KURL mailto;
01175 mailto.setProtocol( "mailto" );
01176 mailto.setPath( person.fullName() );
01177 const QString iconPath =
01178 KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small );
01179 tmpString += " " +
01180 htmlAddLink( mailto.url(), "<img src=\"" + iconPath + "\">" )
01181 ;
01182 }
01183 tmpString += "\n";
01184
01185 return tmpString;
01186 }
01187
01188 static QString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode )
01189 {
01190
01191
01192
01193
01194 QString html;
01195 QString descr;
01196 QStringList comments;
01197
01198 if ( incidence->comments().isEmpty() ) {
01199 if ( !incidence->description().isEmpty() ) {
01200
01201 if ( !QStyleSheet::mightBeRichText( incidence->description() ) ) {
01202 comments << string2HTML( incidence->description() );
01203 } else {
01204 comments << incidence->description();
01205 if ( noHtmlMode ) {
01206 comments[0] = cleanHtml( comments[0] );
01207 }
01208 comments[0] = htmlAddTag( "p", comments[0] );
01209 }
01210 }
01211
01212 } else {
01213
01214 QStringList cl = incidence->comments();
01215 uint i = 0;
01216 for( QStringList::Iterator it=cl.begin(); it!=cl.end(); ++it ) {
01217 if ( !QStyleSheet::mightBeRichText( *it ) ) {
01218 comments.append( string2HTML( *it ) );
01219 } else {
01220 if ( noHtmlMode ) {
01221 comments.append( cleanHtml( "<body>" + (*it) + "</body>" ) );
01222 } else {
01223 comments.append( *it );
01224 }
01225 }
01226 i++;
01227 }
01228 if ( !incidence->description().isEmpty() ) {
01229
01230 if ( !QStyleSheet::mightBeRichText( incidence->description() ) ) {
01231 descr = string2HTML( incidence->description() );
01232 } else {
01233 descr = incidence->description();
01234 if ( noHtmlMode ) {
01235 descr = cleanHtml( descr );
01236 }
01237 descr = htmlAddTag( "p", descr );
01238 }
01239 }
01240 }
01241
01242 if( !descr.isEmpty() ) {
01243 html += "<p>";
01244 html += "<table border=\"0\" style=\"margin-top:4px;\">";
01245 html += "<tr><td><center>" +
01246 htmlAddTag( "u", i18n( "Description:" ) ) +
01247 "</center></td></tr>";
01248 html += "<tr><td>" + descr + "</td></tr>";
01249 html += "</table>";
01250 }
01251
01252 if ( !comments.isEmpty() ) {
01253 html += "<p>";
01254 html += "<table border=\"0\" style=\"margin-top:4px;\">";
01255 html += "<tr><td><center>" +
01256 htmlAddTag( "u", i18n( "Comments:" ) ) +
01257 "</center></td></tr>";
01258 html += "<tr><td>";
01259 if ( comments.count() > 1 ) {
01260 html += "<ul>";
01261 for ( uint i=0; i < comments.count(); ++i ) {
01262 html += "<li>" + comments[i] + "</li>";
01263 }
01264 html += "</ul>";
01265 } else {
01266 html += comments[0];
01267 }
01268 html += "</td></tr>";
01269 html += "</table>";
01270 }
01271 return html;
01272 }
01273
01274 static QString invitationDetailsEvent( Event* event, bool noHtmlMode )
01275 {
01276
01277 if ( !event ) {
01278 return QString::null;
01279 }
01280
01281 QString sSummary = i18n( "Summary unspecified" );
01282 if ( !event->summary().isEmpty() ) {
01283 if ( !QStyleSheet::mightBeRichText( event->summary() ) ) {
01284 sSummary = QStyleSheet::escape( event->summary() );
01285 } else {
01286 sSummary = event->summary();
01287 if ( noHtmlMode ) {
01288 sSummary = cleanHtml( sSummary );
01289 }
01290 }
01291 }
01292
01293 QString sLocation = i18n( "Location unspecified" );
01294 if ( !event->location().isEmpty() ) {
01295 if ( !QStyleSheet::mightBeRichText( event->location() ) ) {
01296 sLocation = QStyleSheet::escape( event->location() );
01297 } else {
01298 sLocation = event->location();
01299 if ( noHtmlMode ) {
01300 sLocation = cleanHtml( sLocation );
01301 }
01302 }
01303 }
01304
01305 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
01306 QString html = QString("<div dir=\"%1\">\n").arg(dir);
01307 html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
01308
01309
01310 html += invitationRow( i18n( "What:" ), sSummary );
01311 html += invitationRow( i18n( "Where:" ), sLocation );
01312
01313
01314 if ( event->dtStart().date() == event->dtEnd().date() ) {
01315 html += invitationRow( i18n( "Date:" ),
01316 IncidenceFormatter::dateToString( event->dtStart(), false ) );
01317 if ( !event->doesFloat() ) {
01318 html += invitationRow( i18n( "Time:" ),
01319 IncidenceFormatter::timeToString( event->dtStart(), true ) +
01320 " - " +
01321 IncidenceFormatter::timeToString( event->dtEnd(), true ) );
01322 }
01323 } else {
01324 html += invitationRow( i18n( "Starting date of an event", "From:" ),
01325 IncidenceFormatter::dateToString( event->dtStart(), false ) );
01326 if ( !event->doesFloat() ) {
01327 html += invitationRow( i18n( "Starting time of an event", "At:" ),
01328 IncidenceFormatter::timeToString( event->dtStart(), true ) );
01329 }
01330 if ( event->hasEndDate() ) {
01331 html += invitationRow( i18n( "Ending date of an event", "To:" ),
01332 IncidenceFormatter::dateToString( event->dtEnd(), false ) );
01333 if ( !event->doesFloat() ) {
01334 html += invitationRow( i18n( "Starting time of an event", "At:" ),
01335 IncidenceFormatter::timeToString( event->dtEnd(), true ) );
01336 }
01337 } else {
01338 html += invitationRow( i18n( "Ending date of an event", "To:" ),
01339 i18n( "no end date specified" ) );
01340 }
01341 }
01342
01343
01344 QString durStr = IncidenceFormatter::durationString( event );
01345 if ( !durStr.isEmpty() ) {
01346 html += invitationRow( i18n( "Duration:" ), durStr );
01347 }
01348
01349 if ( event->doesRecur() )
01350 html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) );
01351
01352 html += "</table>\n";
01353 html += invitationsDetailsIncidence( event, noHtmlMode );
01354 html += "</div>\n";
01355
01356 return html;
01357 }
01358
01359 static QString invitationDetailsTodo( Todo *todo, bool noHtmlMode )
01360 {
01361
01362 if ( !todo ) {
01363 return QString::null;
01364 }
01365
01366 QString sSummary = i18n( "Summary unspecified" );
01367 if ( !todo->summary().isEmpty() ) {
01368 if ( !QStyleSheet::mightBeRichText( todo->summary() ) ) {
01369 sSummary = QStyleSheet::escape( todo->summary() );
01370 } else {
01371 sSummary = todo->summary();
01372 if ( noHtmlMode ) {
01373 sSummary = cleanHtml( sSummary );
01374 }
01375 }
01376 }
01377
01378 QString sLocation = i18n( "Location unspecified" );
01379 if ( !todo->location().isEmpty() ) {
01380 if ( !QStyleSheet::mightBeRichText( todo->location() ) ) {
01381 sLocation = QStyleSheet::escape( todo->location() );
01382 } else {
01383 sLocation = todo->location();
01384 if ( noHtmlMode ) {
01385 sLocation = cleanHtml( sLocation );
01386 }
01387 }
01388 }
01389
01390 QString dir = ( QApplication::reverseLayout() ? "rtl" : "ltr" );
01391 QString html = QString("<div dir=\"%1\">\n").arg(dir);
01392 html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n";
01393
01394
01395 html += invitationRow( i18n( "What:" ), sSummary );
01396 html += invitationRow( i18n( "Where:" ), sLocation );
01397
01398 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
01399 html += invitationRow( i18n( "Start Date:" ),
01400 IncidenceFormatter::dateToString( todo->dtStart(), false ) );
01401 if ( !todo->doesFloat() ) {
01402 html += invitationRow( i18n( "Start Time:" ),
01403 IncidenceFormatter::timeToString( todo->dtStart(), false ) );
01404 }
01405 }
01406 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
01407 html += invitationRow( i18n( "Due Date:" ),
01408 IncidenceFormatter::dateToString( todo->dtDue(), false ) );
01409 if ( !todo->doesFloat() ) {
01410 html += invitationRow( i18n( "Due Time:" ),
01411 IncidenceFormatter::timeToString( todo->dtDue(), false ) );
01412 }
01413
01414 } else {
01415 html += invitationRow( i18n( "Due Date:" ), i18n( "Due Date: None", "None" ) );
01416 }
01417
01418 html += "</table></div>\n";
01419 html += invitationsDetailsIncidence( todo, noHtmlMode );
01420
01421 return html;
01422 }
01423
01424 static QString invitationDetailsJournal( Journal *journal, bool noHtmlMode )
01425 {
01426 if ( !journal ) {
01427 return QString::null;
01428 }
01429
01430 QString sSummary = i18n( "Summary unspecified" );
01431 QString sDescr = i18n( "Description unspecified" );
01432 if ( ! journal->summary().isEmpty() ) {
01433 sSummary = journal->summary();
01434 if ( noHtmlMode ) {
01435 sSummary = cleanHtml( sSummary );
01436 }
01437 }
01438 if ( ! journal->description().isEmpty() ) {
01439 sDescr = journal->description();
01440 if ( noHtmlMode ) {
01441 sDescr = cleanHtml( sDescr );
01442 }
01443 }
01444 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01445 html += invitationRow( i18n( "Summary:" ), sSummary );
01446 html += invitationRow( i18n( "Date:" ),
01447 IncidenceFormatter::dateToString( journal->dtStart(), false ) );
01448 html += invitationRow( i18n( "Description:" ), sDescr );
01449 html += "</table>\n";
01450 html += invitationsDetailsIncidence( journal, noHtmlMode );
01451
01452 return html;
01453 }
01454
01455 static QString invitationDetailsFreeBusy( FreeBusy *fb, bool )
01456 {
01457 if ( !fb )
01458 return QString::null;
01459 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01460
01461 html += invitationRow( i18n("Person:"), fb->organizer().fullName() );
01462 html += invitationRow( i18n("Start date:"),
01463 IncidenceFormatter::dateToString( fb->dtStart(), true ) );
01464 html += invitationRow( i18n("End date:"),
01465 KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) );
01466 html += "<tr><td colspan=2><hr></td></tr>\n";
01467 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
01468
01469 QValueList<Period> periods = fb->busyPeriods();
01470
01471 QValueList<Period>::iterator it;
01472 for ( it = periods.begin(); it != periods.end(); ++it ) {
01473 Period per = *it;
01474 if ( per.hasDuration() ) {
01475 int dur = per.duration().asSeconds();
01476 QString cont;
01477 if ( dur >= 3600 ) {
01478 cont += i18n("1 hour ", "%n hours ", dur / 3600);
01479 dur %= 3600;
01480 }
01481 if ( dur >= 60 ) {
01482 cont += i18n("1 minute", "%n minutes ", dur / 60);
01483 dur %= 60;
01484 }
01485 if ( dur > 0 ) {
01486 cont += i18n("1 second", "%n seconds", dur);
01487 }
01488 html += invitationRow( QString::null, i18n("startDate for duration", "%1 for %2")
01489 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
01490 .arg(cont) );
01491 } else {
01492 QString cont;
01493 if ( per.start().date() == per.end().date() ) {
01494 cont = i18n("date, fromTime - toTime ", "%1, %2 - %3")
01495 .arg( KGlobal::locale()->formatDate( per.start().date() ) )
01496 .arg( KGlobal::locale()->formatTime( per.start().time() ) )
01497 .arg( KGlobal::locale()->formatTime( per.end().time() ) );
01498 } else {
01499 cont = i18n("fromDateTime - toDateTime", "%1 - %2")
01500 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) )
01501 .arg( KGlobal::locale()->formatDateTime( per.end(), false ) );
01502 }
01503
01504 html += invitationRow( QString::null, cont );
01505 }
01506 }
01507
01508 html += "</table>\n";
01509 return html;
01510 }
01511
01512 static bool replyMeansCounter( Incidence * )
01513 {
01514 return false;
01529 }
01530
01531 static QString invitationHeaderEvent( Event *event, Incidence *existingIncidence,
01532 ScheduleMessage *msg, const QString &sender )
01533 {
01534 if ( !msg || !event )
01535 return QString::null;
01536
01537 switch ( msg->method() ) {
01538 case Scheduler::Publish:
01539 return i18n( "This invitation has been published" );
01540 case Scheduler::Request:
01541 if ( existingIncidence && event->revision() > 0 ) {
01542 return i18n( "This invitation has been updated by the organizer %1" ).
01543 arg( event->organizer().fullName() );
01544 }
01545 if ( iamOrganizer( event ) ) {
01546 return i18n( "I created this invitation" );
01547 } else {
01548 if ( senderIsOrganizer( event, sender ) ) {
01549 if ( !event->organizer().fullName().isEmpty() ) {
01550 return i18n( "You received an invitation from %1" ).
01551 arg( event->organizer().fullName() );
01552 } else {
01553 return i18n( "You received an invitation" );
01554 }
01555 } else {
01556 if ( !event->organizer().fullName().isEmpty() ) {
01557 return i18n( "You received an invitation from %1 as a representative of %2" ).
01558 arg( sender, event->organizer().fullName() );
01559 } else {
01560 return i18n( "You received an invitation from %1 as the organizer's representative" ).
01561 arg( sender );
01562 }
01563 }
01564 }
01565 case Scheduler::Refresh:
01566 return i18n( "This invitation was refreshed" );
01567 case Scheduler::Cancel:
01568 return i18n( "This invitation has been canceled" );
01569 case Scheduler::Add:
01570 return i18n( "Addition to the invitation" );
01571 case Scheduler::Reply:
01572 {
01573 if ( replyMeansCounter( event ) ) {
01574 return i18n( "%1 makes this counter proposal" ).
01575 arg( firstAttendeeName( event, i18n( "Sender" ) ) );
01576 }
01577
01578 Attendee::List attendees = event->attendees();
01579 if( attendees.count() == 0 ) {
01580 kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01581 return QString::null;
01582 }
01583 if( attendees.count() != 1 ) {
01584 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01585 << "but is " << attendees.count() << endl;
01586 }
01587 QString attendeeName = firstAttendeeName( event, i18n( "Sender" ) );
01588
01589 QString delegatorName, dummy;
01590 Attendee* attendee = *attendees.begin();
01591 KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
01592 if ( delegatorName.isEmpty() ) {
01593 delegatorName = attendee->delegator();
01594 }
01595
01596 switch( attendee->status() ) {
01597 case Attendee::NeedsAction:
01598 return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName );
01599 case Attendee::Accepted:
01600 if ( event->revision() > 0 ) {
01601 if ( !sender.isEmpty() ) {
01602 return i18n( "This invitation has been updated by attendee %1" ).arg( sender );
01603 } else {
01604 return i18n( "This invitation has been updated by an attendee" );
01605 }
01606 } else {
01607 if ( delegatorName.isEmpty() ) {
01608 return i18n( "%1 accepts this invitation" ).arg( attendeeName );
01609 } else {
01610 return i18n( "%1 accepts this invitation on behalf of %2" ).
01611 arg( attendeeName ).arg( delegatorName );
01612 }
01613 }
01614 case Attendee::Tentative:
01615 if ( delegatorName.isEmpty() ) {
01616 return i18n( "%1 tentatively accepts this invitation" ).
01617 arg( attendeeName );
01618 } else {
01619 return i18n( "%1 tentatively accepts this invitation on behalf of %2" ).
01620 arg( attendeeName ).arg( delegatorName );
01621 }
01622 case Attendee::Declined:
01623 if ( delegatorName.isEmpty() ) {
01624 return i18n( "%1 declines this invitation" ).arg( attendeeName );
01625 } else {
01626 return i18n( "%1 declines this invitation on behalf of %2" ).
01627 arg( attendeeName ).arg( delegatorName );
01628 }
01629 case Attendee::Delegated: {
01630 QString delegate, dummy;
01631 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01632 if ( delegate.isEmpty() ) {
01633 delegate = attendee->delegate();
01634 }
01635 if ( !delegate.isEmpty() ) {
01636 return i18n( "%1 has delegated this invitation to %2" ).
01637 arg( attendeeName ) .arg( delegate );
01638 } else {
01639 return i18n( "%1 has delegated this invitation" ).arg( attendeeName );
01640 }
01641 }
01642 case Attendee::Completed:
01643 return i18n( "This invitation is now completed" );
01644 case Attendee::InProcess:
01645 return i18n( "%1 is still processing the invitation" ).
01646 arg( attendeeName );
01647 default:
01648 return i18n( "Unknown response to this invitation" );
01649 }
01650 break;
01651 }
01652
01653 case Scheduler::Counter:
01654 return i18n( "%1 makes this counter proposal" ).
01655 arg( firstAttendeeName( event, i18n( "Sender" ) ) );
01656
01657 case Scheduler::Declinecounter:
01658 return i18n( "%1 declines the counter proposal" ).
01659 arg( firstAttendeeName( event, i18n( "Sender" ) ) );
01660
01661 case Scheduler::NoMethod:
01662 return i18n("Error: iMIP message with unknown method: '%1'").
01663 arg( msg->method() );
01664 }
01665 return QString::null;
01666 }
01667
01668 static QString invitationHeaderTodo( Todo *todo, Incidence *existingIncidence,
01669 ScheduleMessage *msg, const QString &sender )
01670 {
01671 if ( !msg || !todo ) {
01672 return QString::null;
01673 }
01674
01675 switch ( msg->method() ) {
01676 case Scheduler::Publish:
01677 return i18n("This task has been published");
01678 case Scheduler::Request:
01679 if ( existingIncidence && todo->revision() > 0 ) {
01680 return i18n( "This task has been updated by the organizer %1" ).
01681 arg( todo->organizer().fullName() );
01682 } else {
01683 if ( iamOrganizer( todo ) ) {
01684 return i18n( "I created this task" );
01685 } else {
01686 if ( senderIsOrganizer( todo, sender ) ) {
01687 if ( !todo->organizer().fullName().isEmpty() ) {
01688 return i18n( "You have been assigned this task by %1" ).
01689 arg( todo->organizer().fullName() );
01690 } else {
01691 return i18n( "You have been assigned this task" );
01692 }
01693 } else {
01694 if ( !todo->organizer().fullName().isEmpty() ) {
01695 return i18n( "You have been assigned this task by %1 as a representative of %2" ).
01696 arg( sender, todo->organizer().fullName() );
01697 } else {
01698 return i18n( "You have been assigned this task by %1 as the organizer's representative" ).
01699 arg( sender );
01700 }
01701 }
01702 }
01703 }
01704 case Scheduler::Refresh:
01705 return i18n( "This task was refreshed" );
01706 case Scheduler::Cancel:
01707 return i18n( "This task was canceled" );
01708 case Scheduler::Add:
01709 return i18n( "Addition to the task" );
01710 case Scheduler::Reply:
01711 {
01712 if ( replyMeansCounter( todo ) ) {
01713 return i18n( "%1 makes this counter proposal" ).
01714 arg( firstAttendeeName( todo, i18n( "Sender" ) ) );
01715 }
01716
01717 Attendee::List attendees = todo->attendees();
01718 if( attendees.count() == 0 ) {
01719 kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01720 return QString::null;
01721 }
01722 if( attendees.count() != 1 ) {
01723 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01724 << "but is " << attendees.count() << endl;
01725 }
01726 QString attendeeName = firstAttendeeName( todo, i18n( "Sender" ) );
01727
01728 QString delegatorName, dummy;
01729 Attendee* attendee = *attendees.begin();
01730 KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy );
01731 if ( delegatorName.isEmpty() ) {
01732 delegatorName = attendee->delegator();
01733 }
01734
01735 switch( attendee->status() ) {
01736 case Attendee::NeedsAction:
01737 return i18n( "%1 indicates this task assignment still needs some action" ).arg( attendeeName );
01738 case Attendee::Accepted:
01739 if ( todo->revision() > 0 ) {
01740 if ( !sender.isEmpty() ) {
01741 if ( todo->isCompleted() ) {
01742 return i18n( "This task has been completed by assignee %1" ).arg( sender );
01743 } else {
01744 return i18n( "This task has been updated by assignee %1" ).arg( sender );
01745 }
01746 } else {
01747 if ( todo->isCompleted() ) {
01748 return i18n( "This task has been completed by an assignee" );
01749 } else {
01750 return i18n( "This task has been updated by an assignee" );
01751 }
01752 }
01753 } else {
01754 if ( delegatorName.isEmpty() ) {
01755 return i18n( "%1 accepts this task" ).arg( attendeeName );
01756 } else {
01757 return i18n( "%1 accepts this task on behalf of %2" ).
01758 arg( attendeeName ).arg( delegatorName );
01759 }
01760 }
01761 case Attendee::Tentative:
01762 if ( delegatorName.isEmpty() ) {
01763 return i18n( "%1 tentatively accepts this task" ).
01764 arg( attendeeName );
01765 } else {
01766 return i18n( "%1 tentatively accepts this task on behalf of %2" ).
01767 arg( attendeeName ).arg( delegatorName );
01768 }
01769 case Attendee::Declined:
01770 if ( delegatorName.isEmpty() ) {
01771 return i18n( "%1 declines this task" ).arg( attendeeName );
01772 } else {
01773 return i18n( "%1 declines this task on behalf of %2" ).
01774 arg( attendeeName ).arg( delegatorName );
01775 }
01776 case Attendee::Delegated: {
01777 QString delegate, dummy;
01778 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy );
01779 if ( delegate.isEmpty() ) {
01780 delegate = attendee->delegate();
01781 }
01782 if ( !delegate.isEmpty() ) {
01783 return i18n( "%1 has delegated this request for the task to %2" ).
01784 arg( attendeeName ).arg( delegate );
01785 } else {
01786 return i18n( "%1 has delegated this request for the task" ).
01787 arg( attendeeName );
01788 }
01789 }
01790 case Attendee::Completed:
01791 return i18n( "The request for this task is now completed" );
01792 case Attendee::InProcess:
01793 return i18n( "%1 is still processing the task" ).
01794 arg( attendeeName );
01795 default:
01796 return i18n( "Unknown response to this task" );
01797 }
01798 break;
01799 }
01800
01801 case Scheduler::Counter:
01802 return i18n( "%1 makes this counter proposal" ).
01803 arg( firstAttendeeName( todo, i18n( "Sender" ) ) );
01804
01805 case Scheduler::Declinecounter:
01806 return i18n( "%1 declines the counter proposal" ).
01807 arg( firstAttendeeName( todo, i18n( "Sender" ) ) );
01808
01809 case Scheduler::NoMethod:
01810 return i18n( "Error: iMIP message with unknown method: '%1'" ).
01811 arg( msg->method() );
01812 }
01813 return QString::null;
01814 }
01815
01816 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
01817 {
01818 if ( !msg || !journal ) {
01819 return QString::null;
01820 }
01821
01822 switch ( msg->method() ) {
01823 case Scheduler::Publish:
01824 return i18n("This journal has been published");
01825 case Scheduler::Request:
01826 return i18n( "You have been assigned this journal" );
01827 case Scheduler::Refresh:
01828 return i18n( "This journal was refreshed" );
01829 case Scheduler::Cancel:
01830 return i18n( "This journal was canceled" );
01831 case Scheduler::Add:
01832 return i18n( "Addition to the journal" );
01833 case Scheduler::Reply:
01834 {
01835 if ( replyMeansCounter( journal ) ) {
01836 return i18n( "Sender makes this counter proposal" );
01837 }
01838
01839 Attendee::List attendees = journal->attendees();
01840 if( attendees.count() == 0 ) {
01841 kdDebug(5850) << "No attendees in the iCal reply!" << endl;
01842 return QString::null;
01843 }
01844 if( attendees.count() != 1 ) {
01845 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 "
01846 << "but is " << attendees.count() << endl;
01847 }
01848 Attendee* attendee = *attendees.begin();
01849
01850 switch( attendee->status() ) {
01851 case Attendee::NeedsAction:
01852 return i18n( "Sender indicates this journal assignment still needs some action" );
01853 case Attendee::Accepted:
01854 return i18n( "Sender accepts this journal" );
01855 case Attendee::Tentative:
01856 return i18n( "Sender tentatively accepts this journal" );
01857 case Attendee::Declined:
01858 return i18n( "Sender declines this journal" );
01859 case Attendee::Delegated:
01860 return i18n( "Sender has delegated this request for the journal" );
01861 case Attendee::Completed:
01862 return i18n( "The request for this journal is now completed" );
01863 case Attendee::InProcess:
01864 return i18n( "Sender is still processing the invitation" );
01865 default:
01866 return i18n( "Unknown response to this journal" );
01867 }
01868 break;
01869 }
01870 case Scheduler::Counter:
01871 return i18n( "Sender makes this counter proposal" );
01872 case Scheduler::Declinecounter:
01873 return i18n( "Sender declines the counter proposal" );
01874 case Scheduler::NoMethod:
01875 return i18n("Error: iMIP message with unknown method: '%1'").
01876 arg( msg->method() );
01877 }
01878 return QString::null;
01879 }
01880
01881 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01882 {
01883 if ( !msg || !fb ) {
01884 return QString::null;
01885 }
01886
01887 switch ( msg->method() ) {
01888 case Scheduler::Publish:
01889 return i18n("This free/busy list has been published");
01890 case Scheduler::Request:
01891 return i18n( "The free/busy list has been requested" );
01892 case Scheduler::Refresh:
01893 return i18n( "This free/busy list was refreshed" );
01894 case Scheduler::Cancel:
01895 return i18n( "This free/busy list was canceled" );
01896 case Scheduler::Add:
01897 return i18n( "Addition to the free/busy list" );
01898 case Scheduler::NoMethod:
01899 default:
01900 return i18n("Error: Free/Busy iMIP message with unknown method: '%1'").
01901 arg( msg->method() );
01902 }
01903 }
01904
01905 static QString invitationAttendees( Incidence *incidence )
01906 {
01907 QString tmpStr;
01908 if ( !incidence ) {
01909 return tmpStr;
01910 }
01911
01912 if ( incidence->type() == "Todo" ) {
01913 tmpStr += htmlAddTag( "u", i18n( "Assignees" ) );
01914 } else {
01915 tmpStr += htmlAddTag( "u", i18n( "Attendees" ) );
01916 }
01917 tmpStr += "<br/>";
01918
01919 int count=0;
01920 Attendee::List attendees = incidence->attendees();
01921 if ( !attendees.isEmpty() ) {
01922
01923 Attendee::List::ConstIterator it;
01924 for( it = attendees.begin(); it != attendees.end(); ++it ) {
01925 Attendee *a = *it;
01926 if ( !iamAttendee( a ) ) {
01927 count++;
01928 if ( count == 1 ) {
01929 tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\" columns=\"2\">";
01930 }
01931 tmpStr += "<tr>";
01932 tmpStr += "<td>";
01933 tmpStr += invitationPerson( a->email(), a->name(), QString::null );
01934 if ( !a->delegator().isEmpty() ) {
01935 tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
01936 }
01937 if ( !a->delegate().isEmpty() ) {
01938 tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
01939 }
01940 tmpStr += "</td>";
01941 tmpStr += "<td>" + a->statusStr() + "</td>";
01942 tmpStr += "</tr>";
01943 }
01944 }
01945 }
01946 if ( count ) {
01947 tmpStr += "</table>";
01948 } else {
01949 tmpStr += "<i>" + i18n( "No attendee", "None" ) + "</i>";
01950 }
01951
01952 return tmpStr;
01953 }
01954
01955 static QString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence )
01956 {
01957 QString tmpStr;
01958 if ( !incidence ) {
01959 return tmpStr;
01960 }
01961
01962 Attachment::List attachments = incidence->attachments();
01963 if ( !attachments.isEmpty() ) {
01964 tmpStr += i18n( "Attached Documents:" ) + "<ol>";
01965
01966 Attachment::List::ConstIterator it;
01967 for( it = attachments.begin(); it != attachments.end(); ++it ) {
01968 Attachment *a = *it;
01969 tmpStr += "<li>";
01970
01971 KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() );
01972 const QString iconStr = mimeType ? mimeType->icon( a->uri(), false ) : QString( "application-octet-stream" );
01973 const QString iconPath = KGlobal::iconLoader()->iconPath( iconStr, KIcon::Small );
01974 if ( !iconPath.isEmpty() ) {
01975 tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
01976 }
01977 tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() );
01978 tmpStr += "</li>";
01979 }
01980 tmpStr += "</ol>";
01981 }
01982
01983 return tmpStr;
01984 }
01985
01986 class IncidenceFormatter::ScheduleMessageVisitor
01987 : public IncidenceBase::Visitor
01988 {
01989 public:
01990 ScheduleMessageVisitor() : mExistingIncidence( 0 ), mMessage( 0 ) { mResult = ""; }
01991 bool act( IncidenceBase *incidence, Incidence *existingIncidence, ScheduleMessage *msg,
01992 const QString &sender )
01993 {
01994 mExistingIncidence = existingIncidence;
01995 mMessage = msg;
01996 mSender = sender;
01997 return incidence->accept( *this );
01998 }
01999 QString result() const { return mResult; }
02000
02001 protected:
02002 QString mResult;
02003 Incidence *mExistingIncidence;
02004 ScheduleMessage *mMessage;
02005 QString mSender;
02006 };
02007
02008 class IncidenceFormatter::InvitationHeaderVisitor
02009 : public IncidenceFormatter::ScheduleMessageVisitor
02010 {
02011 protected:
02012 bool visit( Event *event )
02013 {
02014 mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender );
02015 return !mResult.isEmpty();
02016 }
02017 bool visit( Todo *todo )
02018 {
02019 mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender );
02020 return !mResult.isEmpty();
02021 }
02022 bool visit( Journal *journal )
02023 {
02024 mResult = invitationHeaderJournal( journal, mMessage );
02025 return !mResult.isEmpty();
02026 }
02027 bool visit( FreeBusy *fb )
02028 {
02029 mResult = invitationHeaderFreeBusy( fb, mMessage );
02030 return !mResult.isEmpty();
02031 }
02032 };
02033
02034 class IncidenceFormatter::InvitationBodyVisitor
02035 : public IncidenceFormatter::ScheduleMessageVisitor
02036 {
02037 public:
02038 InvitationBodyVisitor( bool noHtmlMode )
02039 : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ) {}
02040
02041 protected:
02042 bool visit( Event *event )
02043 {
02044 mResult = invitationDetailsEvent( event, mNoHtmlMode );
02045 return !mResult.isEmpty();
02046 }
02047 bool visit( Todo *todo )
02048 {
02049 mResult = invitationDetailsTodo( todo, mNoHtmlMode );
02050 return !mResult.isEmpty();
02051 }
02052 bool visit( Journal *journal )
02053 {
02054 mResult = invitationDetailsJournal( journal, mNoHtmlMode );
02055 return !mResult.isEmpty();
02056 }
02057 bool visit( FreeBusy *fb )
02058 {
02059 mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode );
02060 return !mResult.isEmpty();
02061 }
02062
02063 private:
02064 bool mNoHtmlMode;
02065 };
02066
02067 class IncidenceFormatter::IncidenceCompareVisitor
02068 : public IncidenceBase::Visitor
02069 {
02070 public:
02071 IncidenceCompareVisitor() : mExistingIncidence(0) {}
02072 bool act( IncidenceBase *incidence, Incidence *existingIncidence, int method )
02073 {
02074 Incidence *inc = dynamic_cast<Incidence*>( incidence );
02075 if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() )
02076 return false;
02077 mExistingIncidence = existingIncidence;
02078 mMethod = method;
02079 return incidence->accept( *this );
02080 }
02081
02082 QString result() const
02083 {
02084 if ( mChanges.isEmpty() ) {
02085 return QString::null;
02086 }
02087 QString html = "<div align=\"left\"><ul><li>";
02088 html += mChanges.join( "</li><li>" );
02089 html += "</li><ul></div>";
02090 return html;
02091 }
02092
02093 protected:
02094 bool visit( Event *event )
02095 {
02096 compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
02097 compareIncidences( event, mExistingIncidence, mMethod );
02098 return !mChanges.isEmpty();
02099 }
02100 bool visit( Todo *todo )
02101 {
02102 compareTodos( todo, dynamic_cast<Todo*>( mExistingIncidence ) );
02103 compareIncidences( todo, mExistingIncidence, mMethod );
02104 return !mChanges.isEmpty();
02105 }
02106 bool visit( Journal *journal )
02107 {
02108 compareIncidences( journal, mExistingIncidence, mMethod );
02109 return !mChanges.isEmpty();
02110 }
02111 bool visit( FreeBusy *fb )
02112 {
02113 Q_UNUSED( fb );
02114 return !mChanges.isEmpty();
02115 }
02116
02117 private:
02118 void compareEvents( Event *newEvent, Event *oldEvent )
02119 {
02120 if ( !oldEvent || !newEvent )
02121 return;
02122 if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() )
02123 mChanges += i18n( "The invitation starting time has been changed from %1 to %2" )
02124 .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) );
02125 if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() )
02126 mChanges += i18n( "The invitation ending time has been changed from %1 to %2" )
02127 .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) );
02128 }
02129
02130 void compareTodos( Todo *newTodo, Todo *oldTodo )
02131 {
02132 if ( !oldTodo || !newTodo ) {
02133 return;
02134 }
02135
02136 if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) {
02137 mChanges += i18n( "The task has been completed" );
02138 }
02139 if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) {
02140 mChanges += i18n( "The task is no longer completed" );
02141 }
02142 if ( oldTodo->percentComplete() != newTodo->percentComplete() ) {
02143 const QString oldPer = i18n( "%1%" ).arg( oldTodo->percentComplete() );
02144 const QString newPer = i18n( "%1%" ).arg( newTodo->percentComplete() );
02145 mChanges += i18n( "The task completed percentage has changed from %1 to %2" ).
02146 arg( oldPer, newPer );
02147 }
02148
02149 if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) {
02150 mChanges += i18n( "A task starting time has been added" );
02151 }
02152 if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) {
02153 mChanges += i18n( "The task starting time has been removed" );
02154 }
02155 if ( oldTodo->hasStartDate() && newTodo->hasStartDate() &&
02156 oldTodo->dtStart() != newTodo->dtStart() ) {
02157 mChanges += i18n( "The task starting time has been changed from %1 to %2" ).
02158 arg( dateTimeToString( oldTodo->dtStart(), oldTodo->doesFloat(), false ),
02159 dateTimeToString( newTodo->dtStart(), newTodo->doesFloat(), false ) );
02160 }
02161
02162 if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) {
02163 mChanges += i18n( "A task due time has been added" );
02164 }
02165 if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) {
02166 mChanges += i18n( "The task due time has been removed" );
02167 }
02168 if ( oldTodo->hasDueDate() && newTodo->hasDueDate() &&
02169 oldTodo->dtDue() != newTodo->dtDue() ) {
02170 mChanges += i18n( "The task due time has been changed from %1 to %2" ).
02171 arg( dateTimeToString( oldTodo->dtDue(), oldTodo->doesFloat(), false ),
02172 dateTimeToString( newTodo->dtDue(), newTodo->doesFloat(), false ) );
02173 }
02174 }
02175
02176 void compareIncidences( Incidence *newInc, Incidence *oldInc, int method )
02177 {
02178 if ( !oldInc || !newInc )
02179 return;
02180 if ( oldInc->summary() != newInc->summary() )
02181 mChanges += i18n( "The summary has been changed to: \"%1\"" ).arg( newInc->summary() );
02182 if ( oldInc->location() != newInc->location() )
02183 mChanges += i18n( "The location has been changed to: \"%1\"" ).arg( newInc->location() );
02184 if ( oldInc->description() != newInc->description() )
02185 mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() );
02186 Attendee::List oldAttendees = oldInc->attendees();
02187 Attendee::List newAttendees = newInc->attendees();
02188 for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
02189 it != newAttendees.constEnd(); ++it ) {
02190 Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
02191 if ( !oldAtt ) {
02192 mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() );
02193 } else {
02194 if ( oldAtt->status() != (*it)->status() )
02195 mChanges += i18n( "The status of attendee %1 has been changed to: %2" ).
02196 arg( (*it)->fullName() ).arg( (*it)->statusStr() );
02197 }
02198 }
02199 if ( method == Scheduler::Request ) {
02200 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
02201 it != oldAttendees.constEnd(); ++it ) {
02202 if ( (*it)->email() != oldInc->organizer().email() ) {
02203 Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
02204 if ( !newAtt ) {
02205 mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() );
02206 }
02207 }
02208 }
02209 }
02210 }
02211
02212 private:
02213 Incidence *mExistingIncidence;
02214 int mMethod;
02215 QStringList mChanges;
02216 };
02217
02218
02219 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
02220 {
02221 if ( !id.startsWith( "ATTACH:" ) ) {
02222 QString res = QString( "<a href=\"%1\"><b>%2</b></a>" ).
02223 arg( generateLinkURL( id ), text );
02224 return res;
02225 } else {
02226
02227 QString res = QString( "<a href=\"%1\">%2</a>" ).
02228 arg( generateLinkURL( id ), text );
02229 return res;
02230 }
02231 }
02232
02233
02234
02235 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
02236 {
02237 CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
02238 if ( !cal || !incidence ) {
02239 return true;
02240 }
02241 ResourceCalendar *res = cal->resource( incidence );
02242 if ( !res ) {
02243 return true;
02244 }
02245 const QString subRes = res->subresourceIdentifier( incidence );
02246 if ( !subRes.contains( "/.INBOX.directory/" ) ) {
02247 return false;
02248 }
02249 return true;
02250 }
02251
02252
02253 static QString spacer = "<td> </td>";
02254
02255 static QString tdOpen = "<td>";
02256 static QString tdClose = "</td>" + spacer;
02257
02258 static QString responseButtons( Incidence *inc, bool rsvpReq, bool rsvpRec,
02259 InvitationFormatterHelper *helper )
02260 {
02261 QString html;
02262 if ( !helper ) {
02263 return html;
02264 }
02265
02266 if ( !rsvpReq && ( inc && inc->revision() == 0 ) ) {
02267
02268 html += tdOpen;
02269 html += helper->makeLink( "record", i18n( "[Record]" ) );
02270 html += tdClose;
02271
02272
02273 html += tdOpen;
02274 html += helper->makeLink( "delete", i18n( "[Move to Trash]" ) );
02275 html += tdClose;
02276
02277 } else {
02278
02279
02280 html += tdOpen;
02281 html += helper->makeLink( "accept", i18n( "[Accept]" ) );
02282 html += tdClose;
02283
02284
02285 html += tdOpen;
02286 html += helper->makeLink( "accept_conditionally",
02287 i18n( "Accept conditionally", "[Accept cond.]" ) );
02288 html += tdClose;
02289
02290
02291 html += tdOpen;
02292 html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) );
02293 html += tdClose;
02294
02295
02296 html += tdOpen;
02297 html += helper->makeLink( "decline", i18n( "[Decline]" ) );
02298 html += tdClose;
02299 }
02300
02301 if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
02302
02303 html += tdOpen;
02304 html += helper->makeLink( "delegate", i18n( "[Delegate]" ) );
02305 html += tdClose;
02306
02307
02308 html += tdOpen;
02309 html += helper->makeLink( "forward", i18n( "[Forward]" ) );
02310 html += tdClose;
02311
02312
02313 if ( inc && inc->type() == "Event" ) {
02314 html += tdOpen;
02315 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
02316 html += tdClose;
02317 }
02318 }
02319 return html;
02320 }
02321
02322 static QString counterButtons( Incidence *incidence,
02323 InvitationFormatterHelper *helper )
02324 {
02325 QString html;
02326 if ( !helper ) {
02327 return html;
02328 }
02329
02330
02331 html += tdOpen;
02332 html += helper->makeLink( "accept_counter", i18n("[Accept]") );
02333 html += tdClose;
02334
02335
02336 html += tdOpen;
02337 html += helper->makeLink( "decline_counter", i18n("[Decline]") );
02338 html += tdClose;
02339
02340
02341 if ( incidence && incidence->type() == "Event" ) {
02342 html += tdOpen;
02343 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) );
02344 html += tdClose;
02345 }
02346 return html;
02347 }
02348
02349 QString IncidenceFormatter::formatICalInvitationHelper( QString invitation,
02350 Calendar *mCalendar,
02351 InvitationFormatterHelper *helper,
02352 bool noHtmlMode,
02353 const QString &sender )
02354 {
02355 if ( invitation.isEmpty() ) {
02356 return QString::null;
02357 }
02358
02359 ICalFormat format;
02360
02361 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
02362
02363 if( !msg ) {
02364 kdDebug( 5850 ) << "Failed to parse the scheduling message" << endl;
02365 Q_ASSERT( format.exception() );
02366 kdDebug( 5850 ) << format.exception()->message() << endl;
02367 return QString::null;
02368 }
02369
02370 IncidenceBase *incBase = msg->event();
02371
02372
02373 Incidence *existingIncidence = 0;
02374 if ( incBase && helper->calendar() ) {
02375 existingIncidence = helper->calendar()->incidence( incBase->uid() );
02376 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
02377 existingIncidence = 0;
02378 }
02379 if ( !existingIncidence ) {
02380 const Incidence::List list = helper->calendar()->incidences();
02381 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
02382 if ( (*it)->schedulingID() == incBase->uid() &&
02383 incidenceOwnedByMe( helper->calendar(), *it ) ) {
02384 existingIncidence = *it;
02385 break;
02386 }
02387 }
02388 }
02389 }
02390
02391
02392 QString html;
02393
02394 QString tableStyle = QString::fromLatin1(
02395 "style=\"border: solid 1px; margin: 0em;\"" );
02396 QString tableHead = QString::fromLatin1(
02397 "<div align=\"center\">"
02398 "<table width=\"80%\" cellpadding=\"1\" cellspacing=\"0\" %1>"
02399 "<tr><td>").arg(tableStyle);
02400
02401 html += tableHead;
02402 InvitationHeaderVisitor headerVisitor;
02403
02404 if ( !headerVisitor.act( incBase, existingIncidence, msg, sender ) )
02405 return QString::null;
02406 html += "<b>" + headerVisitor.result() + "</b>";
02407
02408 InvitationBodyVisitor bodyVisitor( noHtmlMode );
02409 if ( !bodyVisitor.act( incBase, existingIncidence, msg, sender ) )
02410 return QString::null;
02411 html += bodyVisitor.result();
02412
02413 if ( msg->method() == Scheduler::Request ) {
02414 IncidenceCompareVisitor compareVisitor;
02415 if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
02416 html += "<p align=\"left\">";
02417 html += i18n( "The following changes have been made by the organizer:" );
02418 html += "</p>";
02419 html += compareVisitor.result();
02420 }
02421 }
02422 if ( msg->method() == Scheduler::Reply ) {
02423 IncidenceCompareVisitor compareVisitor;
02424 if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
02425 html += "<p align=\"left\">";
02426 if ( !sender.isEmpty() ) {
02427 html += i18n( "The following changes have been made by %1:" ).arg( sender );
02428 } else {
02429 html += i18n( "The following changes have been made by an attendee:" );
02430 }
02431 html += "</p>";
02432 html += compareVisitor.result();
02433 }
02434 }
02435
02436 Incidence *inc = dynamic_cast<Incidence*>( incBase );
02437
02438
02439 bool myInc = iamOrganizer( inc );
02440
02441
02442 bool rsvpRec = false;
02443 Attendee *ea = 0;
02444 if ( !myInc ) {
02445 Incidence *rsvpIncidence = existingIncidence;
02446 if ( !rsvpIncidence && inc && inc->revision() > 0 ) {
02447 rsvpIncidence = inc;
02448 }
02449 if ( rsvpIncidence ) {
02450 ea = findMyAttendee( rsvpIncidence );
02451 }
02452 if ( ea &&
02453 ( ea->status() == Attendee::Accepted ||
02454 ea->status() == Attendee::Declined ||
02455 ea->status() == Attendee::Tentative ) ) {
02456 rsvpRec = true;
02457 }
02458 }
02459
02460
02461 QString role;
02462 bool isDelegated = false;
02463 Attendee *a = findMyAttendee( inc );
02464 if ( !a && inc ) {
02465 if ( !inc->attendees().isEmpty() ) {
02466 a = inc->attendees().first();
02467 }
02468 }
02469 if ( a ) {
02470 isDelegated = ( a->status() == Attendee::Delegated );
02471 role = Attendee::roleName( a->role() );
02472 }
02473
02474
02475 bool rsvpReq = rsvpRequested( inc );
02476 if ( !myInc && a ) {
02477 html += "<br/>";
02478 html += "<i><u>";
02479 if ( rsvpRec && inc ) {
02480 if ( inc->revision() == 0 ) {
02481 html += i18n( "Your <b>%1</b> response has already been recorded" ).
02482 arg( ea->statusStr() );
02483 } else {
02484 html += i18n( "Your status for this invitation is <b>%1</b>" ).
02485 arg( ea->statusStr() );
02486 }
02487 rsvpReq = false;
02488 } else if ( msg->method() == Scheduler::Cancel ) {
02489 html += i18n( "This invitation was declined" );
02490 } else if ( msg->method() == Scheduler::Add ) {
02491 html += i18n( "This invitation was accepted" );
02492 } else {
02493 if ( !isDelegated ) {
02494 html += rsvpRequestedStr( rsvpReq, role );
02495 } else {
02496 html += i18n( "Awaiting delegation response" );
02497 }
02498 }
02499 html += "</u></i>";
02500 }
02501
02502
02503 if ( !myInc ) {
02504 if ( inc && inc->revision() == 0 ) {
02505 QString statStr = myStatusStr( inc );
02506 if ( !statStr.isEmpty() ) {
02507 html += "<br/>";
02508 html += "<i>";
02509 html += statStr;
02510 html += "</i>";
02511 }
02512 }
02513 }
02514
02515
02516
02517 html += "<br><table border=\"0\" cellspacing=\"0\"><tr><td> </td></tr>";
02518
02519 switch ( msg->method() ) {
02520 case Scheduler::Publish:
02521 case Scheduler::Request:
02522 case Scheduler::Refresh:
02523 case Scheduler::Add:
02524 {
02525 if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
02526 html += "<tr>";
02527 if ( inc->type() == "Todo" ) {
02528 html += "<td colspan=\"9\">";
02529 html += helper->makeLink( "reply", i18n( "[Record invitation in my task list]" ) );
02530 } else {
02531 html += "<td colspan=\"13\">";
02532 html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) );
02533 }
02534 html += "</td></tr>";
02535 }
02536
02537 if ( !myInc && a ) {
02538 html += "<tr>" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + "</tr>";
02539 }
02540 break;
02541 }
02542
02543 case Scheduler::Cancel:
02544
02545 if ( inc ) {
02546 html += "<tr>";
02547 if ( inc->type() == "Todo" ) {
02548 html += "<td colspan=\"9\">";
02549 html += helper->makeLink( "cancel", i18n( "[Remove invitation from my task list]" ) );
02550 } else {
02551 html += "<td colspan=\"13\">";
02552 html += helper->makeLink( "cancel", i18n( "[Remove invitation from my calendar]" ) );
02553 }
02554 html += "</td></tr>";
02555 }
02556 break;
02557
02558 case Scheduler::Reply:
02559 {
02560
02561 Attendee *a = 0;
02562 Attendee *ea = 0;
02563 if ( inc ) {
02564
02565 if ( replyMeansCounter( inc ) ) {
02566 html += "<tr>" + counterButtons( inc, helper ) + "</tr>";
02567 break;
02568 }
02569
02570
02571
02572
02573
02574
02575 a = findDelegatedFromMyAttendee( inc );
02576 if ( a ) {
02577 if ( a->status() != Attendee::Accepted ||
02578 a->status() != Attendee::Tentative ) {
02579 html += "<tr>" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + "</tr>";
02580 break;
02581 }
02582 }
02583
02584
02585 if ( !inc->attendees().isEmpty() ) {
02586 a = inc->attendees().first();
02587 }
02588 if ( a ) {
02589 ea = findAttendee( existingIncidence, a->email() );
02590 }
02591 }
02592 if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
02593 if ( inc && inc->revision() > 0 ) {
02594 html += "<br><u><i>";
02595 html += i18n( "The response has been recorded [%1]" ).arg( ea->statusStr() );
02596 html += "</i></u>";
02597 }
02598 } else {
02599 if ( inc ) {
02600 html += "<tr><td>";
02601 if ( inc->type() == "Todo" ) {
02602 html += helper->makeLink( "reply", i18n( "[Record response in my task list]" ) );
02603 } else {
02604 html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) );
02605 }
02606 html += "</td></tr>";
02607 }
02608 }
02609 break;
02610 }
02611
02612 case Scheduler::Counter:
02613
02614 html += "<tr>" + counterButtons( inc, helper ) + "</tr>";
02615 break;
02616
02617 case Scheduler::Declinecounter:
02618 case Scheduler::NoMethod:
02619 break;
02620 }
02621
02622
02623 html += "</td></tr></table>";
02624
02625
02626 if ( myInc && helper->calendar() ) {
02627 html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
02628 }
02629
02630
02631 html += "</td></tr></table><br></div>";
02632
02633
02634 html += invitationAttachments( helper, inc );
02635
02636 return html;
02637 }
02638
02639 QString IncidenceFormatter::formatICalInvitation( QString invitation,
02640 Calendar *mCalendar,
02641 InvitationFormatterHelper *helper )
02642 {
02643 return formatICalInvitationHelper( invitation, mCalendar, helper, false, QString() );
02644 }
02645
02646 QString IncidenceFormatter::formatICalInvitationNoHtml( QString invitation,
02647 Calendar *mCalendar,
02648 InvitationFormatterHelper *helper )
02649 {
02650 return formatICalInvitationHelper( invitation, mCalendar, helper, true, QString() );
02651 }
02652
02653 QString IncidenceFormatter::formatICalInvitationNoHtml( QString invitation,
02654 Calendar *mCalendar,
02655 InvitationFormatterHelper *helper,
02656 const QString &sender )
02657 {
02658 return formatICalInvitationHelper( invitation, mCalendar, helper, true, sender );
02659 }
02660
02661
02662
02663
02664
02665
02666
02667
02668
02669 static QString stringProp( KTNEFMessage* tnefMsg, const Q_UINT32& key,
02670 const QString& fallback = QString::null)
02671 {
02672 return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16,
02673 fallback );
02674 }
02675
02676 static QString sNamedProp( KTNEFMessage* tnefMsg, const QString& name,
02677 const QString& fallback = QString::null )
02678 {
02679 return tnefMsg->findNamedProp( name, fallback );
02680 }
02681
02682 struct save_tz { char* old_tz; char* tz_env_str; };
02683
02684
02685 static struct save_tz set_tz( const char* _tc )
02686 {
02687 const char *tc = _tc?_tc:"UTC";
02688
02689 struct save_tz rv;
02690
02691 rv.old_tz = 0;
02692 rv.tz_env_str = 0;
02693
02694
02695
02696 char* tz_env = 0;
02697 if( getenv( "TZ" ) ) {
02698 tz_env = strdup( getenv( "TZ" ) );
02699 rv.old_tz = tz_env;
02700 }
02701 char* tmp_env = (char*)malloc( strlen( tc ) + 4 );
02702 strcpy( tmp_env, "TZ=" );
02703 strcpy( tmp_env+3, tc );
02704 putenv( tmp_env );
02705
02706 rv.tz_env_str = tmp_env;
02707
02708
02709
02710 tzset();
02711
02712
02713 return rv;
02714 }
02715
02716
02717 static void unset_tz( struct save_tz old_tz )
02718 {
02719 if( old_tz.old_tz ) {
02720 char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 );
02721 strcpy( tmp_env, "TZ=" );
02722 strcpy( tmp_env+3, old_tz.old_tz );
02723 putenv( tmp_env );
02724
02725 free( old_tz.old_tz );
02726 } else {
02727
02728 putenv( strdup("TZ") );
02729 }
02730 tzset();
02731
02732
02733 if( old_tz.tz_env_str ) free( old_tz.tz_env_str );
02734 }
02735
02736 static QDateTime utc2Local( const QDateTime& utcdt )
02737 {
02738 struct tm tmL;
02739
02740 save_tz tmp_tz = set_tz("UTC");
02741 time_t utc = utcdt.toTime_t();
02742 unset_tz( tmp_tz );
02743
02744 localtime_r( &utc, &tmL );
02745 return QDateTime( QDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ),
02746 QTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) );
02747 }
02748
02749
02750 static QDateTime pureISOToLocalQDateTime( const QString& dtStr,
02751 bool bDateOnly = false )
02752 {
02753 QDate tmpDate;
02754 QTime tmpTime;
02755 int year, month, day, hour, minute, second;
02756
02757 if( bDateOnly ) {
02758 year = dtStr.left( 4 ).toInt();
02759 month = dtStr.mid( 4, 2 ).toInt();
02760 day = dtStr.mid( 6, 2 ).toInt();
02761 hour = 0;
02762 minute = 0;
02763 second = 0;
02764 } else {
02765 year = dtStr.left( 4 ).toInt();
02766 month = dtStr.mid( 4, 2 ).toInt();
02767 day = dtStr.mid( 6, 2 ).toInt();
02768 hour = dtStr.mid( 9, 2 ).toInt();
02769 minute = dtStr.mid( 11, 2 ).toInt();
02770 second = dtStr.mid( 13, 2 ).toInt();
02771 }
02772 tmpDate.setYMD( year, month, day );
02773 tmpTime.setHMS( hour, minute, second );
02774
02775 if( tmpDate.isValid() && tmpTime.isValid() ) {
02776 QDateTime dT = QDateTime( tmpDate, tmpTime );
02777
02778 if( !bDateOnly ) {
02779
02780 if (dtStr.at(dtStr.length()-1) == 'Z') {
02781
02782
02783 dT = utc2Local( dT );
02784 }
02785 }
02786 return dT;
02787 } else
02788 return QDateTime();
02789 }
02790
02791
02792
02793 QString IncidenceFormatter::msTNEFToVPart( const QByteArray& tnef )
02794 {
02795 bool bOk = false;
02796
02797 KTNEFParser parser;
02798 QBuffer buf( tnef );
02799 CalendarLocal cal ( QString::fromLatin1( "UTC" ) );
02800 KABC::Addressee addressee;
02801 KABC::VCardConverter cardConv;
02802 ICalFormat calFormat;
02803 Event* event = new Event();
02804
02805 if( parser.openDevice( &buf ) ) {
02806 KTNEFMessage* tnefMsg = parser.message();
02807
02808
02809
02810
02811 QString msgClass = tnefMsg->findProp( 0x001A, QString::null, true )
02812 .upper();
02813 if( !msgClass.isEmpty() ) {
02814
02815
02816 bool bCompatClassAppointment = false;
02817 bool bCompatMethodRequest = false;
02818 bool bCompatMethodCancled = false;
02819 bool bCompatMethodAccepted = false;
02820 bool bCompatMethodAcceptedCond = false;
02821 bool bCompatMethodDeclined = false;
02822 if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) {
02823 bCompatClassAppointment = true;
02824 if( msgClass.endsWith( ".MTGREQ" ) )
02825 bCompatMethodRequest = true;
02826 if( msgClass.endsWith( ".MTGCNCL" ) )
02827 bCompatMethodCancled = true;
02828 if( msgClass.endsWith( ".MTGRESPP" ) )
02829 bCompatMethodAccepted = true;
02830 if( msgClass.endsWith( ".MTGRESPA" ) )
02831 bCompatMethodAcceptedCond = true;
02832 if( msgClass.endsWith( ".MTGRESPN" ) )
02833 bCompatMethodDeclined = true;
02834 }
02835 bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" );
02836
02837 if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) {
02838
02839 bool bIsReply = false;
02840 QString prodID = "-//Microsoft Corporation//Outlook ";
02841 prodID += tnefMsg->findNamedProp( "0x8554", "9.0" );
02842 prodID += "MIMEDIR/EN\n";
02843 prodID += "VERSION:2.0\n";
02844 calFormat.setApplication( "Outlook", prodID );
02845
02846 Scheduler::Method method;
02847 if( bCompatMethodRequest )
02848 method = Scheduler::Request;
02849 else if( bCompatMethodCancled )
02850 method = Scheduler::Cancel;
02851 else if( bCompatMethodAccepted || bCompatMethodAcceptedCond ||
02852 bCompatMethodDeclined ) {
02853 method = Scheduler::Reply;
02854 bIsReply = true;
02855 } else {
02856
02857
02858
02859
02860
02861
02862
02863
02864 if( tnefMsg->findProp(0x0c17) == "1" )
02865 bIsReply = true;
02866 method = Scheduler::Request;
02867 }
02868
02870 ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown );
02871
02872 QString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) );
02873
02874 if( !sSenderSearchKeyEmail.isEmpty() ) {
02875 int colon = sSenderSearchKeyEmail.find( ':' );
02876
02877 if( sSenderSearchKeyEmail.find( ':' ) == -1 )
02878 sSenderSearchKeyEmail.remove( 0, colon+1 );
02879 }
02880
02881 QString s( tnefMsg->findProp( 0x0e04 ) );
02882 QStringList attendees = QStringList::split( ';', s );
02883 if( attendees.count() ) {
02884 for( QStringList::Iterator it = attendees.begin();
02885 it != attendees.end(); ++it ) {
02886
02887
02888 if( (*it).find('@') == -1 ) {
02889 s = (*it).stripWhiteSpace();
02890
02891 Attendee *attendee = new Attendee( s, s, true );
02892 if( bIsReply ) {
02893 if( bCompatMethodAccepted )
02894 attendee->setStatus( Attendee::Accepted );
02895 if( bCompatMethodDeclined )
02896 attendee->setStatus( Attendee::Declined );
02897 if( bCompatMethodAcceptedCond )
02898 attendee->setStatus(Attendee::Tentative);
02899 } else {
02900 attendee->setStatus( Attendee::NeedsAction );
02901 attendee->setRole( Attendee::ReqParticipant );
02902 }
02903 event->addAttendee(attendee);
02904 }
02905 }
02906 } else {
02907
02908
02909 s = sSenderSearchKeyEmail;
02910 if( !s.isEmpty() ) {
02911 Attendee *attendee = new Attendee( QString::null, QString::null,
02912 true );
02913 if( bIsReply ) {
02914 if( bCompatMethodAccepted )
02915 attendee->setStatus( Attendee::Accepted );
02916 if( bCompatMethodAcceptedCond )
02917 attendee->setStatus( Attendee::Declined );
02918 if( bCompatMethodDeclined )
02919 attendee->setStatus( Attendee::Tentative );
02920 } else {
02921 attendee->setStatus(Attendee::NeedsAction);
02922 attendee->setRole(Attendee::ReqParticipant);
02923 }
02924 event->addAttendee(attendee);
02925 }
02926 }
02927 s = tnefMsg->findProp( 0x0c1f );
02928 if( s.isEmpty() && !bIsReply )
02929 s = sSenderSearchKeyEmail;
02930
02931 if( !s.isEmpty() )
02932 event->setOrganizer( s );
02933
02934 s = tnefMsg->findProp( 0x8516 ).replace( QChar( '-' ), QString::null )
02935 .replace( QChar( ':' ), QString::null );
02936 event->setDtStart( QDateTime::fromString( s ) );
02937
02938 s = tnefMsg->findProp( 0x8517 ).replace( QChar( '-' ), QString::null )
02939 .replace( QChar( ':' ), QString::null );
02940 event->setDtEnd( QDateTime::fromString( s ) );
02941
02942 s = tnefMsg->findProp( 0x8208 );
02943 event->setLocation( s );
02944
02945
02946
02947
02948
02949
02950 s = tnefMsg->findProp( 0x0023 );
02951 event->setUid( s );
02952
02953
02954
02955
02956 s = tnefMsg->findProp( 0x8202 ).replace( QChar( '-' ), QString::null )
02957 .replace( QChar( ':' ), QString::null );
02958
02959
02960
02961 s = tnefMsg->findNamedProp( "Keywords" );
02962 event->setCategories( s );
02963
02964 s = tnefMsg->findProp( 0x1000 );
02965 event->setDescription( s );
02966
02967 s = tnefMsg->findProp( 0x0070 );
02968 event->setSummary( s );
02969
02970 s = tnefMsg->findProp( 0x0026 );
02971 event->setPriority( s.toInt() );
02972
02973
02974 if(!tnefMsg->findProp(0x8503).isEmpty()) {
02975 Alarm *alarm = new Alarm(event);
02976 QDateTime highNoonTime =
02977 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8502 )
02978 .replace( QChar( '-' ), "" )
02979 .replace( QChar( ':' ), "" ) );
02980 QDateTime wakeMeUpTime =
02981 pureISOToLocalQDateTime( tnefMsg->findProp( 0x8560, "" )
02982 .replace( QChar( '-' ), "" )
02983 .replace( QChar( ':' ), "" ) );
02984 alarm->setTime(wakeMeUpTime);
02985
02986 if( highNoonTime.isValid() && wakeMeUpTime.isValid() )
02987 alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) );
02988 else
02989
02990 alarm->setStartOffset( Duration( 15*60 ) );
02991 alarm->setDisplayAlarm( i18n( "Reminder" ) );
02992
02993
02994
02995 event->addAlarm( alarm );
02996 }
02997 cal.addEvent( event );
02998 bOk = true;
02999
03000 } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) {
03001 addressee.setUid( stringProp( tnefMsg, attMSGID ) );
03002 addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) );
03003 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true );
03004 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false );
03005 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false );
03006 addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) );
03007 addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) );
03008 addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) );
03009 addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) );
03010 addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) );
03011 addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) );
03012 addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) );
03013
03014 QString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY )
03015 .replace( QChar( '-' ), QString::null )
03016 .replace( QChar( ':' ), QString::null );
03017 if( !s.isEmpty() )
03018 addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s );
03019
03020 addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE ) ) );
03021
03022
03023 addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) );
03024 addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) );
03025 addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) );
03026 addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) );
03027 addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) );
03028
03029 addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) );
03030 addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) );
03031 addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) );
03032
03033
03034
03035
03036
03037 KABC::Address adr;
03038 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) );
03039 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) );
03040 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) );
03041 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) );
03042 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) );
03043 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) );
03044 adr.setType(KABC::Address::Home);
03045 addressee.insertAddress(adr);
03046
03047 adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) );
03048 adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) );
03049 adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) );
03050 adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) );
03051 adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) );
03052 adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) );
03053 adr.setType( KABC::Address::Work );
03054 addressee.insertAddress( adr );
03055
03056 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) );
03057 adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) );
03058 adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) );
03059 adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) );
03060 adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) );
03061 adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) );
03062 adr.setType( KABC::Address::Dom );
03063 addressee.insertAddress(adr);
03064
03065
03066
03067
03068
03069 QString nr;
03070 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER );
03071 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) );
03072 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER );
03073 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) );
03074 nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER );
03075 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) );
03076 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER );
03077 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) );
03078 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER );
03079 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) );
03080
03081 s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY )
03082 .replace( QChar( '-' ), QString::null )
03083 .replace( QChar( ':' ), QString::null );
03084 if( !s.isEmpty() )
03085 addressee.setBirthday( QDateTime::fromString( s ) );
03086
03087 bOk = ( !addressee.isEmpty() );
03088 } else if( "IPM.NOTE" == msgClass ) {
03089
03090 }
03091 }
03092 }
03093
03094
03095 QString iCal = calFormat.toString( &cal );
03096 if( !iCal.isEmpty() )
03097
03098 return iCal;
03099
03100
03101 KABC::VCardConverter converter;
03102 return converter.createVCard( addressee );
03103 }
03104
03105
03106 QString IncidenceFormatter::formatTNEFInvitation( const QByteArray& tnef,
03107 Calendar *mCalendar, InvitationFormatterHelper *helper )
03108 {
03109 QString vPart = IncidenceFormatter::msTNEFToVPart( tnef );
03110 QString iCal = IncidenceFormatter::formatICalInvitation( vPart, mCalendar, helper );
03111 if( !iCal.isEmpty() )
03112 return iCal;
03113 return vPart;
03114 }
03115
03116
03117
03118
03119
03120
03121
03122
03123 class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor
03124 {
03125 public:
03126 ToolTipVisitor()
03127 : mCalendar( 0 ), mRichText( true ), mResult( "" ) {}
03128
03129 bool act( Calendar *calendar, IncidenceBase *incidence,
03130 const QDate &date=QDate(), bool richText=true )
03131 {
03132 mCalendar = calendar;
03133 mDate = date;
03134 mRichText = richText;
03135 mResult = "";
03136 return incidence ? incidence->accept( *this ) : false;
03137 }
03138 QString result() const { return mResult; }
03139
03140 protected:
03141 bool visit( Event *event );
03142 bool visit( Todo *todo );
03143 bool visit( Journal *journal );
03144 bool visit( FreeBusy *fb );
03145
03146 QString dateRangeText( Event *event, const QDate &date );
03147 QString dateRangeText( Todo *todo, const QDate &date );
03148 QString dateRangeText( Journal *journal );
03149 QString dateRangeText( FreeBusy *fb );
03150
03151 QString generateToolTip( Incidence* incidence, QString dtRangeText );
03152
03153 protected:
03154 Calendar *mCalendar;
03155 QDate mDate;
03156 bool mRichText;
03157 QString mResult;
03158 };
03159
03160 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const QDate &date )
03161 {
03162 QString ret;
03163 QString tmp;
03164
03165 QDateTime startDt = event->dtStart();
03166 QDateTime endDt = event->dtEnd();
03167 if ( event->doesRecur() ) {
03168 if ( date.isValid() ) {
03169 QDateTime dt( date, QTime( 0, 0, 0 ) );
03170 int diffDays = startDt.daysTo( dt );
03171 dt = dt.addSecs( -1 );
03172 startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() );
03173 if ( event->hasEndDate() ) {
03174 endDt = endDt.addDays( diffDays );
03175 if ( startDt > endDt ) {
03176 startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() );
03177 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
03178 }
03179 }
03180 }
03181 }
03182 if ( event->isMultiDay() ) {
03183
03184 tmp = "<br>" + i18n("Event start", "<i>From:</i> %1");
03185 if (event->doesFloat())
03186 ret += tmp.arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") );
03187 else
03188 ret += tmp.arg( IncidenceFormatter::dateToString( startDt ).replace(" ", " ") );
03189
03190 tmp = "<br>" + i18n("Event end","<i>To:</i> %1");
03191 if (event->doesFloat())
03192 ret += tmp.arg( IncidenceFormatter::dateToString( endDt, false ).replace(" ", " ") );
03193 else
03194 ret += tmp.arg( IncidenceFormatter::dateToString( endDt ).replace(" ", " ") );
03195
03196 } else {
03197
03198 ret += "<br>"+i18n("<i>Date:</i> %1").
03199 arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") );
03200 if ( !event->doesFloat() ) {
03201 const QString dtStartTime =
03202 IncidenceFormatter::timeToString( startDt, true ).replace( " ", " " );
03203 const QString dtEndTime =
03204 IncidenceFormatter::timeToString( endDt, true ).replace( " ", " " );
03205 if ( dtStartTime == dtEndTime ) {
03206 tmp = "<br>" + i18n("time for event, to prevent ugly line breaks",
03207 "<i>Time:</i> %1").
03208 arg( dtStartTime );
03209 } else {
03210 tmp = "<br>" + i18n("time range for event, to prevent ugly line breaks",
03211 "<i>Time:</i> %1 - %2").
03212 arg( dtStartTime, dtEndTime );
03213 }
03214 ret += tmp;
03215 }
03216
03217 }
03218 return ret;
03219 }
03220
03221 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const QDate &date )
03222 {
03223 QString ret;
03224 bool floats( todo->doesFloat() );
03225
03226 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
03227 QDateTime startDt = todo->dtStart();
03228 if ( todo->doesRecur() ) {
03229 if ( date.isValid() ) {
03230 startDt.setDate( date );
03231 }
03232 }
03233 ret += "<br>" +
03234 i18n("<i>Start:</i> %1").
03235 arg( IncidenceFormatter::dateTimeToString( startDt, floats, false ).
03236 replace( " ", " " ) );
03237 }
03238
03239 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
03240 QDateTime dueDt = todo->dtDue();
03241 if ( todo->doesRecur() ) {
03242 if ( date.isValid() ) {
03243 QDateTime dt( date, QTime( 0, 0, 0 ) );
03244 dt = dt.addSecs( -1 );
03245 dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() );
03246 }
03247 }
03248 ret += "<br>" +
03249 i18n("<i>Due:</i> %1").
03250 arg( IncidenceFormatter::dateTimeToString( dueDt, floats, false ).
03251 replace( " ", " " ) );
03252 }
03253
03254
03255
03256 if ( todo->priority() > 0 ) {
03257 ret += "<br>";
03258 ret += "<i>" + i18n( "Priority:" ) + "</i>" + " ";
03259 ret += QString::number( todo->priority() );
03260 }
03261
03262 ret += "<br>";
03263 if ( todo->isCompleted() ) {
03264 ret += "<i>" + i18n( "Completed:" ) + "</i>" + " ";
03265 ret += todo->completedStr().replace( " ", " " );
03266 } else {
03267 ret += "<i>" + i18n( "Percent Done:" ) + "</i>" + " ";
03268 ret += i18n( "%1%" ).arg( todo->percentComplete() );
03269 }
03270
03271 return ret;
03272 }
03273
03274 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal )
03275 {
03276 QString ret;
03277 if (journal->dtStart().isValid() ) {
03278 ret += "<br>" +
03279 i18n("<i>Date:</i> %1").
03280 arg( IncidenceFormatter::dateToString( journal->dtStart(), false ) );
03281 }
03282 return ret;
03283 }
03284
03285 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
03286 {
03287 QString tmp( "<br>" + i18n("<i>Period start:</i> %1") );
03288 QString ret = tmp.arg( KGlobal::locale()->formatDateTime( fb->dtStart() ) );
03289 tmp = "<br>" + i18n("<i>Period start:</i> %1");
03290 ret += tmp.arg( KGlobal::locale()->formatDateTime( fb->dtEnd() ) );
03291 return ret;
03292 }
03293
03294
03295
03296 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
03297 {
03298 mResult = generateToolTip( event, dateRangeText( event, mDate ) );
03299 return !mResult.isEmpty();
03300 }
03301
03302 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
03303 {
03304 mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
03305 return !mResult.isEmpty();
03306 }
03307
03308 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
03309 {
03310 mResult = generateToolTip( journal, dateRangeText( journal ) );
03311 return !mResult.isEmpty();
03312 }
03313
03314 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
03315 {
03316 mResult = "<qt><b>" + i18n("Free/Busy information for %1")
03317 .arg(fb->organizer().fullName()) + "</b>";
03318 mResult += dateRangeText( fb );
03319 mResult += "</qt>";
03320 return !mResult.isEmpty();
03321 }
03322
03323 static QString tooltipPerson( const QString& email, QString name )
03324 {
03325
03326
03327 if ( name.isEmpty() && !email.isEmpty() ) {
03328 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
03329 KABC::Addressee::List addressList = add_book->findByEmail( email );
03330 if ( !addressList.isEmpty() ) {
03331 KABC::Addressee o = addressList.first();
03332 if ( !o.isEmpty() && addressList.size() < 2 ) {
03333
03334 name = o.formattedName();
03335 }
03336 }
03337 }
03338
03339
03340 QString tmpString = ( name.isEmpty() ? email : name );
03341
03342 return tmpString;
03343 }
03344
03345 static QString etc = i18n( "elipsis", "..." );
03346 static QString tooltipFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
03347 {
03348 int maxNumAtts = 8;
03349 QString sep = i18n( "separator for lists of people names", ", " );
03350 int sepLen = sep.length();
03351
03352 int i = 0;
03353 QString tmpStr;
03354 Attendee::List::ConstIterator it;
03355 Attendee::List attendees = incidence->attendees();
03356
03357 for( it = attendees.begin(); it != attendees.end(); ++it ) {
03358 Attendee *a = *it;
03359 if ( a->role() != role ) {
03360
03361 continue;
03362 }
03363 if ( a->email() == incidence->organizer().email() ) {
03364
03365 continue;
03366 }
03367 if ( i == maxNumAtts ) {
03368 tmpStr += etc;
03369 break;
03370 }
03371 tmpStr += tooltipPerson( a->email(), a->name() );
03372 if ( !a->delegator().isEmpty() ) {
03373 tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() );
03374 }
03375 if ( !a->delegate().isEmpty() ) {
03376 tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() );
03377 }
03378 tmpStr += sep;
03379 i++;
03380 }
03381 if ( tmpStr.endsWith( sep ) ) {
03382 tmpStr.truncate( tmpStr.length() - sepLen );
03383 }
03384 return tmpStr;
03385 }
03386
03387 static QString tooltipFormatAttendees( Incidence *incidence )
03388 {
03389 QString tmpStr, str;
03390
03391
03392 int attendeeCount = incidence->attendees().count();
03393 if ( attendeeCount > 1 ||
03394 ( attendeeCount == 1 &&
03395 incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
03396 tmpStr += "<i>" + i18n( "Organizer:" ) + "</i>" + " ";
03397 tmpStr += tooltipPerson( incidence->organizer().email(),
03398 incidence->organizer().name() );
03399 }
03400
03401
03402 str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair );
03403 if ( !str.isEmpty() ) {
03404 tmpStr += "<br><i>" + i18n( "Chair:" ) + "</i>" + " ";
03405 tmpStr += str;
03406 }
03407
03408
03409 str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
03410 if ( !str.isEmpty() ) {
03411 tmpStr += "<br><i>" + i18n( "Required Participants:" ) + "</i>" + " ";
03412 tmpStr += str;
03413 }
03414
03415
03416 str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
03417 if ( !str.isEmpty() ) {
03418 tmpStr += "<br><i>" + i18n( "Optional Participants:" ) + "</i>" + " ";
03419 tmpStr += str;
03420 }
03421
03422
03423 str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
03424 if ( !str.isEmpty() ) {
03425 tmpStr += "<br><i>" + i18n( "Observers:" ) + "</i>" + " ";
03426 tmpStr += str;
03427 }
03428
03429 return tmpStr;
03430 }
03431
03432 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, QString dtRangeText )
03433 {
03434 uint maxDescLen = 120;
03435
03436 if ( !incidence ) {
03437 return QString::null;
03438 }
03439
03440 QString tmp = "<qt>";
03441
03442
03443 tmp += "<b>" + incidence->summary().replace( "\n", "<br>" ) + "</b>";
03444
03445 tmp += "<br>----------<br>";
03446
03447 if ( mCalendar ) {
03448 QString calStr = IncidenceFormatter::resourceString( mCalendar, incidence );
03449 if ( !calStr.isEmpty() ) {
03450 tmp += "<i>" + i18n( "Calendar:" ) + "</i>" + " ";
03451 tmp += calStr;
03452 }
03453 }
03454
03455 tmp += dtRangeText;
03456
03457 if ( !incidence->location().isEmpty() ) {
03458 tmp += "<br>";
03459 tmp += "<i>" + i18n( "Location:" ) + "</i>" + " ";
03460 tmp += incidence->location().replace( "\n", "<br>" );
03461 }
03462
03463 QString durStr = IncidenceFormatter::durationString( incidence );
03464 if ( !durStr.isEmpty() ) {
03465 tmp += "<br>";
03466 tmp += "<i>" + i18n( "Duration:" ) + "</i>" + " ";
03467 tmp += durStr;
03468 }
03469
03470 if ( incidence->doesRecur() ) {
03471 tmp += "<br>";
03472 tmp += "<i>" + i18n( "Recurrence:" ) + "</i>" + " ";
03473 tmp += IncidenceFormatter::recurrenceString( incidence );
03474 }
03475
03476 if ( !incidence->description().isEmpty() ) {
03477 QString desc( incidence->description() );
03478 if ( desc.length() > maxDescLen ) {
03479 desc = desc.left( maxDescLen ) + etc;
03480 }
03481 tmp += "<br>----------<br>";
03482 tmp += "<i>" + i18n( "Description:" ) + "</i>" + "<br>";
03483 tmp += desc.replace( "\n", "<br>" );
03484 tmp += "<br>----------";
03485 }
03486
03487 int reminderCount = incidence->alarms().count();
03488 if ( reminderCount > 0 && incidence->isAlarmEnabled() ) {
03489 tmp += "<br>";
03490 tmp += "<i>" + i18n( "Reminder:", "%n Reminders:", reminderCount ) + "</i>" + " ";
03491 tmp += IncidenceFormatter::reminderStringList( incidence ).join( ", " );
03492 }
03493
03494 tmp += "<br>";
03495 tmp += tooltipFormatAttendees( incidence );
03496
03497 int categoryCount = incidence->categories().count();
03498 if ( categoryCount > 0 ) {
03499 tmp += "<br>";
03500 tmp += "<i>" + i18n( "Category:", "%n Categories:", categoryCount ) + "</i>" + " ";
03501 tmp += incidence->categories().join( ", " );
03502 }
03503
03504 tmp += "</qt>";
03505 return tmp;
03506 }
03507
03508 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText )
03509 {
03510 return toolTipStr( 0, incidence, QDate(), richText );
03511 }
03512
03513 QString IncidenceFormatter::toolTipStr( Calendar *calendar,
03514 IncidenceBase *incidence,
03515 const QDate &date,
03516 bool richText )
03517 {
03518 ToolTipVisitor v;
03519 if ( v.act( calendar, incidence, date, richText ) ) {
03520 return v.result();
03521 } else {
03522 return QString::null;
03523 }
03524 }
03525
03526
03527
03528
03529
03530 class IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor
03531 {
03532 public:
03533 MailBodyVisitor() : mResult( "" ) {}
03534
03535 bool act( IncidenceBase *incidence )
03536 {
03537 mResult = "";
03538 return incidence ? incidence->accept( *this ) : false;
03539 }
03540 QString result() const { return mResult; }
03541
03542 protected:
03543 bool visit( Event *event );
03544 bool visit( Todo *todo );
03545 bool visit( Journal *journal );
03546 bool visit( FreeBusy * ) { mResult = i18n("This is a Free Busy Object"); return !mResult.isEmpty(); }
03547 protected:
03548 QString mResult;
03549 };
03550
03551
03552 static QString mailBodyIncidence( Incidence *incidence )
03553 {
03554 QString body;
03555 if ( !incidence->summary().isEmpty() ) {
03556 body += i18n("Summary: %1\n").arg( incidence->summary() );
03557 }
03558 if ( !incidence->organizer().isEmpty() ) {
03559 body += i18n("Organizer: %1\n").arg( incidence->organizer().fullName() );
03560 }
03561 if ( !incidence->location().isEmpty() ) {
03562 body += i18n("Location: %1\n").arg( incidence->location() );
03563 }
03564 return body;
03565 }
03566
03567 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
03568 {
03569 QString recurrence[]= {i18n("no recurrence", "None"),
03570 i18n("Minutely"), i18n("Hourly"), i18n("Daily"),
03571 i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"),
03572 i18n("Yearly"), i18n("Yearly"), i18n("Yearly")};
03573
03574 mResult = mailBodyIncidence( event );
03575 mResult += i18n("Start Date: %1\n").
03576 arg( IncidenceFormatter::dateToString( event->dtStart(), true ) );
03577 if ( !event->doesFloat() ) {
03578 mResult += i18n("Start Time: %1\n").
03579 arg( IncidenceFormatter::timeToString( event->dtStart(), true ) );
03580 }
03581 if ( event->dtStart() != event->dtEnd() ) {
03582 mResult += i18n("End Date: %1\n").
03583 arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) );
03584 }
03585 if ( !event->doesFloat() ) {
03586 mResult += i18n("End Time: %1\n").
03587 arg( IncidenceFormatter::timeToString( event->dtEnd(), true ) );
03588 }
03589 if ( event->doesRecur() ) {
03590 Recurrence *recur = event->recurrence();
03591
03592 mResult += i18n("Recurs: %1\n")
03593 .arg( recurrence[ recur->recurrenceType() ] );
03594 mResult += i18n("Frequency: %1\n")
03595 .arg( event->recurrence()->frequency() );
03596
03597 if ( recur->duration() > 0 ) {
03598 mResult += i18n ("Repeats once", "Repeats %n times", recur->duration());
03599 mResult += '\n';
03600 } else {
03601 if ( recur->duration() != -1 ) {
03602
03603 QString endstr;
03604 if ( event->doesFloat() ) {
03605 endstr = KGlobal::locale()->formatDate( recur->endDate() );
03606 } else {
03607 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime() );
03608 }
03609 mResult += i18n("Repeat until: %1\n").arg( endstr );
03610 } else {
03611 mResult += i18n("Repeats forever\n");
03612 }
03613 }
03614 }
03615 QString details = event->description();
03616 if ( !details.isEmpty() ) {
03617 mResult += i18n("Details:\n%1\n").arg( details );
03618 }
03619 return !mResult.isEmpty();
03620 }
03621
03622 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
03623 {
03624 mResult = mailBodyIncidence( todo );
03625
03626 if ( todo->hasStartDate() ) {
03627 mResult += i18n("Start Date: %1\n").
03628 arg( IncidenceFormatter::dateToString( todo->dtStart( false ), true ) );
03629 if ( !todo->doesFloat() ) {
03630 mResult += i18n("Start Time: %1\n").
03631 arg( IncidenceFormatter::timeToString( todo->dtStart( false ),true ) );
03632 }
03633 }
03634 if ( todo->hasDueDate() ) {
03635 mResult += i18n("Due Date: %1\n").
03636 arg( IncidenceFormatter::dateToString( todo->dtDue(), true ) );
03637 if ( !todo->doesFloat() ) {
03638 mResult += i18n("Due Time: %1\n").
03639 arg( IncidenceFormatter::timeToString( todo->dtDue(), true ) );
03640 }
03641 }
03642 QString details = todo->description();
03643 if ( !details.isEmpty() ) {
03644 mResult += i18n("Details:\n%1\n").arg( details );
03645 }
03646 return !mResult.isEmpty();
03647 }
03648
03649 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
03650 {
03651 mResult = mailBodyIncidence( journal );
03652 mResult += i18n("Date: %1\n").
03653 arg( IncidenceFormatter::dateToString( journal->dtStart(), true ) );
03654 if ( !journal->doesFloat() ) {
03655 mResult += i18n("Time: %1\n").
03656 arg( IncidenceFormatter::timeToString( journal->dtStart(), true ) );
03657 }
03658 if ( !journal->description().isEmpty() )
03659 mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() );
03660 return !mResult.isEmpty();
03661 }
03662
03663
03664
03665 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
03666 {
03667 if ( !incidence )
03668 return QString::null;
03669
03670 MailBodyVisitor v;
03671 if ( v.act( incidence ) ) {
03672 return v.result();
03673 }
03674 return QString::null;
03675 }
03676
03677 static QString recurEnd( Incidence *incidence )
03678 {
03679 QString endstr;
03680 if ( incidence->doesFloat() ) {
03681 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
03682 } else {
03683 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
03684 }
03685 return endstr;
03686 }
03687
03688
03689
03690
03691 QString IncidenceFormatter::recurrenceString( Incidence *incidence )
03692 {
03693 if ( !incidence->doesRecur() ) {
03694 return i18n( "No recurrence" );
03695 }
03696 QStringList dayList;
03697 dayList.append( i18n( "31st Last" ) );
03698 dayList.append( i18n( "30th Last" ) );
03699 dayList.append( i18n( "29th Last" ) );
03700 dayList.append( i18n( "28th Last" ) );
03701 dayList.append( i18n( "27th Last" ) );
03702 dayList.append( i18n( "26th Last" ) );
03703 dayList.append( i18n( "25th Last" ) );
03704 dayList.append( i18n( "24th Last" ) );
03705 dayList.append( i18n( "23rd Last" ) );
03706 dayList.append( i18n( "22nd Last" ) );
03707 dayList.append( i18n( "21st Last" ) );
03708 dayList.append( i18n( "20th Last" ) );
03709 dayList.append( i18n( "19th Last" ) );
03710 dayList.append( i18n( "18th Last" ) );
03711 dayList.append( i18n( "17th Last" ) );
03712 dayList.append( i18n( "16th Last" ) );
03713 dayList.append( i18n( "15th Last" ) );
03714 dayList.append( i18n( "14th Last" ) );
03715 dayList.append( i18n( "13th Last" ) );
03716 dayList.append( i18n( "12th Last" ) );
03717 dayList.append( i18n( "11th Last" ) );
03718 dayList.append( i18n( "10th Last" ) );
03719 dayList.append( i18n( "9th Last" ) );
03720 dayList.append( i18n( "8th Last" ) );
03721 dayList.append( i18n( "7th Last" ) );
03722 dayList.append( i18n( "6th Last" ) );
03723 dayList.append( i18n( "5th Last" ) );
03724 dayList.append( i18n( "4th Last" ) );
03725 dayList.append( i18n( "3rd Last" ) );
03726 dayList.append( i18n( "2nd Last" ) );
03727 dayList.append( i18n( "last day of the month", "Last" ) );
03728 dayList.append( i18n( "unknown day of the month", "unknown" ) );
03729 dayList.append( i18n( "1st" ) );
03730 dayList.append( i18n( "2nd" ) );
03731 dayList.append( i18n( "3rd" ) );
03732 dayList.append( i18n( "4th" ) );
03733 dayList.append( i18n( "5th" ) );
03734 dayList.append( i18n( "6th" ) );
03735 dayList.append( i18n( "7th" ) );
03736 dayList.append( i18n( "8th" ) );
03737 dayList.append( i18n( "9th" ) );
03738 dayList.append( i18n( "10th" ) );
03739 dayList.append( i18n( "11th" ) );
03740 dayList.append( i18n( "12th" ) );
03741 dayList.append( i18n( "13th" ) );
03742 dayList.append( i18n( "14th" ) );
03743 dayList.append( i18n( "15th" ) );
03744 dayList.append( i18n( "16th" ) );
03745 dayList.append( i18n( "17th" ) );
03746 dayList.append( i18n( "18th" ) );
03747 dayList.append( i18n( "19th" ) );
03748 dayList.append( i18n( "20th" ) );
03749 dayList.append( i18n( "21st" ) );
03750 dayList.append( i18n( "22nd" ) );
03751 dayList.append( i18n( "23rd" ) );
03752 dayList.append( i18n( "24th" ) );
03753 dayList.append( i18n( "25th" ) );
03754 dayList.append( i18n( "26th" ) );
03755 dayList.append( i18n( "27th" ) );
03756 dayList.append( i18n( "28th" ) );
03757 dayList.append( i18n( "29th" ) );
03758 dayList.append( i18n( "30th" ) );
03759 dayList.append( i18n( "31st" ) );
03760 int weekStart = KGlobal::locale()->weekStartDay();
03761 QString dayNames;
03762 QString recurStr, txt;
03763 const KCalendarSystem *calSys = KGlobal::locale()->calendar();
03764 Recurrence *recur = incidence->recurrence();
03765 switch ( recur->recurrenceType() ) {
03766 case Recurrence::rNone:
03767 return i18n( "No recurrence" );
03768
03769 case Recurrence::rMinutely:
03770 recurStr = i18n( "Recurs every minute", "Recurs every %n minutes", recur->frequency() );
03771 if ( recur->duration() != -1 ) {
03772 txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) );
03773 if ( recur->duration() > 0 ) {
03774 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03775 }
03776 return txt;
03777 }
03778 return recurStr;
03779
03780 case Recurrence::rHourly:
03781 recurStr = i18n( "Recurs hourly", "Recurs every %n hours", recur->frequency() );
03782 if ( recur->duration() != -1 ) {
03783 txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) );
03784 if ( recur->duration() > 0 ) {
03785 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03786 }
03787 return txt;
03788 }
03789 return recurStr;
03790
03791 case Recurrence::rDaily:
03792 recurStr = i18n( "Recurs daily", "Recurs every %n days", recur->frequency() );
03793 if ( recur->duration() != -1 ) {
03794
03795 txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) );
03796 if ( recur->duration() > 0 ) {
03797 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03798 }
03799 return txt;
03800 }
03801 return recurStr;
03802
03803 case Recurrence::rWeekly:
03804 {
03805 recurStr = i18n( "Recurs weekly", "Recurs every %n weeks", recur->frequency() );
03806
03807 bool addSpace = false;
03808 for ( int i = 0; i < 7; ++i ) {
03809 if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
03810 if ( addSpace ) {
03811 dayNames.append( i18n( "separator for list of days", ", " ) );
03812 }
03813 dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1, true ) );
03814 addSpace = true;
03815 }
03816 }
03817 if ( dayNames.isEmpty() ) {
03818 dayNames = i18n( "Recurs weekly on no days", "no days" );
03819 }
03820 if ( recur->duration() != -1 ) {
03821 txt = i18n( "%1 on %2 until %3" ).
03822 arg( recurStr ).arg( dayNames ).arg( recurEnd( incidence ) );
03823 if ( recur->duration() > 0 ) {
03824 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03825 }
03826 return txt;
03827 }
03828 txt = i18n( "%1 on %2" ).arg( recurStr ).arg( dayNames );
03829 return txt;
03830 }
03831 case Recurrence::rMonthlyPos:
03832 {
03833 recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() );
03834
03835 if ( !recur->monthPositions().isEmpty() ) {
03836 KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0];
03837 if ( recur->duration() != -1 ) {
03838 txt = i18n( "%1 on the %2 %3 until %4" ).
03839 arg( recurStr ).
03840 arg( dayList[rule.pos() + 31] ).
03841 arg( calSys->weekDayName( rule.day(), false ) ).
03842 arg( recurEnd( incidence ) );
03843 if ( recur->duration() > 0 ) {
03844 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03845 }
03846 return txt;
03847 }
03848 txt = i18n( "%1 on the %2 %3" ).
03849 arg( recurStr ).
03850 arg( dayList[rule.pos() + 31] ).
03851 arg( calSys->weekDayName( rule.day(), false ) );
03852 return txt;
03853 } else {
03854 return recurStr;
03855 }
03856 break;
03857 }
03858 case Recurrence::rMonthlyDay:
03859 {
03860 recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() );
03861
03862 if ( !recur->monthDays().isEmpty() ) {
03863 int days = recur->monthDays()[0];
03864 if ( recur->duration() != -1 ) {
03865 txt = i18n( "%1 on the %2 day until %3" ).
03866 arg( recurStr ).
03867 arg( dayList[days + 31] ).
03868 arg( recurEnd( incidence ) );
03869 if ( recur->duration() > 0 ) {
03870 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03871 }
03872 return txt;
03873 }
03874 txt = i18n( "%1 on the %2 day" ).arg( recurStr ).arg( dayList[days + 31] );
03875 return txt;
03876 } else {
03877 return recurStr;
03878 }
03879 break;
03880 }
03881 case Recurrence::rYearlyMonth:
03882 {
03883 recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() );
03884
03885 if ( recur->duration() != -1 ) {
03886 if ( !recur->yearDates().isEmpty() ) {
03887 txt = i18n( "%1 on %2 %3 until %4" ).
03888 arg( recurStr ).
03889 arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ).
03890 arg( dayList[ recur->yearDates()[0] + 31 ] ).
03891 arg( recurEnd( incidence ) );
03892 if ( recur->duration() > 0 ) {
03893 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03894 }
03895 return txt;
03896 }
03897 }
03898 if ( !recur->yearDates().isEmpty() ) {
03899 txt = i18n( "%1 on %2 %3" ).
03900 arg( recurStr ).
03901 arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ).
03902 arg( dayList[ recur->yearDates()[0] + 31 ] );
03903 return txt;
03904 } else {
03905 if ( !recur->yearMonths().isEmpty() ) {
03906 txt = i18n( "Recurs yearly on %1 %2" ).
03907 arg( calSys->monthName( recur->yearMonths()[0],
03908 recur->startDate().year() ) ).
03909 arg( dayList[ recur->startDate().day() + 31 ] );
03910 } else {
03911 txt = i18n( "Recurs yearly on %1 %2" ).
03912 arg( calSys->monthName( recur->startDate().month(),
03913 recur->startDate().year() ) ).
03914 arg( dayList[ recur->startDate().day() + 31 ] );
03915 }
03916 return txt;
03917 }
03918 break;
03919 }
03920 case Recurrence::rYearlyDay:
03921 {
03922 recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() );
03923 if ( !recur->yearDays().isEmpty() ) {
03924 if ( recur->duration() != -1 ) {
03925 txt = i18n( "%1 on day %2 until %3" ).
03926 arg( recurStr ).
03927 arg( recur->yearDays()[0] ).
03928 arg( recurEnd( incidence ) );
03929 if ( recur->duration() > 0 ) {
03930 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03931 }
03932 return txt;
03933 }
03934 txt = i18n( "%1 on day %2" ).arg( recurStr ).arg( recur->yearDays()[0] );
03935 return txt;
03936 } else {
03937 return recurStr;
03938 }
03939 break;
03940 }
03941 case Recurrence::rYearlyPos:
03942 {
03943 recurStr = i18n( "Every year", "Every %n years", recur->frequency() );
03944 if ( !recur->yearPositions().isEmpty() && !recur->yearMonths().isEmpty() ) {
03945 KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0];
03946 if ( recur->duration() != -1 ) {
03947 txt = i18n( "%1 on the %2 %3 of %4 until %5" ).
03948 arg( recurStr ).
03949 arg( dayList[rule.pos() + 31] ).
03950 arg( calSys->weekDayName( rule.day(), false ) ).
03951 arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ).
03952 arg( recurEnd( incidence ) );
03953 if ( recur->duration() > 0 ) {
03954 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() );
03955 }
03956 return txt;
03957 }
03958 txt = i18n( "%1 on the %2 %3 of %4" ).
03959 arg( recurStr ).
03960 arg( dayList[rule.pos() + 31] ).
03961 arg( calSys->weekDayName( rule.day(), false ) ).
03962 arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) );
03963 return txt;
03964 } else {
03965 return recurStr;
03966 }
03967 break;
03968 }
03969 }
03970
03971 return i18n( "Incidence recurs" );
03972 }
03973
03974 QString IncidenceFormatter::timeToString( const QDateTime &date, bool shortfmt )
03975 {
03976 return KGlobal::locale()->formatTime( date.time(), !shortfmt );
03977 }
03978
03979 QString IncidenceFormatter::dateToString( const QDateTime &date, bool shortfmt )
03980 {
03981 return
03982 KGlobal::locale()->formatDate( date.date(), shortfmt );
03983 }
03984
03985 QString IncidenceFormatter::dateTimeToString( const QDateTime &date,
03986 bool allDay, bool shortfmt )
03987 {
03988 if ( allDay ) {
03989 return dateToString( date, shortfmt );
03990 }
03991
03992 return KGlobal::locale()->formatDateTime( date, shortfmt );
03993 }
03994
03995 QString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence )
03996 {
03997 if ( !calendar || !incidence ) {
03998 return QString::null;
03999 }
04000
04001 CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar );
04002 if ( !calendarResource ) {
04003 return QString::null;
04004 }
04005
04006 ResourceCalendar *resourceCalendar = calendarResource->resource( incidence );
04007 if ( resourceCalendar ) {
04008 if ( !resourceCalendar->subresources().isEmpty() ) {
04009 QString subRes = resourceCalendar->subresourceIdentifier( incidence );
04010 if ( subRes.isEmpty() ) {
04011 return resourceCalendar->resourceName();
04012 } else {
04013 return resourceCalendar->labelForSubresource( subRes );
04014 }
04015 }
04016 return resourceCalendar->resourceName();
04017 }
04018
04019 return QString::null;
04020 }
04021
04022 static QString secs2Duration( int secs )
04023 {
04024 QString tmp;
04025 int days = secs / 86400;
04026 if ( days > 0 ) {
04027 tmp += i18n( "1 day", "%n days", days );
04028 tmp += ' ';
04029 secs -= ( days * 86400 );
04030 }
04031 int hours = secs / 3600;
04032 if ( hours > 0 ) {
04033 tmp += i18n( "1 hour", "%n hours", hours );
04034 tmp += ' ';
04035 secs -= ( hours * 3600 );
04036 }
04037 int mins = secs / 60;
04038 if ( mins > 0 ) {
04039 tmp += i18n( "1 minute", "%n minutes", mins );
04040 }
04041 return tmp;
04042 }
04043
04044 QString IncidenceFormatter::durationString( Incidence *incidence )
04045 {
04046 QString tmp;
04047 if ( incidence->type() == "Event" ) {
04048 Event *event = static_cast<Event *>( incidence );
04049 if ( event->hasEndDate() ) {
04050 if ( !event->doesFloat() ) {
04051 tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) );
04052 } else {
04053 tmp = i18n( "1 day", "%n days",
04054 event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 );
04055 }
04056 } else {
04057 tmp = i18n( "forever" );
04058 }
04059 } else if ( incidence->type() == "Todo" ) {
04060 Todo *todo = static_cast<Todo *>( incidence );
04061 if ( todo->hasDueDate() ) {
04062 if ( todo->hasStartDate() ) {
04063 if ( !todo->doesFloat() ) {
04064 tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) );
04065 } else {
04066 tmp = i18n( "1 day", "%n days",
04067 todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 );
04068 }
04069 }
04070 }
04071 }
04072 return tmp;
04073 }
04074
04075 QStringList IncidenceFormatter::reminderStringList( Incidence *incidence, bool shortfmt )
04076 {
04077
04078 Q_UNUSED( shortfmt );
04079
04080 QStringList reminderStringList;
04081
04082 if ( incidence ) {
04083 Alarm::List alarms = incidence->alarms();
04084 Alarm::List::ConstIterator it;
04085 for ( it = alarms.begin(); it != alarms.end(); ++it ) {
04086 Alarm *alarm = *it;
04087 int offset = 0;
04088 QString remStr, atStr, offsetStr;
04089 if ( alarm->hasTime() ) {
04090 offset = 0;
04091 if ( alarm->time().isValid() ) {
04092 atStr = KGlobal::locale()->formatDateTime( alarm->time() );
04093 }
04094 } else if ( alarm->hasStartOffset() ) {
04095 offset = alarm->startOffset().asSeconds();
04096 if ( offset < 0 ) {
04097 offset = -offset;
04098 offsetStr = i18n( "N days/hours/minutes before the start datetime",
04099 "%1 before the start" );
04100 } else if ( offset > 0 ) {
04101 offsetStr = i18n( "N days/hours/minutes after the start datetime",
04102 "%1 after the start" );
04103 } else {
04104 if ( incidence->dtStart().isValid() ) {
04105 atStr = KGlobal::locale()->formatDateTime( incidence->dtStart() );
04106 }
04107 }
04108 } else if ( alarm->hasEndOffset() ) {
04109 offset = alarm->endOffset().asSeconds();
04110 if ( offset < 0 ) {
04111 offset = -offset;
04112 if ( incidence->type() == "Todo" ) {
04113 offsetStr = i18n( "N days/hours/minutes before the due datetime",
04114 "%1 before the to-do is due" );
04115 } else {
04116 offsetStr = i18n( "N days/hours/minutes before the end datetime",
04117 "%1 before the end" );
04118 }
04119 } else if ( offset > 0 ) {
04120 if ( incidence->type() == "Todo" ) {
04121 offsetStr = i18n( "N days/hours/minutes after the due datetime",
04122 "%1 after the to-do is due" );
04123 } else {
04124 offsetStr = i18n( "N days/hours/minutes after the end datetime",
04125 "%1 after the end" );
04126 }
04127 } else {
04128 if ( incidence->type() == "Todo" ) {
04129 Todo *t = static_cast<Todo *>( incidence );
04130 if ( t->dtDue().isValid() ) {
04131 atStr = KGlobal::locale()->formatDateTime( t->dtDue() );
04132 }
04133 } else {
04134 Event *e = static_cast<Event *>( incidence );
04135 if ( e->dtEnd().isValid() ) {
04136 atStr = KGlobal::locale()->formatDateTime( e->dtEnd() );
04137 }
04138 }
04139 }
04140 }
04141 if ( offset == 0 ) {
04142 if ( !atStr.isEmpty() ) {
04143 remStr = i18n( "reminder occurs at datetime", "at %1" ).arg( atStr );
04144 }
04145 } else {
04146 remStr = offsetStr.arg( secs2Duration( offset ) );
04147 }
04148
04149 if ( alarm->repeatCount() > 0 ) {
04150 QString countStr = i18n( "repeats once", "repeats %n times", alarm->repeatCount() );
04151 QString intervalStr = i18n( "interval is N days/hours/minutes", "interval is %1" ).
04152 arg( secs2Duration( alarm->snoozeTime().asSeconds() ) );
04153 QString repeatStr = i18n( "(repeat string, interval string)", "(%1, %2)" ).
04154 arg( countStr, intervalStr );
04155 remStr = remStr + ' ' + repeatStr;
04156
04157 }
04158 reminderStringList << remStr;
04159 }
04160 }
04161
04162 return reminderStringList;
04163 }