00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00037 #include "incidenceformatter.h"
00038 #include "attachment.h"
00039 #include "event.h"
00040 #include "todo.h"
00041 #include "journal.h"
00042 #include "calendar.h"
00043 #include "calendarlocal.h"
00044 #include "icalformat.h"
00045 #include "freebusy.h"
00046 #include "calendarresources.h"
00047
00048 #include "kpimutils/email.h"
00049 #include "kabc/phonenumber.h"
00050 #include "kabc/vcardconverter.h"
00051 #include "kabc/stdaddressbook.h"
00052
00053 #include <kdatetime.h>
00054 #include <kemailsettings.h>
00055
00056 #include <kglobal.h>
00057 #include <kiconloader.h>
00058 #include <klocale.h>
00059 #include <kcalendarsystem.h>
00060 #include <ksystemtimezone.h>
00061 #include <kmimetype.h>
00062
00063 #include <QtCore/QBuffer>
00064 #include <QtCore/QList>
00065 #include <QtGui/QTextDocument>
00066 #include <QtGui/QApplication>
00067
00068 using namespace KCal;
00069 using namespace IncidenceFormatter;
00070
00071
00072
00073
00074
00075
00076 static QString htmlAddLink( const QString &ref, const QString &text,
00077 bool newline = true )
00078 {
00079 QString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
00080 if ( newline ) {
00081 tmpStr += '\n';
00082 }
00083 return tmpStr;
00084 }
00085
00086 static QString htmlAddTag( const QString &tag, const QString &text )
00087 {
00088 int numLineBreaks = text.count( "\n" );
00089 QString str = '<' + tag + '>';
00090 QString tmpText = text;
00091 QString tmpStr = str;
00092 if( numLineBreaks >= 0 ) {
00093 if ( numLineBreaks > 0 ) {
00094 int pos = 0;
00095 QString tmp;
00096 for ( int i = 0; i <= numLineBreaks; ++i ) {
00097 pos = tmpText.indexOf( "\n" );
00098 tmp = tmpText.left( pos );
00099 tmpText = tmpText.right( tmpText.length() - pos - 1 );
00100 tmpStr += tmp + "<br>";
00101 }
00102 } else {
00103 tmpStr += tmpText;
00104 }
00105 }
00106 tmpStr += "</" + tag + '>';
00107 return tmpStr;
00108 }
00109
00110 static bool iamAttendee( Attendee *attendee )
00111 {
00112
00113
00114 bool iam = false;
00115 KEMailSettings settings;
00116 QStringList profiles = settings.profiles();
00117 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
00118 settings.setProfile( *it );
00119 if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
00120 iam = true;
00121 break;
00122 }
00123 }
00124 return iam;
00125 }
00126
00127 static bool iamOrganizer( Incidence *incidence )
00128 {
00129
00130
00131 if ( !incidence ) {
00132 return false;
00133 }
00134
00135 bool iam = false;
00136 KEMailSettings settings;
00137 QStringList profiles = settings.profiles();
00138 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
00139 settings.setProfile( *it );
00140 if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) {
00141 iam = true;
00142 break;
00143 }
00144 }
00145 return iam;
00146 }
00147
00148
00149
00150
00151
00152
00153
00154 static QString displayViewLinkPerson( const QString &email, QString name,
00155 QString uid, const QString &iconPath )
00156 {
00157
00158
00159 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
00160 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
00161 KABC::Addressee::List addressList = add_book->findByEmail( email );
00162 KABC::Addressee o = ( !addressList.isEmpty() ? addressList.first() : KABC::Addressee() );
00163 if ( !o.isEmpty() && addressList.size() < 2 ) {
00164 if ( name.isEmpty() ) {
00165
00166 name = o.formattedName();
00167 }
00168 uid = o.uid();
00169 } else {
00170
00171 uid.clear();
00172 }
00173 }
00174
00175
00176 QString tmpString;
00177 if ( !uid.isEmpty() ) {
00178
00179 if ( name.isEmpty() ) {
00180
00181 tmpString += htmlAddLink( "uid:" + uid, email );
00182 } else {
00183 tmpString += htmlAddLink( "uid:" + uid, name );
00184 }
00185 } else {
00186
00187 tmpString += ( name.isEmpty() ? email : name );
00188 }
00189
00190
00191 if ( !email.isEmpty() && !iconPath.isNull() ) {
00192 KUrl mailto;
00193 mailto.setProtocol( "mailto" );
00194 mailto.setPath( email );
00195 tmpString += htmlAddLink( mailto.url(),
00196 "<img valign=\"top\" src=\"" + iconPath + "\">" );
00197 }
00198
00199 return tmpString;
00200 }
00201
00202 static QString displayViewFormatAttendees( Incidence *incidence )
00203 {
00204 QString tmpStr;
00205 Attendee::List attendees = incidence->attendees();
00206 KIconLoader *iconLoader = KIconLoader::global();
00207 const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
00208
00209
00210 tmpStr += "<tr>";
00211 tmpStr += "<td><b>" + i18n( "Organizer:" ) + "</b></td>";
00212 tmpStr += "<td>" +
00213 displayViewLinkPerson( incidence->organizer().email(),
00214 incidence->organizer().name(),
00215 QString(), iconPath ) +
00216 "</td>";
00217 tmpStr += "</tr>";
00218
00219
00220 tmpStr += "<tr>";
00221 tmpStr += "<td><b>" + i18n( "Attendees:" ) + "</b></td>";
00222 tmpStr += "<td>";
00223 Attendee::List::ConstIterator it;
00224 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
00225 Attendee *a = *it;
00226 if ( a->email() == incidence->organizer().email() ) {
00227
00228 continue;
00229 }
00230 tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid(), iconPath );
00231 if ( !a->delegator().isEmpty() ) {
00232 tmpStr += i18n( " (delegated by %1)", a->delegator() );
00233 }
00234 if ( !a->delegate().isEmpty() ) {
00235 tmpStr += i18n( " (delegated to %1)", a->delegate() );
00236 }
00237 tmpStr += "<br>";
00238 }
00239 if ( tmpStr.endsWith( QLatin1String( "<br>" ) ) ) {
00240 tmpStr.chop( 4 );
00241 }
00242
00243 tmpStr += "</td>";
00244 tmpStr += "</tr>";
00245 return tmpStr;
00246 }
00247
00248 static QString displayViewFormatAttachments( Incidence *incidence )
00249 {
00250 QString tmpStr;
00251 Attachment::List as = incidence->attachments();
00252 Attachment::List::ConstIterator it;
00253 int count = 0;
00254 for ( it = as.constBegin(); it != as.constEnd(); ++it ) {
00255 count++;
00256 if ( (*it)->isUri() ) {
00257 QString name;
00258 if ( (*it)->uri().startsWith( QLatin1String( "kmail:" ) ) ) {
00259 name = i18n( "Show mail" );
00260 } else {
00261 name = (*it)->label();
00262 }
00263 tmpStr += htmlAddLink( (*it)->uri(), name );
00264 } else {
00265 tmpStr += (*it)->label();
00266 }
00267 if ( count < as.count() ) {
00268 tmpStr += "<br>";
00269 }
00270 }
00271 return tmpStr;
00272 }
00273
00274 static QString displayViewFormatCategories( Incidence *incidence )
00275 {
00276 return incidence->categoriesStr();
00277 }
00278
00279 static QString displayViewFormatCreationDate( Incidence *incidence, KDateTime::Spec spec )
00280 {
00281 KDateTime kdt = incidence->created().toTimeSpec( spec );
00282 return i18n( "Creation date: %1", dateTimeToString( incidence->created(), false, true, spec ) );
00283 }
00284
00285 static QString displayViewFormatBirthday( Event *event )
00286 {
00287 if ( !event ) {
00288 return QString();
00289 }
00290 if ( event->customProperty( "KABC", "BIRTHDAY" ) != "YES" ) {
00291 return QString();
00292 }
00293
00294 QString uid_1 = event->customProperty( "KABC", "UID-1" );
00295 QString name_1 = event->customProperty( "KABC", "NAME-1" );
00296 QString email_1= event->customProperty( "KABC", "EMAIL-1" );
00297
00298 KIconLoader *iconLoader = KIconLoader::global();
00299 const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
00300
00301 QString tmpStr = displayViewLinkPerson( email_1, name_1, uid_1, iconPath );
00302
00303 if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00304 QString uid_2 = event->customProperty( "KABC", "UID-2" );
00305 QString name_2 = event->customProperty( "KABC", "NAME-2" );
00306 QString email_2= event->customProperty( "KABC", "EMAIL-2" );
00307 tmpStr += "<br>";
00308 tmpStr += displayViewLinkPerson( email_2, name_2, uid_2, iconPath );
00309 }
00310
00311 return tmpStr;
00312 }
00313
00314 static QString displayViewFormatHeader( Incidence *incidence )
00315 {
00316 QString tmpStr = "<table><tr>";
00317
00318
00319 KIconLoader *iconLoader = KIconLoader::global();
00320 tmpStr += "<td>";
00321
00322
00323
00324
00325 if ( incidence->type() == "Todo" ) {
00326 tmpStr += "<img valign=\"top\" src=\"";
00327 Todo *todo = static_cast<Todo *>( incidence );
00328 if ( !todo->isCompleted() ) {
00329 tmpStr += iconLoader->iconPath( "view-calendar-tasks", KIconLoader::Small );
00330 } else {
00331 tmpStr += iconLoader->iconPath( "task-complete", KIconLoader::Small );
00332 }
00333 tmpStr += "\">";
00334 }
00335
00336 if ( incidence->type() == "Event" ) {
00337 QString iconPath;
00338 if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00339 if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00340 iconPath = iconLoader->iconPath( "view-calendar-anniversary", KIconLoader::Small );
00341 } else {
00342 iconPath = iconLoader->iconPath( "view-calendar-birthday", KIconLoader::Small );
00343 }
00344 } else {
00345 iconPath = iconLoader->iconPath( "view-calendar-day", KIconLoader::Small );
00346 }
00347 tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
00348 }
00349
00350 if ( incidence->type() == "Journal" ) {
00351 tmpStr += "<img valign=\"top\" src=\"" +
00352 iconLoader->iconPath( "view-pim-journal", KIconLoader::Small ) +
00353 "\">";
00354 }
00355
00356 if ( incidence->isAlarmEnabled() ) {
00357 tmpStr += "<img valign=\"top\" src=\"" +
00358 iconLoader->iconPath( "preferences-desktop-notification-bell", KIconLoader::Small ) +
00359 "\">";
00360 }
00361 if ( incidence->recurs() ) {
00362 tmpStr += "<img valign=\"top\" src=\"" +
00363 iconLoader->iconPath( "edit-redo", KIconLoader::Small ) +
00364 "\">";
00365 }
00366 if ( incidence->isReadOnly() ) {
00367 tmpStr += "<img valign=\"top\" src=\"" +
00368 iconLoader->iconPath( "object-locked", KIconLoader::Small ) +
00369 "\">";
00370 }
00371 tmpStr += "</td>";
00372
00373 tmpStr += "<td>";
00374 tmpStr += "<b><u>" + incidence->richSummary() + "</u></b>";
00375 tmpStr += "</td>";
00376
00377 tmpStr += "</tr></table>";
00378
00379 return tmpStr;
00380 }
00381
00382 static QString displayViewFormatEvent( Calendar *calendar, Event *event,
00383 const QDate &date, KDateTime::Spec spec )
00384 {
00385 if ( !event ) {
00386 return QString();
00387 }
00388
00389 QString tmpStr = displayViewFormatHeader( event );
00390
00391 tmpStr += "<table>";
00392 tmpStr += "<col width=\"25%\"/>";
00393 tmpStr += "<col width=\"75%\"/>";
00394
00395 if ( calendar ) {
00396 QString calStr = resourceString( calendar, event );
00397 if ( !calStr.isEmpty() ) {
00398 tmpStr += "<tr>";
00399 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00400 tmpStr += "<td>" + calStr + "</td>";
00401 tmpStr += "</tr>";
00402 }
00403 }
00404
00405 if ( !event->location().isEmpty() ) {
00406 tmpStr += "<tr>";
00407 tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00408 tmpStr += "<td>" + event->richLocation() + "</td>";
00409 tmpStr += "</tr>";
00410 }
00411
00412 tmpStr += "<tr>";
00413 KDateTime startDt = event->dtStart();
00414 KDateTime endDt = event->dtEnd();
00415 if ( event->recurs() ) {
00416 if ( date.isValid() ) {
00417 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
00418 int diffDays = startDt.daysTo( kdt );
00419 kdt = kdt.addSecs( -1 );
00420 startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
00421 if ( event->hasEndDate() ) {
00422 endDt = endDt.addDays( diffDays );
00423 if ( startDt > endDt ) {
00424 startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
00425 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
00426 }
00427 }
00428 }
00429 }
00430 if ( event->allDay() ) {
00431 if ( event->isMultiDay() ) {
00432 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00433 tmpStr += "<td>" +
00434 i18nc( "<beginTime> - <endTime>","%1 - %2",
00435 dateToString( startDt, false, spec ),
00436 dateToString( endDt, false, spec ) ) +
00437 "</td>";
00438 } else {
00439 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00440 tmpStr += "<td>" +
00441 i18nc( "date as string","%1",
00442 dateToString( startDt, false, spec ) ) +
00443 "</td>";
00444 }
00445 } else {
00446 if ( event->isMultiDay() ) {
00447 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00448 tmpStr += "<td>" +
00449 i18nc( "<beginTime> - <endTime>","%1 - %2",
00450 dateToString( startDt, false, spec ),
00451 dateToString( endDt, false, spec ) ) +
00452 "</td>";
00453 } else {
00454 tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>";
00455 if ( event->hasEndDate() && startDt != endDt ) {
00456 tmpStr += "<td>" +
00457 i18nc( "<beginTime> - <endTime>","%1 - %2",
00458 timeToString( startDt, true, spec ),
00459 timeToString( endDt, true, spec ) ) +
00460 "</td>";
00461 } else {
00462 tmpStr += "<td>" +
00463 timeToString( startDt, true, spec ) +
00464 "</td>";
00465 }
00466 tmpStr += "</tr><tr>";
00467 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00468 tmpStr += "<td>" +
00469 i18nc( "date as string","%1",
00470 dateToString( startDt, false, spec ) ) +
00471 "</td>";
00472 }
00473 }
00474 tmpStr += "</tr>";
00475
00476 if ( event->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00477 tmpStr += "<tr>";
00478 if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00479 tmpStr += "<td><b>" + i18n( "Anniversary:" ) + "</b></td>";
00480 } else {
00481 tmpStr += "<td><b>" + i18n( "Birthday:" ) + "</b></td>";
00482 }
00483 tmpStr += "<td>" + displayViewFormatBirthday( event ) + "</td>";
00484 tmpStr += "</tr>";
00485 tmpStr += "</table>";
00486 return tmpStr;
00487 }
00488
00489 if ( !event->description().isEmpty() ) {
00490 tmpStr += "<tr>";
00491 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00492 tmpStr += "<td>" + event->richDescription() + "</td>";
00493 tmpStr += "</tr>";
00494 }
00495
00496 int categoryCount = event->categories().count();
00497 if ( categoryCount > 0 ) {
00498 tmpStr += "<tr>";
00499 tmpStr += "<td><b>";
00500 tmpStr += i18np( "Category:", "Categories:", categoryCount ) +
00501 "</b></td>";
00502 tmpStr += "<td>" + displayViewFormatCategories( event ) + "</td>";
00503 tmpStr += "</tr>";
00504 }
00505
00506 if ( event->recurs() ) {
00507 KDateTime dt = event->recurrence()->getNextDateTime( KDateTime::currentUtcDateTime() );
00508 tmpStr += "<tr>";
00509 tmpStr += "<td><b>" + i18nc( "next occurrence", "Next:" )+ "</b></td>";
00510 tmpStr += "<td>" +
00511 ( dt.isValid() ?
00512 dateTimeToString( dt, event->allDay(), false, spec ) :
00513 i18nc( "no date", "none" ) ) +
00514 "</td>";
00515 tmpStr += "</tr>";
00516 }
00517
00518 if ( event->attendees().count() > 1 ) {
00519 tmpStr += displayViewFormatAttendees( event );
00520 }
00521
00522 int attachmentCount = event->attachments().count();
00523 if ( attachmentCount > 0 ) {
00524 tmpStr += "<tr>";
00525 tmpStr += "<td><b>" +
00526 i18np( "Attachment:", "Attachments:", attachmentCount ) +
00527 "</b></td>";
00528 tmpStr += "<td>" + displayViewFormatAttachments( event ) + "</td>";
00529 tmpStr += "</tr>";
00530 }
00531 tmpStr += "</table>";
00532
00533 tmpStr += "<p><em>" + displayViewFormatCreationDate( event, spec ) + "</em>";
00534
00535 return tmpStr;
00536 }
00537
00538 static QString displayViewFormatTodo( Calendar *calendar, Todo *todo,
00539 const QDate &date, KDateTime::Spec spec )
00540 {
00541 if ( !todo ) {
00542 return QString();
00543 }
00544
00545 QString tmpStr = displayViewFormatHeader( todo );
00546
00547 tmpStr += "<table>";
00548 tmpStr += "<col width=\"25%\"/>";
00549 tmpStr += "<col width=\"75%\"/>";
00550
00551 if ( calendar ) {
00552 QString calStr = IncidenceFormatter::resourceString( calendar, todo );
00553 if ( !calStr.isEmpty() ) {
00554 tmpStr += "<tr>";
00555 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00556 tmpStr += "<td>" + calStr + "</td>";
00557 tmpStr += "</tr>";
00558 }
00559 }
00560
00561 if ( !todo->location().isEmpty() ) {
00562 tmpStr += "<tr>";
00563 tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>";
00564 tmpStr += "<td>" + todo->richLocation() + "</td>";
00565 tmpStr += "</tr>";
00566 }
00567
00568 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
00569 KDateTime startDt = todo->dtStart();
00570 if ( todo->recurs() ) {
00571 if ( date.isValid() ) {
00572 startDt.setDate( date );
00573 }
00574 }
00575 tmpStr += "<tr>";
00576 tmpStr += "<td><b>" +
00577 i18nc( "to-do start date/time", "Start:" ) +
00578 "</b></td>";
00579 tmpStr += "<td>" +
00580 dateTimeToString( startDt, todo->allDay(), false, spec ) +
00581 "</td>";
00582 tmpStr += "</tr>";
00583 }
00584
00585 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
00586 KDateTime dueDt = todo->dtDue();
00587 if ( todo->recurs() ) {
00588 if ( date.isValid() ) {
00589 dueDt.addDays( todo->dtDue().date().daysTo( date ) );
00590 }
00591 }
00592 tmpStr += "<tr>";
00593 tmpStr += "<td><b>" +
00594 i18nc( "to-do due date/time", "Due:" ) +
00595 "</b></td>";
00596 tmpStr += "<td>" +
00597 dateTimeToString( dueDt, todo->allDay(), false, spec ) +
00598 "</td>";
00599 tmpStr += "</tr>";
00600 }
00601
00602 if ( !todo->description().isEmpty() ) {
00603 tmpStr += "<tr>";
00604 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00605 tmpStr += "<td>" + todo->richDescription() + "</td>";
00606 tmpStr += "</tr>";
00607 }
00608
00609 int categoryCount = todo->categories().count();
00610 if ( categoryCount > 0 ) {
00611 tmpStr += "<tr>";
00612 tmpStr += "<td><b>" +
00613 i18np( "Category:", "Categories:", categoryCount ) +
00614 "</b></td>";
00615 tmpStr += "<td>" + displayViewFormatCategories( todo ) + "</td>";
00616 tmpStr += "</tr>";
00617 }
00618
00619 tmpStr += "<tr>";
00620 tmpStr += "<td><b>" + i18n( "Priority:" ) + "</b></td>";
00621 tmpStr += "<td>";
00622 if ( todo->priority() > 0 ) {
00623 tmpStr += QString::number( todo->priority() );
00624 } else {
00625 tmpStr += i18n( "Unspecified" );
00626 }
00627 tmpStr += "</td>";
00628 tmpStr += "</tr>";
00629
00630 tmpStr += "<tr>";
00631 tmpStr += "<td><b>" +
00632 i18nc( "percent completed", "Completed:" ) + "</b></td>";
00633 tmpStr += "<td>" + i18n( "%1%", todo->percentComplete() ) + "</td>";
00634 tmpStr += "</tr>";
00635
00636 if ( todo->recurs() ) {
00637 KDateTime dt = todo->recurrence()->getNextDateTime( KDateTime::currentUtcDateTime() );
00638 tmpStr += "<tr>";
00639 tmpStr += "<td><b>" + i18nc( "next occurrence", "Next:" ) + "</b></td>";
00640 tmpStr += ( dt.isValid() ?
00641 dateTimeToString( dt, todo->allDay(), false, spec ) :
00642 i18nc( "no date", "none" ) ) +
00643 "</td>";
00644 tmpStr += "</tr>";
00645 }
00646
00647 if ( todo->attendees().count() > 1 ) {
00648 tmpStr += displayViewFormatAttendees( todo );
00649 }
00650
00651 int attachmentCount = todo->attachments().count();
00652 if ( attachmentCount > 0 ) {
00653 tmpStr += "<tr>";
00654 tmpStr += "<td><b>" +
00655 i18np( "Attachment:", "Attachments:", attachmentCount ) +
00656 "</b></td>";
00657 tmpStr += "<td>" + displayViewFormatAttachments( todo ) + "</td>";
00658 tmpStr += "</tr>";
00659 }
00660 tmpStr += "</table>";
00661
00662 tmpStr += "<p><em>" + displayViewFormatCreationDate( todo, spec ) + "</em>";
00663
00664 return tmpStr;
00665 }
00666
00667 static QString displayViewFormatJournal( Calendar *calendar, Journal *journal,
00668 KDateTime::Spec spec )
00669 {
00670 if ( !journal ) {
00671 return QString();
00672 }
00673
00674 QString tmpStr = displayViewFormatHeader( journal );
00675
00676 tmpStr += "<table>";
00677 tmpStr += "<col width=\"25%\"/>";
00678 tmpStr += "<col width=\"75%\"/>";
00679
00680 if ( calendar ) {
00681 QString calStr = IncidenceFormatter::resourceString( calendar, journal );
00682 if ( !calStr.isEmpty() ) {
00683 tmpStr += "<tr>";
00684 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
00685 tmpStr += "<td>" + calStr + "</td>";
00686 tmpStr += "</tr>";
00687 }
00688 }
00689
00690 tmpStr += "<tr>";
00691 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
00692 tmpStr += "<td>" +
00693 dateToString( journal->dtStart(), false, spec ) +
00694 "</td>";
00695 tmpStr += "</tr>";
00696
00697 if ( !journal->description().isEmpty() ) {
00698 tmpStr += "<tr>";
00699 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
00700 tmpStr += "<td>" + journal->richDescription() + "</td>";
00701 tmpStr += "</tr>";
00702 }
00703
00704 int categoryCount = journal->categories().count();
00705 if ( categoryCount > 0 ) {
00706 tmpStr += "<tr>";
00707 tmpStr += "<td><b>" +
00708 i18np( "Category:", "Categories:", categoryCount ) +
00709 "</b></td>";
00710 tmpStr += "<td>" + displayViewFormatCategories( journal ) + "</td>";
00711 tmpStr += "</tr>";
00712 }
00713
00714 tmpStr += "</table>";
00715
00716 tmpStr += "<p><em>" + displayViewFormatCreationDate( journal, spec ) + "</em>";
00717
00718 return tmpStr;
00719 }
00720
00721 static QString displayViewFormatFreeBusy( Calendar *calendar, FreeBusy *fb,
00722 KDateTime::Spec spec )
00723 {
00724 Q_UNUSED( calendar );
00725 if ( !fb ) {
00726 return QString();
00727 }
00728
00729 QString tmpStr(
00730 htmlAddTag(
00731 "h2", i18n( "Free/Busy information for %1", fb->organizer().fullName() ) ) );
00732
00733 tmpStr += htmlAddTag( "h4",
00734 i18n( "Busy times in date range %1 - %2:",
00735 dateToString( fb->dtStart(), true, spec ),
00736 dateToString( fb->dtEnd(), true, spec ) ) );
00737
00738 QList<Period> periods = fb->busyPeriods();
00739
00740 QString text =
00741 htmlAddTag( "em",
00742 htmlAddTag( "b", i18nc( "tag for busy periods list", "Busy:" ) ) );
00743
00744 QList<Period>::iterator it;
00745 for ( it = periods.begin(); it != periods.end(); ++it ) {
00746 Period per = *it;
00747 if ( per.hasDuration() ) {
00748 int dur = per.duration().asSeconds();
00749 QString cont;
00750 if ( dur >= 3600 ) {
00751 cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
00752 dur %= 3600;
00753 }
00754 if ( dur >= 60 ) {
00755 cont += i18ncp( "minutes part duration", "1 minute ", "%1 minutes ", dur / 60 );
00756 dur %= 60;
00757 }
00758 if ( dur > 0 ) {
00759 cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
00760 }
00761 text += i18nc( "startDate for duration", "%1 for %2",
00762 dateTimeToString( per.start(), false, true, spec ),
00763 cont );
00764 text += "<br>";
00765 } else {
00766 if ( per.start().date() == per.end().date() ) {
00767 text += i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
00768 dateToString( per.start(), true, spec ),
00769 timeToString( per.start(), true, spec ),
00770 timeToString( per.end(), true, spec ) );
00771 } else {
00772 text += i18nc( "fromDateTime - toDateTime", "%1 - %2",
00773 dateTimeToString( per.start(), false, true, spec ),
00774 dateTimeToString( per.end(), false, true, spec ) );
00775 }
00776 text += "<br>";
00777 }
00778 }
00779 tmpStr += htmlAddTag( "p", text );
00780 return tmpStr;
00781 }
00782
00783
00784
00785 class KCal::IncidenceFormatter::EventViewerVisitor
00786 : public IncidenceBase::Visitor
00787 {
00788 public:
00789 EventViewerVisitor()
00790 : mCalendar( 0 ), mSpec( KDateTime::Spec() ), mResult( "" ) {}
00791
00792 bool act( Calendar *calendar, IncidenceBase *incidence, const QDate &date,
00793 KDateTime::Spec spec=KDateTime::Spec() )
00794 {
00795 mCalendar = calendar;
00796 mDate = date;
00797 mSpec = spec;
00798 mResult = "";
00799 return incidence->accept( *this );
00800 }
00801 QString result() const { return mResult; }
00802
00803 protected:
00804 bool visit( Event *event )
00805 {
00806 mResult = displayViewFormatEvent( mCalendar, event, mDate, mSpec );
00807 return !mResult.isEmpty();
00808 }
00809 bool visit( Todo *todo )
00810 {
00811 mResult = displayViewFormatTodo( mCalendar, todo, mDate, mSpec );
00812 return !mResult.isEmpty();
00813 }
00814 bool visit( Journal *journal )
00815 {
00816 mResult = displayViewFormatJournal( mCalendar, journal, mSpec );
00817 return !mResult.isEmpty();
00818 }
00819 bool visit( FreeBusy *fb )
00820 {
00821 mResult = displayViewFormatFreeBusy( mCalendar, fb, mSpec );
00822 return !mResult.isEmpty();
00823 }
00824
00825 protected:
00826 Calendar *mCalendar;
00827 QDate mDate;
00828 KDateTime::Spec mSpec;
00829 QString mResult;
00830 };
00831
00832
00833 QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
00834 {
00835 return extensiveDisplayStr( 0, incidence, QDate(), KDateTime::Spec() );
00836 }
00837
00838 QString IncidenceFormatter::extensiveDisplayStr( IncidenceBase *incidence,
00839 KDateTime::Spec spec )
00840 {
00841 if ( !incidence ) {
00842 return QString();
00843 }
00844
00845 EventViewerVisitor v;
00846 if ( v.act( 0, incidence, QDate(), spec ) ) {
00847 return v.result();
00848 } else {
00849 return QString();
00850 }
00851 }
00852
00853 QString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar,
00854 IncidenceBase *incidence,
00855 const QDate &date,
00856 KDateTime::Spec spec )
00857 {
00858 if ( !incidence ) {
00859 return QString();
00860 }
00861
00862 EventViewerVisitor v;
00863 if ( v.act( calendar, incidence, date, spec ) ) {
00864 return v.result();
00865 } else {
00866 return QString();
00867 }
00868 }
00869
00870
00871
00872
00873
00874
00875 static QString string2HTML( const QString &str )
00876 {
00877 return Qt::convertFromPlainText( str, Qt::WhiteSpaceNormal );
00878 }
00879
00880 static QString cleanHtml( const QString &html )
00881 {
00882 QRegExp rx( "<body[^>]*>(.*)</body>", Qt::CaseInsensitive );
00883 rx.indexIn( html );
00884 QString body = rx.cap( 1 );
00885
00886 return Qt::escape( body.remove( QRegExp( "<[^>]*>" ) ).trimmed() );
00887 }
00888
00889 static QString eventStartTimeStr( Event *event )
00890 {
00891 QString tmp;
00892 if ( !event->allDay() ) {
00893 tmp = i18nc( "%1: Start Date, %2: Start Time", "%1 %2",
00894 dateToString( event->dtStart(), true, KSystemTimeZones::local() ),
00895 timeToString( event->dtStart(), true, KSystemTimeZones::local() ) );
00896 } else {
00897 tmp = i18nc( "%1: Start Date", "%1 (all day)",
00898 dateToString( event->dtStart(), true, KSystemTimeZones::local() ) );
00899 }
00900 return tmp;
00901 }
00902
00903 static QString eventEndTimeStr( Event *event )
00904 {
00905 QString tmp;
00906 if ( event->hasEndDate() && event->dtEnd().isValid() ) {
00907 if ( !event->allDay() ) {
00908 tmp = i18nc( "%1: End Date, %2: End Time", "%1 %2",
00909 dateToString( event->dtEnd(), true, KSystemTimeZones::local() ),
00910 timeToString( event->dtEnd(), true, KSystemTimeZones::local() ) );
00911 } else {
00912 tmp = i18nc( "%1: End Date", "%1 (all day)",
00913 dateToString( event->dtEnd(), true, KSystemTimeZones::local() ) );
00914 }
00915 }
00916 return tmp;
00917 }
00918
00919 static QString invitationRow( const QString &cell1, const QString &cell2 )
00920 {
00921 return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
00922 }
00923
00924 static Attendee *findMyAttendee( Incidence *incidence )
00925 {
00926
00927
00928 Attendee *attendee = 0;
00929 if ( !incidence ) {
00930 return attendee;
00931 }
00932
00933 KEMailSettings settings;
00934 QStringList profiles = settings.profiles();
00935 for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
00936 settings.setProfile( *it );
00937
00938 Attendee::List attendees = incidence->attendees();
00939 Attendee::List::ConstIterator it2;
00940 for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) {
00941 Attendee *a = *it2;
00942 if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
00943 attendee = a;
00944 break;
00945 }
00946 }
00947 }
00948 return attendee;
00949 }
00950
00951 static Attendee *findAttendee( Incidence *incidence, const QString &email )
00952 {
00953
00954
00955 Attendee *attendee = 0;
00956 if ( !incidence ) {
00957 return attendee;
00958 }
00959
00960 Attendee::List attendees = incidence->attendees();
00961 Attendee::List::ConstIterator it;
00962 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
00963 Attendee *a = *it;
00964 if ( email == a->email() ) {
00965 attendee = a;
00966 break;
00967 }
00968 }
00969 return attendee;
00970 }
00971
00972 static bool rsvpRequested( Incidence *incidence )
00973 {
00974 if ( !incidence ) {
00975 return false;
00976 }
00977
00978
00979
00980 bool rsvp = true;
00981 Attendee::List attendees = incidence->attendees();
00982 Attendee::List::ConstIterator it;
00983 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
00984 if ( it == attendees.constBegin() ) {
00985 rsvp = (*it)->RSVP();
00986 } else {
00987 if ( (*it)->RSVP() != rsvp ) {
00988 rsvp = true;
00989 break;
00990 }
00991 }
00992 }
00993 return rsvp;
00994 }
00995
00996 static QString rsvpRequestedStr( bool rsvpRequested )
00997 {
00998 if ( rsvpRequested ) {
00999 return i18n( "Your response is requested" );
01000 } else {
01001 return i18n( "A response is not necessary" );
01002 }
01003 }
01004
01005 static QString invitationPerson( const QString &email, QString name, QString uid )
01006 {
01007
01008
01009 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
01010 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
01011 KABC::Addressee::List addressList = add_book->findByEmail( email );
01012 if ( !addressList.isEmpty() ) {
01013 KABC::Addressee o = addressList.first();
01014 if ( !o.isEmpty() && addressList.size() < 2 ) {
01015 if ( name.isEmpty() ) {
01016
01017 name = o.formattedName();
01018 }
01019 uid = o.uid();
01020 } else {
01021
01022 uid.clear();
01023 }
01024 }
01025 }
01026
01027
01028 QString tmpString;
01029 if ( !uid.isEmpty() ) {
01030
01031 if ( name.isEmpty() ) {
01032
01033 tmpString += htmlAddLink( "uid:" + uid, email );
01034 } else {
01035 tmpString += htmlAddLink( "uid:" + uid, name );
01036 }
01037 } else {
01038
01039 tmpString += ( name.isEmpty() ? email : name );
01040 }
01041 tmpString += '\n';
01042
01043
01044 if ( !email.isEmpty() ) {
01045 KCal::Person person( name, email );
01046 KUrl mailto;
01047 mailto.setProtocol( "mailto" );
01048 mailto.setPath( person.fullName() );
01049 const QString iconPath =
01050 KIconLoader::global()->iconPath( "mail-message-new", KIconLoader::Small );
01051 tmpString += htmlAddLink( mailto.url(),
01052 "<img valign=\"top\" src=\"" + iconPath + "\">" );
01053 }
01054 tmpString += '\n';
01055
01056 return tmpString;
01057 }
01058
01059 static QString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode )
01060 {
01061
01062
01063
01064
01065 QString html;
01066 QString descr;
01067 QStringList comments;
01068
01069 if ( incidence->comments().isEmpty() ) {
01070 if ( !incidence->description().isEmpty() ) {
01071
01072 if ( !incidence->descriptionIsRich() ) {
01073 comments << string2HTML( incidence->description() );
01074 } else {
01075 comments << incidence->richDescription();
01076 if ( noHtmlMode ) {
01077 comments[0] = cleanHtml( comments[0] );
01078 }
01079 comments[0] = htmlAddTag( "p", comments[0] );
01080 }
01081 }
01082
01083 } else {
01084
01085 foreach ( const QString &c, incidence->comments() ) {
01086 if ( !c.isEmpty() ) {
01087 comments += string2HTML( c );
01088 }
01089 }
01090 if ( !incidence->description().isEmpty() ) {
01091
01092 if ( !incidence->descriptionIsRich() ) {
01093 descr = string2HTML( incidence->description() );
01094 } else {
01095 descr = incidence->richDescription();
01096 if ( noHtmlMode ) {
01097 descr = cleanHtml( descr );
01098 }
01099 descr = htmlAddTag( "p", descr );
01100 }
01101 }
01102 }
01103
01104 if( !descr.isEmpty() ) {
01105 html += "<p>";
01106 html += "<table border=\"0\" style=\"margin-top:4px;\">";
01107 html += "<tr><td><center>" +
01108 htmlAddTag( "u", i18n( "Description:" ) ) +
01109 "</center></td></tr>";
01110 html += "<tr><td>" + descr + "</td></tr>";
01111 html += "</table>";
01112 }
01113
01114 if ( !comments.isEmpty() ) {
01115 html += "<p>";
01116 html += "<table border=\"0\" style=\"margin-top:4px;\">";
01117 html += "<tr><td><center>" +
01118 htmlAddTag( "u", i18n( "Comments:" ) ) +
01119 "</center></td></tr>";
01120 html += "<tr><td>";
01121 if ( comments.count() > 1 ) {
01122 html += "<ul>";
01123 for ( int i=0; i < comments.count(); ++i ) {
01124 html += "<li>" + comments[i] + "</li>";
01125 }
01126 html += "</ul>";
01127 } else {
01128 html += comments[0];
01129 }
01130 html += "</td></tr>";
01131 html += "</table>";
01132 }
01133 return html;
01134 }
01135
01136 static QString invitationDetailsEvent( Event *event, bool noHtmlMode, KDateTime::Spec spec )
01137 {
01138
01139 if ( !event ) {
01140 return QString();
01141 }
01142
01143 QString sSummary = i18n( "Summary unspecified" );
01144 if ( !event->summary().isEmpty() ) {
01145 if ( !event->summaryIsRich() ) {
01146 sSummary = Qt::escape( event->summary() );
01147 } else {
01148 sSummary = event->richSummary();
01149 if ( noHtmlMode ) {
01150 sSummary = cleanHtml( sSummary );
01151 }
01152 }
01153 }
01154
01155 QString sLocation = i18n( "Location unspecified" );
01156 if ( !event->location().isEmpty() ) {
01157 if ( !event->locationIsRich() ) {
01158 sLocation = Qt::escape( event->location() );
01159 } else {
01160 sLocation = event->richLocation();
01161 if ( noHtmlMode ) {
01162 sLocation = cleanHtml( sLocation );
01163 }
01164 }
01165 }
01166
01167 QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" );
01168 QString html = QString( "<div dir=\"%1\">\n" ).arg( dir );
01169 html += "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
01170
01171
01172 html += invitationRow( i18n( "What:" ), sSummary );
01173 html += invitationRow( i18n( "Where:" ), sLocation );
01174
01175
01176 if ( event->dtStart().date() == event->dtEnd().date() ) {
01177 html += invitationRow( i18n( "Date:" ), dateToString( event->dtStart(), false, spec ) );
01178 if ( !event->allDay() ) {
01179 html += invitationRow( i18n( "Time:" ),
01180 timeToString( event->dtStart(), true, spec ) +
01181 " - " +
01182 timeToString( event->dtEnd(), true, spec ) );
01183 }
01184 } else {
01185 html += invitationRow( i18nc( "starting date", "From:" ),
01186 dateToString( event->dtStart(), false, spec ) );
01187 if ( !event->allDay() ) {
01188 html += invitationRow( i18nc( "starting time", "At:" ),
01189 timeToString( event->dtStart(), true, spec ) );
01190 }
01191 if ( event->hasEndDate() ) {
01192 html += invitationRow( i18nc( "ending date", "To:" ),
01193 dateToString( event->dtEnd(), false, spec ) );
01194 if ( !event->allDay() ) {
01195 html += invitationRow( i18nc( "ending time", "At:" ),
01196 timeToString( event->dtEnd(), true, spec ) );
01197 }
01198 } else {
01199 html += invitationRow( i18nc( "ending date", "To:" ),
01200 i18n( "no end date specified" ) );
01201 }
01202 }
01203
01204
01205 if ( !event->allDay() && event->hasEndDate() && event->dtEnd().isValid() ) {
01206 QString tmp;
01207 int secs = event->dtStart().secsTo( event->dtEnd() );
01208 int days = secs / 86400;
01209 if ( days > 0 ) {
01210 tmp += i18np( "1 day", "%1 days", days );
01211 tmp += ' ';
01212 secs -= ( days * 86400 );
01213 }
01214 int hours = secs / 3600;
01215 if ( hours > 0 ) {
01216 tmp += i18np( "1 hour", "%1 hours", hours );
01217 tmp += ' ';
01218 secs -= ( hours * 3600 );
01219 }
01220 int mins = secs / 60;
01221 if ( mins > 0 ) {
01222 tmp += i18np( "1 minute", "%1 minutes", mins );
01223 tmp += ' ';
01224 }
01225 html += invitationRow( i18n( "Duration:" ), tmp );
01226 }
01227
01228 if ( event->recurs() ) {
01229 html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) );
01230 }
01231
01232 html += "</table></div>\n";
01233 html += invitationsDetailsIncidence( event, noHtmlMode );
01234
01235 return html;
01236 }
01237
01238 static QString invitationDetailsTodo( Todo *todo, bool noHtmlMode, KDateTime::Spec spec )
01239 {
01240
01241 if ( !todo ) {
01242 return QString();
01243 }
01244
01245 QString sSummary = i18n( "Summary unspecified" );
01246 if ( !todo->summary().isEmpty() ) {
01247 if ( !todo->summaryIsRich() ) {
01248 sSummary = Qt::escape( todo->summary() );
01249 } else {
01250 sSummary = todo->richSummary();
01251 if ( noHtmlMode ) {
01252 sSummary = cleanHtml( sSummary );
01253 }
01254 }
01255 }
01256
01257 QString sLocation = i18n( "Location unspecified" );
01258 if ( !todo->location().isEmpty() ) {
01259 if ( !todo->locationIsRich() ) {
01260 sLocation = Qt::escape( todo->location() );
01261 } else {
01262 sLocation = todo->richLocation();
01263 if ( noHtmlMode ) {
01264 sLocation = cleanHtml( sLocation );
01265 }
01266 }
01267 }
01268
01269 QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" );
01270 QString html = QString( "<div dir=\"%1\">\n" ).arg( dir );
01271 html += "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
01272
01273
01274 html += invitationRow( i18n( "What:" ), sSummary );
01275 html += invitationRow( i18n( "Where:" ), sLocation );
01276
01277 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
01278 html += invitationRow( i18n( "Start Date:" ), dateToString( todo->dtStart(), false, spec ) );
01279 }
01280 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
01281 html += invitationRow( i18n( "Due Date:" ), dateToString( todo->dtDue(), false, spec ) );
01282 } else {
01283 html += invitationRow( i18n( "Due Date:" ), i18nc( "no to-do due date", "None" ) );
01284 }
01285
01286 html += "</table></div>\n";
01287 html += invitationsDetailsIncidence( todo, noHtmlMode );
01288
01289 return html;
01290 }
01291
01292 static QString invitationDetailsJournal( Journal *journal, bool noHtmlMode, KDateTime::Spec spec )
01293 {
01294 if ( !journal ) {
01295 return QString();
01296 }
01297
01298 QString sSummary = i18n( "Summary unspecified" );
01299 QString sDescr = i18n( "Description unspecified" );
01300 if ( ! journal->summary().isEmpty() ) {
01301 sSummary = journal->richSummary();
01302 if ( noHtmlMode ) {
01303 sSummary = cleanHtml( sSummary );
01304 }
01305 }
01306 if ( ! journal->description().isEmpty() ) {
01307 sDescr = journal->richDescription();
01308 if ( noHtmlMode ) {
01309 sDescr = cleanHtml( sDescr );
01310 }
01311 }
01312 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01313 html += invitationRow( i18n( "Summary:" ), sSummary );
01314 html += invitationRow( i18n( "Date:" ), dateToString( journal->dtStart(), false, spec ) );
01315 html += invitationRow( i18n( "Description:" ), sDescr );
01316 html += "</table>\n";
01317 html += invitationsDetailsIncidence( journal, noHtmlMode );
01318
01319 return html;
01320 }
01321
01322 static QString invitationDetailsFreeBusy( FreeBusy *fb, bool noHtmlMode, KDateTime::Spec spec )
01323 {
01324 Q_UNUSED( noHtmlMode );
01325
01326 if ( !fb ) {
01327 return QString();
01328 }
01329
01330 QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
01331 html += invitationRow( i18n( "Person:" ), fb->organizer().fullName() );
01332 html += invitationRow( i18n( "Start date:" ), dateToString( fb->dtStart(), true, spec ) );
01333 html += invitationRow( i18n( "End date:" ), dateToString( fb->dtEnd(), true, spec ) );
01334
01335 html += "<tr><td colspan=2><hr></td></tr>\n";
01336 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
01337
01338 QList<Period> periods = fb->busyPeriods();
01339 QList<Period>::iterator it;
01340 for ( it = periods.begin(); it != periods.end(); ++it ) {
01341 Period per = *it;
01342 if ( per.hasDuration() ) {
01343 int dur = per.duration().asSeconds();
01344 QString cont;
01345 if ( dur >= 3600 ) {
01346 cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
01347 dur %= 3600;
01348 }
01349 if ( dur >= 60 ) {
01350 cont += i18ncp( "minutes part of duration", "1 minute", "%1 minutes ", dur / 60 );
01351 dur %= 60;
01352 }
01353 if ( dur > 0 ) {
01354 cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
01355 }
01356 html += invitationRow(
01357 QString(), i18nc( "startDate for duration", "%1 for %2",
01358 KGlobal::locale()->formatDateTime(
01359 per.start().dateTime(), KLocale::LongDate ), cont ) );
01360 } else {
01361 QString cont;
01362 if ( per.start().date() == per.end().date() ) {
01363 cont = i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
01364 KGlobal::locale()->formatDate( per.start().date() ),
01365 KGlobal::locale()->formatTime( per.start().time() ),
01366 KGlobal::locale()->formatTime( per.end().time() ) );
01367 } else {
01368 cont = i18nc( "fromDateTime - toDateTime", "%1 - %2",
01369 KGlobal::locale()->formatDateTime(
01370 per.start().dateTime(), KLocale::LongDate ),
01371 KGlobal::locale()->formatDateTime(
01372 per.end().dateTime(), KLocale::LongDate ) );
01373 }
01374
01375 html += invitationRow( QString(), cont );
01376 }
01377 }
01378
01379 html += "</table>\n";
01380 return html;
01381 }
01382
01383 static QString invitationHeaderEvent( Event *event, ScheduleMessage *msg )
01384 {
01385 if ( !msg || !event ) {
01386 return QString();
01387 }
01388
01389 switch ( msg->method() ) {
01390 case iTIPPublish:
01391 return i18n( "This event has been published" );
01392 case iTIPRequest:
01393 if ( event->revision() > 0 ) {
01394 return i18n( "This invitation has been updated" );
01395 }
01396 if ( iamOrganizer( event ) ) {
01397 return i18n( "I sent this invitation" );
01398 } else {
01399 if ( !event->organizer().fullName().isEmpty() ) {
01400 return i18n( "You received an invitation from %1",
01401 event->organizer().fullName() );
01402 } else {
01403 return i18n( "You received an invitation" );
01404 }
01405 }
01406 case iTIPRefresh:
01407 return i18n( "This invitation was refreshed" );
01408 case iTIPCancel:
01409 return i18n( "This invitation has been canceled" );
01410 case iTIPAdd:
01411 return i18n( "Addition to the invitation" );
01412 case iTIPReply:
01413 {
01414 Attendee::List attendees = event->attendees();
01415 if( attendees.count() == 0 ) {
01416 kDebug() << "No attendees in the iCal reply!";
01417 return QString();
01418 }
01419 if ( attendees.count() != 1 ) {
01420 kDebug() << "Warning: attendeecount in the reply should be 1"
01421 << "but is" << attendees.count();
01422 }
01423 Attendee *attendee = *attendees.begin();
01424 QString attendeeName = attendee->name();
01425 if ( attendeeName.isEmpty() ) {
01426 attendeeName = attendee->email();
01427 }
01428 if ( attendeeName.isEmpty() ) {
01429 attendeeName = i18n( "Sender" );
01430 }
01431
01432 QString delegatorName, dummy;
01433 KPIMUtils::extractEmailAddressAndName( attendee->delegator(), dummy, delegatorName );
01434 if ( delegatorName.isEmpty() ) {
01435 delegatorName = attendee->delegator();
01436 }
01437
01438 switch( attendee->status() ) {
01439 case Attendee::NeedsAction:
01440 return i18n( "%1 indicates this invitation still needs some action", attendeeName );
01441 case Attendee::Accepted:
01442 if ( delegatorName.isEmpty() ) {
01443 return i18n( "%1 accepts this invitation", attendeeName );
01444 } else {
01445 return i18n( "%1 accepts this invitation on behalf of %2",
01446 attendeeName, delegatorName );
01447 }
01448 case Attendee::Tentative:
01449 if ( delegatorName.isEmpty() ) {
01450 return i18n( "%1 tentatively accepts this invitation", attendeeName );
01451 } else {
01452 return i18n( "%1 tentatively accepts this invitation on behalf of %2",
01453 attendeeName, delegatorName );
01454 }
01455 case Attendee::Declined:
01456 if ( delegatorName.isEmpty() ) {
01457 return i18n( "%1 declines this invitation", attendeeName );
01458 } else {
01459 return i18n( "%1 declines this invitation on behalf of %2",
01460 attendeeName, delegatorName );
01461 }
01462 case Attendee::Delegated:
01463 {
01464 QString delegate, dummy;
01465 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
01466 if ( delegate.isEmpty() ) {
01467 delegate = attendee->delegate();
01468 }
01469 if ( !delegate.isEmpty() ) {
01470 return i18n( "%1 has delegated this invitation to %2", attendeeName, delegate );
01471 } else {
01472 return i18n( "%1 has delegated this invitation", attendeeName );
01473 }
01474 }
01475 case Attendee::Completed:
01476 return i18n( "This invitation is now completed" );
01477 case Attendee::InProcess:
01478 return i18n( "%1 is still processing the invitation", attendeeName );
01479 case Attendee::None:
01480 return i18n( "Unknown response to this invitation" );
01481 }
01482 break;
01483 }
01484 case iTIPCounter:
01485 return i18n( "Sender makes this counter proposal" );
01486 case iTIPDeclineCounter:
01487 return i18n( "Sender declines the counter proposal" );
01488 case iTIPNoMethod:
01489 return i18n( "Error: Event iTIP message with unknown method" );
01490 }
01491 kError() << "encountered an iTIP method that we do not support";
01492 return QString();
01493 }
01494
01495 static QString invitationHeaderTodo( Todo *todo, ScheduleMessage *msg )
01496 {
01497 if ( !msg || !todo ) {
01498 return QString();
01499 }
01500
01501 switch ( msg->method() ) {
01502 case iTIPPublish:
01503 return i18n( "This to-do has been published" );
01504 case iTIPRequest:
01505 if ( todo->revision() > 0 ) {
01506 return i18n( "This to-do has been updated" );
01507 } else {
01508 return i18n( "You have been assigned this to-do" );
01509 }
01510 case iTIPRefresh:
01511 return i18n( "This to-do was refreshed" );
01512 case iTIPCancel:
01513 return i18n( "This to-do was canceled" );
01514 case iTIPAdd:
01515 return i18n( "Addition to the to-do" );
01516 case iTIPReply:
01517 {
01518 Attendee::List attendees = todo->attendees();
01519 if ( attendees.count() == 0 ) {
01520 kDebug() << "No attendees in the iCal reply!";
01521 return QString();
01522 }
01523 if ( attendees.count() != 1 ) {
01524 kDebug() << "Warning: attendeecount in the reply should be 1"
01525 << "but is" << attendees.count();
01526 }
01527 Attendee *attendee = *attendees.begin();
01528
01529 switch( attendee->status() ) {
01530 case Attendee::NeedsAction:
01531 return i18n( "Sender indicates this to-do assignment still needs some action" );
01532 case Attendee::Accepted:
01533 return i18n( "Sender accepts this to-do" );
01534 case Attendee::Tentative:
01535 return i18n( "Sender tentatively accepts this to-do" );
01536 case Attendee::Declined:
01537 return i18n( "Sender declines this to-do" );
01538 case Attendee::Delegated:
01539 {
01540 QString delegate, dummy;
01541 KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
01542 if ( delegate.isEmpty() ) {
01543 delegate = attendee->delegate();
01544 }
01545 if ( !delegate.isEmpty() ) {
01546 return i18n( "Sender has delegated this request for the to-do to %1", delegate );
01547 } else {
01548 return i18n( "Sender has delegated this request for the to-do " );
01549 }
01550 }
01551 case Attendee::Completed:
01552 return i18n( "The request for this to-do is now completed" );
01553 case Attendee::InProcess:
01554 return i18n( "Sender is still processing the invitation" );
01555 case Attendee::None:
01556 return i18n( "Unknown response to this to-do" );
01557 }
01558 break;
01559 }
01560 case iTIPCounter:
01561 return i18n( "Sender makes this counter proposal" );
01562 case iTIPDeclineCounter:
01563 return i18n( "Sender declines the counter proposal" );
01564 case iTIPNoMethod:
01565 return i18n( "Error: To-do iTIP message with unknown method" );
01566 }
01567 kError() << "encountered an iTIP method that we do not support";
01568 return QString();
01569 }
01570
01571 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
01572 {
01573 if ( !msg || !journal ) {
01574 return QString();
01575 }
01576
01577 switch ( msg->method() ) {
01578 case iTIPPublish:
01579 return i18n( "This journal has been published" );
01580 case iTIPRequest:
01581 return i18n( "You have been assigned this journal" );
01582 case iTIPRefresh:
01583 return i18n( "This journal was refreshed" );
01584 case iTIPCancel:
01585 return i18n( "This journal was canceled" );
01586 case iTIPAdd:
01587 return i18n( "Addition to the journal" );
01588 case iTIPReply:
01589 {
01590 Attendee::List attendees = journal->attendees();
01591 if ( attendees.count() == 0 ) {
01592 kDebug() << "No attendees in the iCal reply!";
01593 return QString();
01594 }
01595 if( attendees.count() != 1 ) {
01596 kDebug() << "Warning: attendeecount in the reply should be 1 "
01597 << "but is " << attendees.count();
01598 }
01599 Attendee *attendee = *attendees.begin();
01600
01601 switch( attendee->status() ) {
01602 case Attendee::NeedsAction:
01603 return i18n( "Sender indicates this journal assignment still needs some action" );
01604 case Attendee::Accepted:
01605 return i18n( "Sender accepts this journal" );
01606 case Attendee::Tentative:
01607 return i18n( "Sender tentatively accepts this journal" );
01608 case Attendee::Declined:
01609 return i18n( "Sender declines this journal" );
01610 case Attendee::Delegated:
01611 return i18n( "Sender has delegated this request for the journal" );
01612 case Attendee::Completed:
01613 return i18n( "The request for this journal is now completed" );
01614 case Attendee::InProcess:
01615 return i18n( "Sender is still processing the invitation" );
01616 case Attendee::None:
01617 return i18n( "Unknown response to this journal" );
01618 }
01619 break;
01620 }
01621 case iTIPCounter:
01622 return i18n( "Sender makes this counter proposal" );
01623 case iTIPDeclineCounter:
01624 return i18n( "Sender declines the counter proposal" );
01625 case iTIPNoMethod:
01626 return i18n( "Error: Journal iTIP message with unknown method" );
01627 }
01628 kError() << "encountered an iTIP method that we do not support";
01629 return QString();
01630 }
01631
01632 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
01633 {
01634 if ( !msg || !fb ) {
01635 return QString();
01636 }
01637
01638 switch ( msg->method() ) {
01639 case iTIPPublish:
01640 return i18n( "This free/busy list has been published" );
01641 case iTIPRequest:
01642 return i18n( "The free/busy list has been requested" );
01643 case iTIPRefresh:
01644 return i18n( "This free/busy list was refreshed" );
01645 case iTIPCancel:
01646 return i18n( "This free/busy list was canceled" );
01647 case iTIPAdd:
01648 return i18n( "Addition to the free/busy list" );
01649 case iTIPReply:
01650 return i18n( "Reply to the free/busy list" );
01651 case iTIPCounter:
01652 return i18n( "Sender makes this counter proposal" );
01653 case iTIPDeclineCounter:
01654 return i18n( "Sender declines the counter proposal" );
01655 case iTIPNoMethod:
01656 return i18n( "Error: Free/Busy iTIP message with unknown method" );
01657 }
01658 kError() << "encountered an iTIP method that we do not support";
01659 return QString();
01660 }
01661
01662
01663 static QString invitationAttendees( Incidence *incidence )
01664 {
01665 QString tmpStr;
01666 if ( !incidence ) {
01667 return tmpStr;
01668 }
01669
01670 tmpStr += i18n( "Invitation List" );
01671
01672 int count=0;
01673 Attendee::List attendees = incidence->attendees();
01674 if ( !attendees.isEmpty() ) {
01675
01676 Attendee::List::ConstIterator it;
01677 for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
01678 Attendee *a = *it;
01679 if ( !iamAttendee( a ) ) {
01680 count++;
01681 if ( count == 1 ) {
01682 tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\">";
01683 }
01684 tmpStr += "<tr>";
01685 tmpStr += "<td>";
01686 tmpStr += invitationPerson( a->email(), a->name(), QString() );
01687 if ( !a->delegator().isEmpty() ) {
01688 tmpStr += i18n( " (delegated by %1)", a->delegator() );
01689 }
01690 if ( !a->delegate().isEmpty() ) {
01691 tmpStr += i18n( " (delegated to %1)", a->delegate() );
01692 }
01693 tmpStr += "</td>";
01694 tmpStr += "<td>" + a->statusStr() + "</td>";
01695 tmpStr += "</tr>";
01696 }
01697 }
01698 }
01699 if ( count ) {
01700 tmpStr += "</table>";
01701 } else {
01702 tmpStr += "<i>" + i18nc( "no attendees", "None" ) + "</i>";
01703 }
01704
01705 return tmpStr;
01706 }
01707
01708 static QString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence )
01709 {
01710 QString tmpStr;
01711 if ( !incidence ) {
01712 return tmpStr;
01713 }
01714
01715 Attachment::List attachments = incidence->attachments();
01716 if ( !attachments.isEmpty() ) {
01717 tmpStr += i18n( "Attached Documents:" ) + "<ol>";
01718
01719 Attachment::List::ConstIterator it;
01720 for ( it = attachments.constBegin(); it != attachments.constEnd(); ++it ) {
01721 Attachment *a = *it;
01722 tmpStr += "<li>";
01723
01724 KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() );
01725 const QString iconStr = ( mimeType ?
01726 mimeType->iconName( a->uri() ) :
01727 QString( "application-octet-stream" ) );
01728 const QString iconPath = KIconLoader::global()->iconPath( iconStr, KIconLoader::Small );
01729 if ( !iconPath.isEmpty() ) {
01730 tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
01731 }
01732 tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() );
01733 tmpStr += "</li>";
01734 }
01735 tmpStr += "</ol>";
01736 }
01737
01738 return tmpStr;
01739 }
01740
01741
01742 class KCal::IncidenceFormatter::ScheduleMessageVisitor
01743 : public IncidenceBase::Visitor
01744 {
01745 public:
01746 ScheduleMessageVisitor() : mMessage(0) { mResult = ""; }
01747 bool act( IncidenceBase *incidence, ScheduleMessage *msg )
01748 {
01749 mMessage = msg;
01750 return incidence->accept( *this );
01751 }
01752 QString result() const { return mResult; }
01753
01754 protected:
01755 QString mResult;
01756 ScheduleMessage *mMessage;
01757 };
01758
01759 class KCal::IncidenceFormatter::InvitationHeaderVisitor :
01760 public IncidenceFormatter::ScheduleMessageVisitor
01761 {
01762 protected:
01763 bool visit( Event *event )
01764 {
01765 mResult = invitationHeaderEvent( event, mMessage );
01766 return !mResult.isEmpty();
01767 }
01768 bool visit( Todo *todo )
01769 {
01770 mResult = invitationHeaderTodo( todo, mMessage );
01771 return !mResult.isEmpty();
01772 }
01773 bool visit( Journal *journal )
01774 {
01775 mResult = invitationHeaderJournal( journal, mMessage );
01776 return !mResult.isEmpty();
01777 }
01778 bool visit( FreeBusy *fb )
01779 {
01780 mResult = invitationHeaderFreeBusy( fb, mMessage );
01781 return !mResult.isEmpty();
01782 }
01783 };
01784
01785 class KCal::IncidenceFormatter::InvitationBodyVisitor
01786 : public IncidenceFormatter::ScheduleMessageVisitor
01787 {
01788 public:
01789 InvitationBodyVisitor( bool noHtmlMode, KDateTime::Spec spec )
01790 : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ), mSpec( spec ) {}
01791
01792 protected:
01793 bool visit( Event *event )
01794 {
01795 mResult = invitationDetailsEvent( event, mNoHtmlMode, mSpec );
01796 return !mResult.isEmpty();
01797 }
01798 bool visit( Todo *todo )
01799 {
01800 mResult = invitationDetailsTodo( todo, mNoHtmlMode, mSpec );
01801 return !mResult.isEmpty();
01802 }
01803 bool visit( Journal *journal )
01804 {
01805 mResult = invitationDetailsJournal( journal, mNoHtmlMode, mSpec );
01806 return !mResult.isEmpty();
01807 }
01808 bool visit( FreeBusy *fb )
01809 {
01810 mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode, mSpec );
01811 return !mResult.isEmpty();
01812 }
01813
01814 private:
01815 bool mNoHtmlMode;
01816 KDateTime::Spec mSpec;
01817 };
01818
01819
01820 QString InvitationFormatterHelper::generateLinkURL( const QString &id )
01821 {
01822 return id;
01823 }
01824
01825
01826 class IncidenceFormatter::IncidenceCompareVisitor
01827 : public IncidenceBase::Visitor
01828 {
01829 public:
01830 IncidenceCompareVisitor() : mExistingIncidence( 0 ) {}
01831 bool act( IncidenceBase *incidence, Incidence *existingIncidence )
01832 {
01833 if ( !existingIncidence ) {
01834 return false;
01835 }
01836 Incidence *inc = dynamic_cast<Incidence *>( incidence );
01837 if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() ) {
01838 return false;
01839 }
01840 mExistingIncidence = existingIncidence;
01841 return incidence->accept( *this );
01842 }
01843
01844 QString result() const
01845 {
01846 if ( mChanges.isEmpty() ) {
01847 return QString();
01848 }
01849 QString html = "<div align=\"left\"><ul><li>";
01850 html += mChanges.join( "</li><li>" );
01851 html += "</li><ul></div>";
01852 return html;
01853 }
01854
01855 protected:
01856 bool visit( Event *event )
01857 {
01858 compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
01859 compareIncidences( event, mExistingIncidence );
01860 return !mChanges.isEmpty();
01861 }
01862 bool visit( Todo *todo )
01863 {
01864 compareIncidences( todo, mExistingIncidence );
01865 return !mChanges.isEmpty();
01866 }
01867 bool visit( Journal *journal )
01868 {
01869 compareIncidences( journal, mExistingIncidence );
01870 return !mChanges.isEmpty();
01871 }
01872 bool visit( FreeBusy *fb )
01873 {
01874 Q_UNUSED( fb );
01875 return !mChanges.isEmpty();
01876 }
01877
01878 private:
01879 void compareEvents( Event *newEvent, Event *oldEvent )
01880 {
01881 if ( !oldEvent || !newEvent ) {
01882 return;
01883 }
01884 if ( oldEvent->dtStart() != newEvent->dtStart() ||
01885 oldEvent->allDay() != newEvent->allDay() ) {
01886 mChanges += i18n( "The invitation starting time has been changed from %1 to %2",
01887 eventStartTimeStr( oldEvent ), eventStartTimeStr( newEvent ) );
01888 }
01889 if ( oldEvent->dtEnd() != newEvent->dtEnd() ||
01890 oldEvent->allDay() != newEvent->allDay() ) {
01891 mChanges += i18n( "The invitation ending time has been changed from %1 to %2",
01892 eventEndTimeStr( oldEvent ), eventEndTimeStr( newEvent ) );
01893 }
01894 }
01895
01896 void compareIncidences( Incidence *newInc, Incidence *oldInc )
01897 {
01898 if ( !oldInc || !newInc ) {
01899 return;
01900 }
01901
01902 if ( oldInc->summary() != newInc->summary() ) {
01903 mChanges += i18n( "The summary has been changed to: \"%1\"",
01904 newInc->richSummary() );
01905 }
01906
01907 if ( oldInc->location() != newInc->location() ) {
01908 mChanges += i18n( "The location has been changed to: \"%1\"",
01909 newInc->richLocation() );
01910 }
01911
01912 if ( oldInc->description() != newInc->description() ) {
01913 mChanges += i18n( "The description has been changed to: \"%1\"",
01914 newInc->richDescription() );
01915 }
01916
01917 Attendee::List oldAttendees = oldInc->attendees();
01918 Attendee::List newAttendees = newInc->attendees();
01919 for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
01920 it != newAttendees.constEnd(); ++it ) {
01921 Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
01922 if ( !oldAtt ) {
01923 mChanges += i18n( "Attendee %1 has been added", (*it)->fullName() );
01924 } else {
01925 if ( oldAtt->status() != (*it)->status() ) {
01926 mChanges += i18n( "The status of attendee %1 has been changed to: %2",
01927 (*it)->fullName(), (*it)->statusStr() );
01928 }
01929 }
01930 }
01931
01932 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
01933 it != oldAttendees.constEnd(); ++it ) {
01934 Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
01935 if ( !newAtt ) {
01936 mChanges += i18n( "Attendee %1 has been removed", (*it)->fullName() );
01937 }
01938 }
01939 }
01940
01941 private:
01942 Incidence *mExistingIncidence;
01943 QStringList mChanges;
01944 };
01945
01946
01947 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
01948 {
01949 if ( !id.startsWith( QLatin1String( "ATTACH:" ) ) ) {
01950 QString res = QString( "<a href=\"%1\"><b>%2</b></a>" ).
01951 arg( generateLinkURL( id ), text );
01952 return res;
01953 } else {
01954
01955 QString res = QString( "<a href=\"%1\">%2</a>" ).
01956 arg( generateLinkURL( id ), text );
01957 return res;
01958 }
01959 }
01960
01961
01962
01963 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
01964 {
01965 CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
01966 if ( !cal || !incidence ) {
01967 return true;
01968 }
01969 ResourceCalendar *res = cal->resource( incidence );
01970 if ( !res ) {
01971 return true;
01972 }
01973 const QString subRes = res->subresourceIdentifier( incidence );
01974 if ( !subRes.contains( "/.INBOX.directory/" ) ) {
01975 return false;
01976 }
01977 return true;
01978 }
01979
01980 Calendar *InvitationFormatterHelper::calendar() const
01981 {
01982 return 0;
01983 }
01984
01985 static QString formatICalInvitationHelper( QString invitation,
01986 Calendar *mCalendar,
01987 InvitationFormatterHelper *helper,
01988 bool noHtmlMode,
01989 KDateTime::Spec spec )
01990 {
01991 if ( invitation.isEmpty() ) {
01992 return QString();
01993 }
01994
01995 ICalFormat format;
01996
01997
01998 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
01999
02000 if( !msg ) {
02001 kDebug() << "Failed to parse the scheduling message";
02002 Q_ASSERT( format.exception() );
02003 kDebug() << format.exception()->message();
02004 return QString();
02005 }
02006
02007 IncidenceBase *incBase = msg->event();
02008 incBase->shiftTimes( mCalendar->timeSpec(), KDateTime::Spec::LocalZone() );
02009
02010
02011 Incidence *existingIncidence = 0;
02012 if ( incBase && helper->calendar() ) {
02013 existingIncidence = helper->calendar()->incidence( incBase->uid() );
02014 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
02015 existingIncidence = 0;
02016 }
02017 if ( !existingIncidence ) {
02018 const Incidence::List list = helper->calendar()->incidences();
02019 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
02020 if ( (*it)->schedulingID() == incBase->uid() &&
02021 incidenceOwnedByMe( helper->calendar(), *it ) ) {
02022 existingIncidence = *it;
02023 break;
02024 }
02025 }
02026 }
02027 }
02028
02029
02030 QString html;
02031 html += "<div align=\"center\" style=\"border:solid 1px;\">";
02032
02033 IncidenceFormatter::InvitationHeaderVisitor headerVisitor;
02034
02035 if ( !headerVisitor.act( incBase, msg ) ) {
02036 return QString();
02037 }
02038 html += htmlAddTag( "h3", headerVisitor.result() );
02039
02040 IncidenceFormatter::InvitationBodyVisitor bodyVisitor( noHtmlMode, spec );
02041 if ( !bodyVisitor.act( incBase, msg ) ) {
02042 return QString();
02043 }
02044 html += bodyVisitor.result();
02045
02046 if ( msg->method() == iTIPRequest ) {
02047 IncidenceFormatter::IncidenceCompareVisitor compareVisitor;
02048 if ( compareVisitor.act( incBase, existingIncidence ) ) {
02049 html +=
02050 i18n( "<p align=\"left\">The following changes have been made by the organizer:</p>" );
02051 html += compareVisitor.result();
02052 }
02053 }
02054
02055 Incidence *inc = dynamic_cast<Incidence*>( incBase );
02056
02057
02058 bool myInc = iamOrganizer( inc );
02059
02060
02061 bool rsvpRec = false;
02062 Attendee *ea = 0;
02063 if ( !myInc ) {
02064 if ( existingIncidence ) {
02065 ea = findMyAttendee( existingIncidence );
02066 }
02067 if ( ea && ( ea->status() == Attendee::Accepted || ea->status() == Attendee::Declined ) ) {
02068 rsvpRec = true;
02069 }
02070 }
02071
02072
02073 bool rsvpReq = rsvpRequested( inc );
02074 if ( !myInc ) {
02075 html += "<br/>";
02076 html += "<i><u>";
02077 if ( rsvpRec && ( inc && inc->revision() == 0 ) ) {
02078 html += i18n( "Your response has already been recorded [%1]",
02079 ea->statusStr() );
02080 rsvpReq = false;
02081 } else if ( msg->method() == iTIPCancel ) {
02082 html += i18n( "This invitation was declined" );
02083 } else if ( msg->method() == iTIPAdd ) {
02084 html += i18n( "This invitation was accepted" );
02085 } else {
02086 html += rsvpRequestedStr( rsvpReq );
02087 }
02088 html += "</u></i><br>";
02089 }
02090
02091
02092
02093 html += "<p>";
02094 html += "<table border=\"0\" align=\"center\" cellspacing=\"4\"><tr>";
02095
02096 const QString tdOpen = "<td style=\"border-width:2px;border-style:outset\">";
02097 const QString tdClose = "</td>";
02098 switch ( msg->method() ) {
02099 case iTIPPublish:
02100 case iTIPRequest:
02101 case iTIPRefresh:
02102 case iTIPAdd:
02103 {
02104 if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
02105 if ( inc->type() == "Todo" ) {
02106 html += helper->makeLink( "reply", i18n( "[Record invitation in my to-do list]" ) );
02107 } else {
02108 html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) );
02109 }
02110 }
02111
02112 if ( !myInc ) {
02113 if ( rsvpReq ) {
02114
02115 html += tdOpen;
02116 html += helper->makeLink( "accept",
02117 i18nc( "accept invitation",
02118 "Accept" ) );
02119 html += tdClose;
02120
02121
02122 html += tdOpen;
02123 html += helper->makeLink( "accept_conditionally",
02124 i18nc( "Accept invitation conditionally",
02125 "Accept cond." ) );
02126 html += tdClose;
02127 }
02128
02129 if ( rsvpReq ) {
02130
02131 html += tdOpen;
02132 html += helper->makeLink( "counter",
02133 i18nc( "invitation counter proposal",
02134 "Counter proposal" ) );
02135 html += tdClose;
02136 }
02137
02138 if ( rsvpReq ) {
02139
02140 html += tdOpen;
02141 html += helper->makeLink( "decline",
02142 i18nc( "decline invitation",
02143 "Decline" ) );
02144 html += tdClose;
02145 }
02146
02147 if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
02148
02149 html += tdOpen;
02150 html += helper->makeLink( "delegate",
02151 i18nc( "delegate inviation to another",
02152 "Delegate" ) );
02153 html += tdClose;
02154
02155
02156 html += tdOpen;
02157 html += helper->makeLink( "forward",
02158 i18nc( "forward request to another",
02159 "Forward" ) );
02160 html += tdClose;
02161
02162
02163 if ( incBase && incBase->type() == "Event" ) {
02164 html += tdOpen;
02165 html += helper->makeLink( "check_calendar",
02166 i18nc( "look for scheduling conflicts",
02167 "Check my calendar" ) );
02168 html += tdClose;
02169 }
02170 }
02171 }
02172 break;
02173 }
02174
02175 case iTIPCancel:
02176
02177 if ( inc ) {
02178 html += tdOpen;
02179 if ( inc->type() == "Todo" ) {
02180 html += helper->makeLink( "cancel",
02181 i18n( "Remove invitation from my to-do list" ) );
02182 } else {
02183 html += helper->makeLink( "cancel",
02184 i18n( "Remove invitation from my calendar" ) );
02185 }
02186 html += tdClose;
02187 }
02188 break;
02189
02190 case iTIPReply:
02191 {
02192
02193 Attendee *a = 0;
02194 Attendee *ea = 0;
02195 if ( inc ) {
02196 a = inc->attendees().first();
02197 if ( a && helper->calendar() ) {
02198 ea = findAttendee( existingIncidence, a->email() );
02199 }
02200 }
02201 if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
02202 html += tdOpen;
02203 html += htmlAddTag( "i", i18n( "The response has already been recorded" ) );
02204 html += tdClose;
02205 } else {
02206 if ( inc ) {
02207 if ( inc->type() == "Todo" ) {
02208 html += helper->makeLink( "reply", i18n( "[Record response in my to-do list]" ) );
02209 } else {
02210 html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) );
02211 }
02212 }
02213 }
02214 break;
02215 }
02216
02217 case iTIPCounter:
02218
02219 html += tdOpen;
02220 html += helper->makeLink( "accept_counter", i18n( "Accept" ) );
02221 html += tdClose;
02222
02223 html += tdOpen;
02224 html += helper->makeLink( "decline_counter", i18n( "Decline" ) );
02225 html += tdClose;
02226
02227 html += tdOpen;
02228 html += helper->makeLink( "check_calendar", i18n( "Check my calendar" ) );
02229 html += tdClose;
02230 break;
02231
02232 case iTIPDeclineCounter:
02233 case iTIPNoMethod:
02234 break;
02235 }
02236
02237
02238 html += "</tr></table>";
02239
02240
02241 if ( myInc && helper->calendar() ) {
02242 html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
02243 }
02244
02245
02246 html += "</div>";
02247
02248
02249 html += invitationAttachments( helper, inc );
02250
02251 return html;
02252 }
02253
02254
02255 QString IncidenceFormatter::formatICalInvitation( QString invitation,
02256 Calendar *mCalendar,
02257 InvitationFormatterHelper *helper )
02258 {
02259 return formatICalInvitationHelper( invitation, mCalendar, helper, false,
02260 KSystemTimeZones::local() );
02261 }
02262
02263 QString IncidenceFormatter::formatICalInvitationNoHtml( QString invitation,
02264 Calendar *mCalendar,
02265 InvitationFormatterHelper *helper )
02266 {
02267 return formatICalInvitationHelper( invitation, mCalendar, helper, true,
02268 KSystemTimeZones::local() );
02269 }
02270
02271
02272
02273
02274
02275
02276 class KCal::IncidenceFormatter::ToolTipVisitor
02277 : public IncidenceBase::Visitor
02278 {
02279 public:
02280 ToolTipVisitor()
02281 : mCalendar( 0 ), mRichText( true ), mSpec( KDateTime::Spec() ), mResult( "" ) {}
02282
02283 bool act( Calendar *calendar, IncidenceBase *incidence,
02284 const QDate &date=QDate(), bool richText=true,
02285 KDateTime::Spec spec=KDateTime::Spec() )
02286 {
02287 mCalendar = calendar;
02288 mDate = date;
02289 mRichText = richText;
02290 mSpec = spec;
02291 mResult = "";
02292 return incidence ? incidence->accept( *this ) : false;
02293 }
02294 QString result() const { return mResult; }
02295
02296 protected:
02297 bool visit( Event *event );
02298 bool visit( Todo *todo );
02299 bool visit( Journal *journal );
02300 bool visit( FreeBusy *fb );
02301
02302 QString dateRangeText( Event *event, const QDate &date );
02303 QString dateRangeText( Todo *todo, const QDate &date );
02304 QString dateRangeText( Journal *journal );
02305 QString dateRangeText( FreeBusy *fb );
02306
02307 QString generateToolTip( Incidence *incidence, QString dtRangeText );
02308
02309 protected:
02310 Calendar *mCalendar;
02311 QDate mDate;
02312 bool mRichText;
02313 KDateTime::Spec mSpec;
02314 QString mResult;
02315 };
02316
02317 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const QDate &date )
02318 {
02319
02320 QString ret;
02321 QString tmp;
02322
02323 KDateTime startDt = event->dtStart();
02324 KDateTime endDt = event->dtEnd();
02325 if ( event->recurs() ) {
02326 if ( date.isValid() ) {
02327 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
02328 int diffDays = startDt.daysTo( kdt );
02329 kdt = kdt.addSecs( -1 );
02330 startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
02331 if ( event->hasEndDate() ) {
02332 endDt = endDt.addDays( diffDays );
02333 if ( startDt > endDt ) {
02334 startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
02335 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
02336 }
02337 }
02338 }
02339 }
02340
02341 if ( event->isMultiDay() ) {
02342 tmp = dateToString( startDt, true, mSpec );
02343 ret += "<br>" + i18nc( "Event start", "<i>From:</i> %1", tmp );
02344
02345 tmp = dateToString( endDt, true, mSpec );
02346 ret += "<br>" + i18nc( "Event end","<i>To:</i> %1", tmp );
02347
02348 } else {
02349
02350 ret += "<br>" +
02351 i18n( "<i>Date:</i> %1", dateToString( startDt, false, mSpec ) );
02352 if ( !event->allDay() ) {
02353 const QString dtStartTime = timeToString( startDt, true, mSpec );
02354 const QString dtEndTime = timeToString( endDt, true, mSpec );
02355 if ( dtStartTime == dtEndTime ) {
02356
02357 tmp = "<br>" +
02358 i18nc( "time for event", "<i>Time:</i> %1",
02359 dtStartTime );
02360 } else {
02361 tmp = "<br>" +
02362 i18nc( "time range for event",
02363 "<i>Time:</i> %1 - %2",
02364 dtStartTime, dtEndTime );
02365 }
02366 ret += tmp;
02367 }
02368 }
02369 return ret.replace( ' ', " " );
02370 }
02371
02372 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const QDate &date )
02373 {
02374
02375 QString ret;
02376 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
02377 KDateTime startDt = todo->dtStart();
02378 if ( todo->recurs() ) {
02379 if ( date.isValid() ) {
02380 startDt.setDate( date );
02381 }
02382 }
02383 ret += "<br>" +
02384 i18n( "<i>Start:</i> %1", dateToString( startDt, false, mSpec ) );
02385 }
02386
02387 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
02388 KDateTime dueDt = todo->dtDue();
02389 if ( todo->recurs() ) {
02390 if ( date.isValid() ) {
02391 dueDt.addDays( todo->dtDue().date().daysTo( date ) );
02392 }
02393 }
02394 ret += "<br>" +
02395 i18n( "<i>Due:</i> %1",
02396 dateTimeToString( dueDt, todo->allDay(), false, mSpec ) );
02397 }
02398 if ( todo->isCompleted() ) {
02399 ret += "<br>" +
02400 i18n( "<i>Completed:</i> %1", todo->completedStr() );
02401 } else {
02402 ret += "<br>" +
02403 i18nc( "percent complete", "%1% completed", todo->percentComplete() );
02404 }
02405
02406 return ret.replace( ' ', " " );
02407 }
02408
02409 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal *journal )
02410 {
02411
02412 QString ret;
02413 if ( journal->dtStart().isValid() ) {
02414 ret += "<br>" +
02415 i18n( "<i>Date:</i> %1", dateToString( journal->dtStart(), false, mSpec ) );
02416 }
02417 return ret.replace( ' ', " " );
02418 }
02419
02420 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
02421 {
02422
02423 QString ret;
02424 ret = "<br>" +
02425 i18n( "<i>Period start:</i> %1",
02426 KGlobal::locale()->formatDateTime( fb->dtStart().dateTime() ) );
02427 ret += "<br>" +
02428 i18n( "<i>Period start:</i> %1",
02429 KGlobal::locale()->formatDateTime( fb->dtEnd().dateTime() ) );
02430 return ret.replace( ' ', " " );
02431 }
02432
02433 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
02434 {
02435 mResult = generateToolTip( event, dateRangeText( event, mDate ) );
02436 return !mResult.isEmpty();
02437 }
02438
02439 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
02440 {
02441 mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
02442 return !mResult.isEmpty();
02443 }
02444
02445 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
02446 {
02447 mResult = generateToolTip( journal, dateRangeText( journal ) );
02448 return !mResult.isEmpty();
02449 }
02450
02451 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
02452 {
02453
02454 mResult = "<qt><b>" + i18n( "Free/Busy information for %1", fb->organizer().fullName() ) + "</b>";
02455 mResult += dateRangeText( fb );
02456 mResult += "</qt>";
02457 return !mResult.isEmpty();
02458 }
02459
02460 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence *incidence,
02461 QString dtRangeText )
02462 {
02463
02464 if ( !incidence ) {
02465 return QString();
02466 }
02467
02468 QString tmp = "<qt><b>"+ incidence->richSummary() + "</b>";
02469 if ( mCalendar ) {
02470 QString calStr = IncidenceFormatter::resourceString( mCalendar, incidence );
02471 if ( !calStr.isEmpty() ) {
02472 tmp += "<br>" + i18n( "<i>Calendar:</i> %1", calStr );
02473 }
02474 }
02475
02476 tmp += dtRangeText;
02477
02478 if ( !incidence->location().isEmpty() ) {
02479 tmp += "<br>" +
02480 i18n( "<i>Location:</i> %1", incidence->richLocation() );
02481 }
02482
02483 if ( !incidence->description().isEmpty() ) {
02484 QString desc( incidence->description() );
02485 if ( !incidence->descriptionIsRich() ) {
02486 if ( desc.length() > 120 ) {
02487 desc = desc.left( 120 ) + "...";
02488 }
02489 desc = Qt::escape( desc ).replace( '\n', "<br>" );
02490 } else {
02491
02492 }
02493 tmp += "<br>----------<br>" + i18n( "<i>Description:</i>" ) + "<br>" + desc;
02494 }
02495 tmp += "</qt>";
02496 return tmp;
02497 }
02498
02499
02500 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence,
02501 bool richText )
02502 {
02503 return toolTipStr( 0, incidence, QDate(), richText, KDateTime::Spec() );
02504 }
02505
02506 QString IncidenceFormatter::toolTipStr( IncidenceBase *incidence,
02507 bool richText, KDateTime::Spec spec )
02508 {
02509 ToolTipVisitor v;
02510 if ( v.act( 0, incidence, QDate(), richText, spec ) ) {
02511 return v.result();
02512 } else {
02513 return QString();
02514 }
02515 }
02516
02517 QString IncidenceFormatter::toolTipStr( Calendar *calendar,
02518 IncidenceBase *incidence,
02519 const QDate &date,
02520 bool richText, KDateTime::Spec spec )
02521 {
02522 ToolTipVisitor v;
02523 if ( v.act( calendar, incidence, date, richText, spec ) ) {
02524 return v.result();
02525 } else {
02526 return QString();
02527 }
02528 }
02529
02530
02531
02532
02533
02534
02535 static QString mailBodyIncidence( Incidence *incidence )
02536 {
02537 QString body;
02538 if ( !incidence->summary().isEmpty() ) {
02539 body += i18n( "Summary: %1\n", incidence->richSummary() );
02540 }
02541 if ( !incidence->organizer().isEmpty() ) {
02542 body += i18n( "Organizer: %1\n", incidence->organizer().fullName() );
02543 }
02544 if ( !incidence->location().isEmpty() ) {
02545 body += i18n( "Location: %1\n", incidence->richLocation() );
02546 }
02547 return body;
02548 }
02549
02550
02551
02552 class KCal::IncidenceFormatter::MailBodyVisitor
02553 : public IncidenceBase::Visitor
02554 {
02555 public:
02556 MailBodyVisitor()
02557 : mSpec( KDateTime::Spec() ), mResult( "" ) {}
02558
02559 bool act( IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec() )
02560 {
02561 mSpec = spec;
02562 mResult = "";
02563 return incidence ? incidence->accept( *this ) : false;
02564 }
02565 QString result() const
02566 {
02567 return mResult;
02568 }
02569
02570 protected:
02571 bool visit( Event *event );
02572 bool visit( Todo *todo );
02573 bool visit( Journal *journal );
02574 bool visit( FreeBusy * )
02575 {
02576 mResult = i18n( "This is a Free Busy Object" );
02577 return !mResult.isEmpty();
02578 }
02579 protected:
02580 KDateTime::Spec mSpec;
02581 QString mResult;
02582 };
02583
02584 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
02585 {
02586 QString recurrence[]= {
02587 i18nc( "no recurrence", "None" ),
02588 i18nc( "event recurs by minutes", "Minutely" ),
02589 i18nc( "event recurs by hours", "Hourly" ),
02590 i18nc( "event recurs by days", "Daily" ),
02591 i18nc( "event recurs by weeks", "Weekly" ),
02592 i18nc( "event recurs same position (e.g. first monday) each month", "Monthly Same Position" ),
02593 i18nc( "event recurs same day each month", "Monthly Same Day" ),
02594 i18nc( "event recurs same month each year", "Yearly Same Month" ),
02595 i18nc( "event recurs same day each year", "Yearly Same Day" ),
02596 i18nc( "event recurs same position (e.g. first monday) each year", "Yearly Same Position" )
02597 };
02598
02599 mResult = mailBodyIncidence( event );
02600 mResult += i18n( "Start Date: %1\n", dateToString( event->dtStart(), true, mSpec ) );
02601 if ( !event->allDay() ) {
02602 mResult += i18n( "Start Time: %1\n", timeToString( event->dtStart(), true, mSpec ) );
02603 }
02604 if ( event->dtStart() != event->dtEnd() ) {
02605 mResult += i18n( "End Date: %1\n", dateToString( event->dtEnd(), true, mSpec ) );
02606 }
02607 if ( !event->allDay() ) {
02608 mResult += i18n( "End Time: %1\n", timeToString( event->dtEnd(), true, mSpec ) );
02609 }
02610 if ( event->recurs() ) {
02611 Recurrence *recur = event->recurrence();
02612
02613 mResult += i18n( "Recurs: %1\n", recurrence[ recur->recurrenceType() ] );
02614 mResult += i18n( "Frequency: %1\n", event->recurrence()->frequency() );
02615
02616 if ( recur->duration() > 0 ) {
02617 mResult += i18np( "Repeats once", "Repeats %1 times", recur->duration() );
02618 mResult += '\n';
02619 } else {
02620 if ( recur->duration() != -1 ) {
02621
02622 QString endstr;
02623 if ( event->allDay() ) {
02624 endstr = KGlobal::locale()->formatDate( recur->endDate() );
02625 } else {
02626 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime().dateTime() );
02627 }
02628 mResult += i18n( "Repeat until: %1\n", endstr );
02629 } else {
02630 mResult += i18n( "Repeats forever\n" );
02631 }
02632 }
02633 }
02634
02635 QString details = event->richDescription();
02636 if ( !details.isEmpty() ) {
02637 mResult += i18n( "Details:\n%1\n", details );
02638 }
02639 return !mResult.isEmpty();
02640 }
02641
02642 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
02643 {
02644 mResult = mailBodyIncidence( todo );
02645
02646 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
02647 mResult += i18n( "Start Date: %1\n", dateToString( todo->dtStart( false ), true, mSpec ) );
02648 if ( !todo->allDay() ) {
02649 mResult += i18n( "Start Time: %1\n", timeToString( todo->dtStart( false ), true, mSpec ) );
02650 }
02651 }
02652 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
02653 mResult += i18n( "Due Date: %1\n", dateToString( todo->dtDue(), true, mSpec ) );
02654 if ( !todo->allDay() ) {
02655 mResult += i18n( "Due Time: %1\n", timeToString( todo->dtDue(), true, mSpec ) );
02656 }
02657 }
02658 QString details = todo->richDescription();
02659 if ( !details.isEmpty() ) {
02660 mResult += i18n( "Details:\n%1\n", details );
02661 }
02662 return !mResult.isEmpty();
02663 }
02664
02665 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
02666 {
02667 mResult = mailBodyIncidence( journal );
02668 mResult += i18n( "Date: %1\n", dateToString( journal->dtStart(), true, mSpec ) );
02669 if ( !journal->allDay() ) {
02670 mResult += i18n( "Time: %1\n", timeToString( journal->dtStart(), true, mSpec ) );
02671 }
02672 if ( !journal->description().isEmpty() ) {
02673 mResult += i18n( "Text of the journal:\n%1\n", journal->richDescription() );
02674 }
02675 return !mResult.isEmpty();
02676 }
02677
02678
02679 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
02680 {
02681 return mailBodyStr( incidence, KDateTime::Spec() );
02682 }
02683
02684 QString IncidenceFormatter::mailBodyStr( IncidenceBase *incidence,
02685 KDateTime::Spec spec )
02686 {
02687 if ( !incidence ) {
02688 return QString();
02689 }
02690
02691 MailBodyVisitor v;
02692 if ( v.act( incidence, spec ) ) {
02693 return v.result();
02694 }
02695 return QString();
02696 }
02697
02698
02699 static QString recurEnd( Incidence *incidence )
02700 {
02701 QString endstr;
02702 if ( incidence->allDay() ) {
02703 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
02704 } else {
02705 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
02706 }
02707 return endstr;
02708 }
02709
02710
02711
02712
02713
02714
02715 QString IncidenceFormatter::recurrenceString( Incidence *incidence )
02716 {
02717 if ( !incidence->recurs() ) {
02718 return i18n( "No recurrence" );
02719 }
02720 QStringList dayList;
02721 dayList.append( i18n( "31st Last" ) );
02722 dayList.append( i18n( "30th Last" ) );
02723 dayList.append( i18n( "29th Last" ) );
02724 dayList.append( i18n( "28th Last" ) );
02725 dayList.append( i18n( "27th Last" ) );
02726 dayList.append( i18n( "26th Last" ) );
02727 dayList.append( i18n( "25th Last" ) );
02728 dayList.append( i18n( "24th Last" ) );
02729 dayList.append( i18n( "23rd Last" ) );
02730 dayList.append( i18n( "22nd Last" ) );
02731 dayList.append( i18n( "21st Last" ) );
02732 dayList.append( i18n( "20th Last" ) );
02733 dayList.append( i18n( "19th Last" ) );
02734 dayList.append( i18n( "18th Last" ) );
02735 dayList.append( i18n( "17th Last" ) );
02736 dayList.append( i18n( "16th Last" ) );
02737 dayList.append( i18n( "15th Last" ) );
02738 dayList.append( i18n( "14th Last" ) );
02739 dayList.append( i18n( "13th Last" ) );
02740 dayList.append( i18n( "12th Last" ) );
02741 dayList.append( i18n( "11th Last" ) );
02742 dayList.append( i18n( "10th Last" ) );
02743 dayList.append( i18n( "9th Last" ) );
02744 dayList.append( i18n( "8th Last" ) );
02745 dayList.append( i18n( "7th Last" ) );
02746 dayList.append( i18n( "6th Last" ) );
02747 dayList.append( i18n( "5th Last" ) );
02748 dayList.append( i18n( "4th Last" ) );
02749 dayList.append( i18n( "3rd Last" ) );
02750 dayList.append( i18n( "2nd Last" ) );
02751 dayList.append( i18nc( "last day of the month", "Last" ) );
02752 dayList.append( i18nc( "unknown day of the month", "unknown" ) );
02753 dayList.append( i18n( "1st" ) );
02754 dayList.append( i18n( "2nd" ) );
02755 dayList.append( i18n( "3rd" ) );
02756 dayList.append( i18n( "4th" ) );
02757 dayList.append( i18n( "5th" ) );
02758 dayList.append( i18n( "6th" ) );
02759 dayList.append( i18n( "7th" ) );
02760 dayList.append( i18n( "8th" ) );
02761 dayList.append( i18n( "9th" ) );
02762 dayList.append( i18n( "10th" ) );
02763 dayList.append( i18n( "11th" ) );
02764 dayList.append( i18n( "12th" ) );
02765 dayList.append( i18n( "13th" ) );
02766 dayList.append( i18n( "14th" ) );
02767 dayList.append( i18n( "15th" ) );
02768 dayList.append( i18n( "16th" ) );
02769 dayList.append( i18n( "17th" ) );
02770 dayList.append( i18n( "18th" ) );
02771 dayList.append( i18n( "19th" ) );
02772 dayList.append( i18n( "20th" ) );
02773 dayList.append( i18n( "21st" ) );
02774 dayList.append( i18n( "22nd" ) );
02775 dayList.append( i18n( "23rd" ) );
02776 dayList.append( i18n( "24th" ) );
02777 dayList.append( i18n( "25th" ) );
02778 dayList.append( i18n( "26th" ) );
02779 dayList.append( i18n( "27th" ) );
02780 dayList.append( i18n( "28th" ) );
02781 dayList.append( i18n( "29th" ) );
02782 dayList.append( i18n( "30th" ) );
02783 dayList.append( i18n( "31st" ) );
02784 int weekStart = KGlobal::locale()->weekStartDay();
02785 QString dayNames;
02786 QString txt;
02787 const KCalendarSystem *calSys = KGlobal::locale()->calendar();
02788 Recurrence *recur = incidence->recurrence();
02789 switch ( recur->recurrenceType() ) {
02790 case Recurrence::rNone:
02791 return i18n( "No recurrence" );
02792 case Recurrence::rMinutely:
02793 if ( recur->duration() != -1 ) {
02794 txt = i18np( "Recurs every minute until %2",
02795 "Recurs every %1 minutes until %2",
02796 recur->frequency(), recurEnd( incidence ) );
02797 if ( recur->duration() > 0 ) {
02798 txt += i18nc( "number of occurrences",
02799 " (<numid>%1</numid> occurrences)",
02800 recur->duration() );
02801 }
02802 return txt;
02803 }
02804 return i18np( "Recurs every minute",
02805 "Recurs every %1 minutes", recur->frequency() );
02806 case Recurrence::rHourly:
02807 if ( recur->duration() != -1 ) {
02808 txt = i18np( "Recurs hourly until %2",
02809 "Recurs every %1 hours until %2",
02810 recur->frequency(), recurEnd( incidence ) );
02811 if ( recur->duration() > 0 ) {
02812 txt += i18nc( "number of occurrences",
02813 " (<numid>%1</numid> occurrences)",
02814 recur->duration() );
02815 }
02816 return txt;
02817 }
02818 return i18np( "Recurs hourly", "Recurs every %1 hours", recur->frequency() );
02819 case Recurrence::rDaily:
02820 if ( recur->duration() != -1 ) {
02821 txt = i18np( "Recurs daily until %2",
02822 "Recurs every %1 days until %2",
02823 recur->frequency(), recurEnd( incidence ) );
02824 if ( recur->duration() > 0 ) {
02825 txt += i18nc( "number of occurrences",
02826 " (<numid>%1</numid> occurrences)",
02827 recur->duration() );
02828 }
02829 return txt;
02830 }
02831 return i18np( "Recurs daily", "Recurs every %1 days", recur->frequency() );
02832 case Recurrence::rWeekly:
02833 {
02834 bool addSpace = false;
02835 for ( int i = 0; i < 7; ++i ) {
02836 if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
02837 if ( addSpace ) {
02838 dayNames.append( i18nc( "separator for list of days", ", " ) );
02839 }
02840 dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1,
02841 KCalendarSystem::ShortDayName ) );
02842 addSpace = true;
02843 }
02844 }
02845 if ( dayNames.isEmpty() ) {
02846 dayNames = i18nc( "Recurs weekly on no days", "no days" );
02847 }
02848 if ( recur->duration() != -1 ) {
02849 txt = i18ncp( "Recurs weekly on [list of days] until end-date",
02850 "Recurs weekly on %2 until %3",
02851 "Recurs every <numid>%1</numid> weeks on %2 until %3",
02852 recur->frequency(), dayNames, recurEnd( incidence ) );
02853 if ( recur->duration() > 0 ) {
02854 txt += i18nc( "number of occurrences",
02855 " (<numid>%1</numid> occurrences)",
02856 recur->duration() );
02857 }
02858 return txt;
02859 }
02860 return i18ncp( "Recurs weekly on [list of days]",
02861 "Recurs weekly on %2",
02862 "Recurs every <numid>%1</numid> weeks on %2",
02863 recur->frequency(), dayNames );
02864 }
02865 case Recurrence::rMonthlyPos:
02866 {
02867 if ( !recur->monthPositions().isEmpty() ) {
02868 KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0];
02869 if ( recur->duration() != -1 ) {
02870 txt = i18ncp( "Recurs every N months on the [2nd|3rd|...]"
02871 " weekdayname until end-date",
02872 "Recurs every month on the %2 %3 until %4",
02873 "Recurs every <numid>%1</numid> months on the %2 %3 until %4",
02874 recur->frequency(),
02875 dayList[rule.pos() + 31],
02876 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
02877 recurEnd( incidence ) );
02878 if ( recur->duration() > 0 ) {
02879 txt += i18nc( "number of occurrences",
02880 " (<numid>%1</numid> occurrences)",
02881 recur->duration() );
02882 }
02883 return txt;
02884 }
02885 return i18ncp( "Recurs every N months on the [2nd|3rd|...] weekdayname",
02886 "Recurs every month on the %2 %3",
02887 "Recurs every %1 months on the %2 %3",
02888 recur->frequency(),
02889 dayList[rule.pos() + 31],
02890 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ) );
02891 }
02892 break;
02893 }
02894 case Recurrence::rMonthlyDay:
02895 {
02896 if ( !recur->monthDays().isEmpty() ) {
02897 int days = recur->monthDays()[0];
02898 if ( recur->duration() != -1 ) {
02899 txt = i18ncp( "Recurs monthly on the [1st|2nd|...] day until end-date",
02900 "Recurs monthly on the %2 day until %3",
02901 "Recurs every %1 months on the %2 day until %3",
02902 recur->frequency(),
02903 dayList[days + 31],
02904 recurEnd( incidence ) );
02905 if ( recur->duration() > 0 ) {
02906 txt += i18nc( "number of occurrences",
02907 " (<numid>%1</numid> occurrences)",
02908 recur->duration() );
02909 }
02910 return txt;
02911 }
02912 return i18ncp( "Recurs monthly on the [1st|2nd|...] day",
02913 "Recurs monthly on the %2 day",
02914 "Recurs every <numid>%1</numid> month on the %2 day",
02915 recur->frequency(),
02916 dayList[days + 31] );
02917 }
02918 break;
02919 }
02920 case Recurrence::rYearlyMonth:
02921 {
02922 if ( recur->duration() != -1 ) {
02923 if ( !recur->yearDates().isEmpty() && !recur->yearMonths().isEmpty() ) {
02924 txt = i18ncp( "Recurs Every N years on month-name [1st|2nd|...]"
02925 " until end-date",
02926 "Recurs yearly on %2 %3 until %4",
02927 "Recurs every %1 years on %2 %3 until %4",
02928 recur->frequency(),
02929 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
02930 dayList[ recur->yearDates()[0] + 31 ],
02931 recurEnd( incidence ) );
02932 if ( recur->duration() > 0 ) {
02933 txt += i18nc( "number of occurrences",
02934 " (<numid>%1</numid> occurrences)",
02935 recur->duration() );
02936 }
02937 return txt;
02938 }
02939 }
02940 if ( !recur->yearDates().isEmpty() && !recur->yearMonths().isEmpty() ) {
02941 return i18ncp( "Recurs Every N years on month-name [1st|2nd|...]",
02942 "Recurs yearly on %2 %3",
02943 "Recurs every %1 years on %2 %3",
02944 recur->frequency(),
02945 calSys->monthName( recur->yearMonths()[0],
02946 recur->startDate().year() ),
02947 dayList[ recur->yearDates()[0] + 31 ] );
02948 } else {
02949 if (!recur->yearMonths().isEmpty() ) {
02950 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
02951 "Recurs yearly on %1 %2",
02952 calSys->monthName( recur->yearMonths()[0],
02953 recur->startDate().year() ),
02954 dayList[ recur->startDate().day() + 31 ] );
02955 } else {
02956 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
02957 "Recurs yearly on %1 %2",
02958 calSys->monthName( recur->startDate().month(),
02959 recur->startDate().year() ),
02960 dayList[ recur->startDate().day() + 31 ] );
02961 }
02962 }
02963 break;
02964 }
02965 case Recurrence::rYearlyDay:
02966 if ( !recur->yearDays().isEmpty() ) {
02967 if ( recur->duration() != -1 ) {
02968 txt = i18ncp( "Recurs every N years on day N until end-date",
02969 "Recurs every year on day <numid>%2</numid> until %3",
02970 "Recurs every <numid>%1</numid> years"
02971 " on day <numid>%2</numid> until %3",
02972 recur->frequency(),
02973 recur->yearDays()[0],
02974 recurEnd( incidence ) );
02975 if ( recur->duration() > 0 ) {
02976 txt += i18nc( "number of occurrences",
02977 " (<numid>%1</numid> occurrences)",
02978 recur->duration() );
02979 }
02980 return txt;
02981 }
02982 return i18ncp( "Recurs every N YEAR[S] on day N",
02983 "Recurs every year on day <numid>%2</numid>",
02984 "Recurs every <numid>%1</numid> years"
02985 " on day <numid>%2</numid>",
02986 recur->frequency(), recur->yearDays()[0] );
02987 }
02988 break;
02989 case Recurrence::rYearlyPos:
02990 {
02991 if ( !recur->yearMonths().isEmpty() && !recur->yearPositions().isEmpty() ) {
02992 KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0];
02993 if ( recur->duration() != -1 ) {
02994 txt = i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
02995 "of monthname until end-date",
02996 "Every year on the %2 %3 of %4 until %5",
02997 "Every <numid>%1</numid> years on the %2 %3 of %4"
02998 " until %5",
02999 recur->frequency(),
03000 dayList[rule.pos() + 31],
03001 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
03002 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
03003 recurEnd( incidence ) );
03004 if ( recur->duration() > 0 ) {
03005 txt += i18nc( "number of occurrences",
03006 " (<numid>%1</numid> occurrences)",
03007 recur->duration() );
03008 }
03009 return txt;
03010 }
03011 return i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
03012 "of monthname",
03013 "Every year on the %2 %3 of %4",
03014 "Every <numid>%1</numid> years on the %2 %3 of %4",
03015 recur->frequency(),
03016 dayList[rule.pos() + 31],
03017 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
03018 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) );
03019 }
03020 }
03021 break;
03022 }
03023 return i18n( "Incidence recurs" );
03024 }
03025
03026 QString IncidenceFormatter::timeToString( const KDateTime &date,
03027 bool shortfmt,
03028 const KDateTime::Spec &spec )
03029 {
03030 if ( spec.isValid() ) {
03031
03032 QString timeZone;
03033 if ( spec.timeZone() != KSystemTimeZones::local() ) {
03034 timeZone = ' ' + spec.timeZone().name();
03035 }
03036
03037 return KGlobal::locale()->formatTime( date.toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
03038 } else {
03039 return KGlobal::locale()->formatTime( date.time(), !shortfmt );
03040 }
03041 }
03042
03043 QString IncidenceFormatter::dateToString( const KDateTime &date,
03044 bool shortfmt,
03045 const KDateTime::Spec &spec )
03046 {
03047 if ( spec.isValid() ) {
03048
03049 QString timeZone;
03050 if ( spec.timeZone() != KSystemTimeZones::local() ) {
03051 timeZone = ' ' + spec.timeZone().name();
03052 }
03053
03054 return
03055 KGlobal::locale()->formatDate( date.toTimeSpec( spec ).date(),
03056 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) +
03057 timeZone;
03058 } else {
03059 return
03060 KGlobal::locale()->formatDate( date.date(),
03061 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
03062 }
03063 }
03064
03065 QString IncidenceFormatter::dateTimeToString( const KDateTime &date,
03066 bool allDay,
03067 bool shortfmt,
03068 const KDateTime::Spec &spec )
03069 {
03070 if ( allDay ) {
03071 return dateToString( date, shortfmt, spec );
03072 }
03073
03074 if ( spec.isValid() ) {
03075 QString timeZone;
03076 if ( spec.timeZone() != KSystemTimeZones::local() ) {
03077 timeZone = ' ' + spec.timeZone().name();
03078 }
03079
03080 return KGlobal::locale()->formatDateTime(
03081 date.toTimeSpec( spec ).dateTime(),
03082 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
03083 } else {
03084 return KGlobal::locale()->formatDateTime(
03085 date.dateTime(),
03086 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
03087 }
03088 }
03089
03090 QString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence )
03091 {
03092 if ( !calendar || !incidence ) {
03093 return QString();
03094 }
03095
03096 CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar );
03097 if ( !calendarResource ) {
03098 return QString();
03099 }
03100
03101 ResourceCalendar *resourceCalendar = calendarResource->resource( incidence );
03102 if ( resourceCalendar ) {
03103 if ( !resourceCalendar->subresources().isEmpty() ) {
03104 QString subRes = resourceCalendar->subresourceIdentifier( incidence );
03105 if ( subRes.isEmpty() ) {
03106 return resourceCalendar->resourceName();
03107 } else {
03108 return resourceCalendar->labelForSubresource( subRes );
03109 }
03110 }
03111 return resourceCalendar->resourceName();
03112 }
03113
03114 return QString();
03115 }
03116