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