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