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