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