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