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