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