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