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