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 html += tdOpen;
02178 if ( inc->type() == "Todo" ) {
02179 html += helper->makeLink( "cancel",
02180 i18n( "Remove invitation from my to-do list" ) );
02181 } else {
02182 html += helper->makeLink( "cancel",
02183 i18n( "Remove invitation from my calendar" ) );
02184 }
02185 html += tdClose;
02186 break;
02187
02188 case iTIPReply:
02189 {
02190
02191 Attendee *a = 0;
02192 Attendee *ea = 0;
02193 if ( inc ) {
02194 a = inc->attendees().first();
02195 if ( a && helper->calendar() ) {
02196 ea = findAttendee( existingIncidence, a->email() );
02197 }
02198 }
02199 if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
02200 html += tdOpen;
02201 html += htmlAddTag( "i", i18n( "The response has already been recorded" ) );
02202 html += tdClose;
02203 } else {
02204 if ( inc ) {
02205 if ( inc->type() == "Todo" ) {
02206 html += helper->makeLink( "reply", i18n( "[Record response in my to-do list]" ) );
02207 } else {
02208 html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) );
02209 }
02210 }
02211 }
02212 break;
02213 }
02214
02215 case iTIPCounter:
02216
02217 html += tdOpen;
02218 html += helper->makeLink( "accept_counter", i18n( "Accept" ) );
02219 html += tdClose;
02220
02221 html += tdOpen;
02222 html += helper->makeLink( "decline_counter", i18n( "Decline" ) );
02223 html += tdClose;
02224
02225 html += tdOpen;
02226 html += helper->makeLink( "check_calendar", i18n( "Check my calendar" ) );
02227 html += tdClose;
02228 break;
02229
02230 case iTIPDeclineCounter:
02231 case iTIPNoMethod:
02232 break;
02233 }
02234
02235
02236 html += "</tr></table>";
02237
02238
02239 if ( myInc && helper->calendar() ) {
02240 html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
02241 }
02242
02243
02244 html += "</div>";
02245
02246
02247 html += invitationAttachments( helper, inc );
02248
02249 return html;
02250 }
02251
02252
02253 QString IncidenceFormatter::formatICalInvitation( QString invitation,
02254 Calendar *mCalendar,
02255 InvitationFormatterHelper *helper )
02256 {
02257 return formatICalInvitationHelper( invitation, mCalendar, helper, false,
02258 KSystemTimeZones::local() );
02259 }
02260
02261 QString IncidenceFormatter::formatICalInvitationNoHtml( QString invitation,
02262 Calendar *mCalendar,
02263 InvitationFormatterHelper *helper )
02264 {
02265 return formatICalInvitationHelper( invitation, mCalendar, helper, true,
02266 KSystemTimeZones::local() );
02267 }
02268
02269
02270
02271
02272
02273
02274 class KCal::IncidenceFormatter::ToolTipVisitor
02275 : public IncidenceBase::Visitor
02276 {
02277 public:
02278 ToolTipVisitor()
02279 : mCalendar( 0 ), mRichText( true ), mSpec( KDateTime::Spec() ), mResult( "" ) {}
02280
02281 bool act( Calendar *calendar, IncidenceBase *incidence,
02282 const QDate &date=QDate(), bool richText=true,
02283 KDateTime::Spec spec=KDateTime::Spec() )
02284 {
02285 mCalendar = calendar;
02286 mDate = date;
02287 mRichText = richText;
02288 mSpec = spec;
02289 mResult = "";
02290 return incidence ? incidence->accept( *this ) : false;
02291 }
02292 QString result() const { return mResult; }
02293
02294 protected:
02295 bool visit( Event *event );
02296 bool visit( Todo *todo );
02297 bool visit( Journal *journal );
02298 bool visit( FreeBusy *fb );
02299
02300 QString dateRangeText( Event *event, const QDate &date );
02301 QString dateRangeText( Todo *todo, const QDate &date );
02302 QString dateRangeText( Journal *journal );
02303 QString dateRangeText( FreeBusy *fb );
02304
02305 QString generateToolTip( Incidence *incidence, QString dtRangeText );
02306
02307 protected:
02308 Calendar *mCalendar;
02309 QDate mDate;
02310 bool mRichText;
02311 KDateTime::Spec mSpec;
02312 QString mResult;
02313 };
02314
02315 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const QDate &date )
02316 {
02317
02318 QString ret;
02319 QString tmp;
02320
02321 KDateTime startDt = event->dtStart();
02322 KDateTime endDt = event->dtEnd();
02323 if ( event->recurs() ) {
02324 if ( date.isValid() ) {
02325 KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
02326 int diffDays = startDt.daysTo( kdt );
02327 kdt = kdt.addSecs( -1 );
02328 startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
02329 if ( event->hasEndDate() ) {
02330 endDt = endDt.addDays( diffDays );
02331 if ( startDt > endDt ) {
02332 startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
02333 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
02334 }
02335 }
02336 }
02337 }
02338
02339 if ( event->isMultiDay() ) {
02340 tmp = dateToString( startDt, true, mSpec );
02341 ret += "<br>" + i18nc( "Event start", "<i>From:</i> %1", tmp );
02342
02343 tmp = dateToString( endDt, true, mSpec );
02344 ret += "<br>" + i18nc( "Event end","<i>To:</i> %1", tmp );
02345
02346 } else {
02347
02348 ret += "<br>" +
02349 i18n( "<i>Date:</i> %1", dateToString( startDt, false, mSpec ) );
02350 if ( !event->allDay() ) {
02351 const QString dtStartTime = timeToString( startDt, true, mSpec );
02352 const QString dtEndTime = timeToString( endDt, true, mSpec );
02353 if ( dtStartTime == dtEndTime ) {
02354
02355 tmp = "<br>" +
02356 i18nc( "time for event", "<i>Time:</i> %1",
02357 dtStartTime );
02358 } else {
02359 tmp = "<br>" +
02360 i18nc( "time range for event",
02361 "<i>Time:</i> %1 - %2",
02362 dtStartTime, dtEndTime );
02363 }
02364 ret += tmp;
02365 }
02366 }
02367 return ret.replace( ' ', " " );
02368 }
02369
02370 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const QDate &date )
02371 {
02372
02373 QString ret;
02374 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
02375 KDateTime startDt = todo->dtStart();
02376 if ( todo->recurs() ) {
02377 if ( date.isValid() ) {
02378 startDt.setDate( date );
02379 }
02380 }
02381 ret += "<br>" +
02382 i18n( "<i>Start:</i> %1", dateToString( startDt, false, mSpec ) );
02383 }
02384
02385 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
02386 KDateTime dueDt = todo->dtDue();
02387 if ( todo->recurs() ) {
02388 if ( date.isValid() ) {
02389 dueDt.addDays( todo->dtDue().date().daysTo( date ) );
02390 }
02391 }
02392 ret += "<br>" +
02393 i18n( "<i>Due:</i> %1",
02394 dateTimeToString( dueDt, todo->allDay(), false, mSpec ) );
02395 }
02396 if ( todo->isCompleted() ) {
02397 ret += "<br>" +
02398 i18n( "<i>Completed:</i> %1", todo->completedStr() );
02399 } else {
02400 ret += "<br>" +
02401 i18nc( "percent complete", "%1% completed", todo->percentComplete() );
02402 }
02403
02404 return ret.replace( ' ', " " );
02405 }
02406
02407 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal *journal )
02408 {
02409
02410 QString ret;
02411 if ( journal->dtStart().isValid() ) {
02412 ret += "<br>" +
02413 i18n( "<i>Date:</i> %1", dateToString( journal->dtStart(), false, mSpec ) );
02414 }
02415 return ret.replace( ' ', " " );
02416 }
02417
02418 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
02419 {
02420
02421 QString ret;
02422 ret = "<br>" +
02423 i18n( "<i>Period start:</i> %1",
02424 KGlobal::locale()->formatDateTime( fb->dtStart().dateTime() ) );
02425 ret += "<br>" +
02426 i18n( "<i>Period start:</i> %1",
02427 KGlobal::locale()->formatDateTime( fb->dtEnd().dateTime() ) );
02428 return ret.replace( ' ', " " );
02429 }
02430
02431 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
02432 {
02433 mResult = generateToolTip( event, dateRangeText( event, mDate ) );
02434 return !mResult.isEmpty();
02435 }
02436
02437 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
02438 {
02439 mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
02440 return !mResult.isEmpty();
02441 }
02442
02443 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
02444 {
02445 mResult = generateToolTip( journal, dateRangeText( journal ) );
02446 return !mResult.isEmpty();
02447 }
02448
02449 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
02450 {
02451
02452 mResult = "<qt><b>" + i18n( "Free/Busy information for %1", fb->organizer().fullName() ) + "</b>";
02453 mResult += dateRangeText( fb );
02454 mResult += "</qt>";
02455 return !mResult.isEmpty();
02456 }
02457
02458 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence *incidence,
02459 QString dtRangeText )
02460 {
02461
02462 if ( !incidence ) {
02463 return QString();
02464 }
02465
02466 QString tmp = "<qt><b>"+ incidence->richSummary() + "</b>";
02467 if ( mCalendar ) {
02468 QString calStr = IncidenceFormatter::resourceString( mCalendar, incidence );
02469 if ( !calStr.isEmpty() ) {
02470 tmp += "<br>" + i18n( "<i>Calendar:</i> %1", calStr );
02471 }
02472 }
02473
02474 tmp += dtRangeText;
02475
02476 if ( !incidence->location().isEmpty() ) {
02477 tmp += "<br>" +
02478 i18n( "<i>Location:</i> %1", incidence->richLocation() );
02479 }
02480
02481 if ( !incidence->description().isEmpty() ) {
02482 QString desc( incidence->description() );
02483 if ( !incidence->descriptionIsRich() ) {
02484 if ( desc.length() > 120 ) {
02485 desc = desc.left( 120 ) + "...";
02486 }
02487 desc = Qt::escape( desc ).replace( '\n', "<br>" );
02488 } else {
02489
02490 }
02491 tmp += "<br>----------<br>" + i18n( "<i>Description:</i>" ) + "<br>" + desc;
02492 }
02493 tmp += "</qt>";
02494 return tmp;
02495 }
02496
02497
02498 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence,
02499 bool richText )
02500 {
02501 return toolTipStr( 0, incidence, QDate(), richText, KDateTime::Spec() );
02502 }
02503
02504 QString IncidenceFormatter::toolTipStr( IncidenceBase *incidence,
02505 bool richText, KDateTime::Spec spec )
02506 {
02507 ToolTipVisitor v;
02508 if ( v.act( 0, incidence, QDate(), richText, spec ) ) {
02509 return v.result();
02510 } else {
02511 return QString();
02512 }
02513 }
02514
02515 QString IncidenceFormatter::toolTipStr( Calendar *calendar,
02516 IncidenceBase *incidence,
02517 const QDate &date,
02518 bool richText, KDateTime::Spec spec )
02519 {
02520 ToolTipVisitor v;
02521 if ( v.act( calendar, incidence, date, richText, spec ) ) {
02522 return v.result();
02523 } else {
02524 return QString();
02525 }
02526 }
02527
02528
02529
02530
02531
02532
02533 static QString mailBodyIncidence( Incidence *incidence )
02534 {
02535 QString body;
02536 if ( !incidence->summary().isEmpty() ) {
02537 body += i18n( "Summary: %1\n", incidence->richSummary() );
02538 }
02539 if ( !incidence->organizer().isEmpty() ) {
02540 body += i18n( "Organizer: %1\n", incidence->organizer().fullName() );
02541 }
02542 if ( !incidence->location().isEmpty() ) {
02543 body += i18n( "Location: %1\n", incidence->richLocation() );
02544 }
02545 return body;
02546 }
02547
02548
02549
02550 class KCal::IncidenceFormatter::MailBodyVisitor
02551 : public IncidenceBase::Visitor
02552 {
02553 public:
02554 MailBodyVisitor()
02555 : mSpec( KDateTime::Spec() ), mResult( "" ) {}
02556
02557 bool act( IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec() )
02558 {
02559 mSpec = spec;
02560 mResult = "";
02561 return incidence ? incidence->accept( *this ) : false;
02562 }
02563 QString result() const
02564 {
02565 return mResult;
02566 }
02567
02568 protected:
02569 bool visit( Event *event );
02570 bool visit( Todo *todo );
02571 bool visit( Journal *journal );
02572 bool visit( FreeBusy * )
02573 {
02574 mResult = i18n( "This is a Free Busy Object" );
02575 return !mResult.isEmpty();
02576 }
02577 protected:
02578 KDateTime::Spec mSpec;
02579 QString mResult;
02580 };
02581
02582 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
02583 {
02584 QString recurrence[]= {
02585 i18nc( "no recurrence", "None" ),
02586 i18nc( "event recurs by minutes", "Minutely" ),
02587 i18nc( "event recurs by hours", "Hourly" ),
02588 i18nc( "event recurs by days", "Daily" ),
02589 i18nc( "event recurs by weeks", "Weekly" ),
02590 i18nc( "event recurs same position (e.g. first monday) each month", "Monthly Same Position" ),
02591 i18nc( "event recurs same day each month", "Monthly Same Day" ),
02592 i18nc( "event recurs same month each year", "Yearly Same Month" ),
02593 i18nc( "event recurs same day each year", "Yearly Same Day" ),
02594 i18nc( "event recurs same position (e.g. first monday) each year", "Yearly Same Position" )
02595 };
02596
02597 mResult = mailBodyIncidence( event );
02598 mResult += i18n( "Start Date: %1\n", dateToString( event->dtStart(), true, mSpec ) );
02599 if ( !event->allDay() ) {
02600 mResult += i18n( "Start Time: %1\n", timeToString( event->dtStart(), true, mSpec ) );
02601 }
02602 if ( event->dtStart() != event->dtEnd() ) {
02603 mResult += i18n( "End Date: %1\n", dateToString( event->dtEnd(), true, mSpec ) );
02604 }
02605 if ( !event->allDay() ) {
02606 mResult += i18n( "End Time: %1\n", timeToString( event->dtEnd(), true, mSpec ) );
02607 }
02608 if ( event->recurs() ) {
02609 Recurrence *recur = event->recurrence();
02610
02611 mResult += i18n( "Recurs: %1\n", recurrence[ recur->recurrenceType() ] );
02612 mResult += i18n( "Frequency: %1\n", event->recurrence()->frequency() );
02613
02614 if ( recur->duration() > 0 ) {
02615 mResult += i18np( "Repeats once", "Repeats %1 times", recur->duration() );
02616 mResult += '\n';
02617 } else {
02618 if ( recur->duration() != -1 ) {
02619
02620 QString endstr;
02621 if ( event->allDay() ) {
02622 endstr = KGlobal::locale()->formatDate( recur->endDate() );
02623 } else {
02624 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime().dateTime() );
02625 }
02626 mResult += i18n( "Repeat until: %1\n", endstr );
02627 } else {
02628 mResult += i18n( "Repeats forever\n" );
02629 }
02630 }
02631 }
02632
02633 QString details = event->richDescription();
02634 if ( !details.isEmpty() ) {
02635 mResult += i18n( "Details:\n%1\n", details );
02636 }
02637 return !mResult.isEmpty();
02638 }
02639
02640 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
02641 {
02642 mResult = mailBodyIncidence( todo );
02643
02644 if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
02645 mResult += i18n( "Start Date: %1\n", dateToString( todo->dtStart( false ), true, mSpec ) );
02646 if ( !todo->allDay() ) {
02647 mResult += i18n( "Start Time: %1\n", timeToString( todo->dtStart( false ), true, mSpec ) );
02648 }
02649 }
02650 if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
02651 mResult += i18n( "Due Date: %1\n", dateToString( todo->dtDue(), true, mSpec ) );
02652 if ( !todo->allDay() ) {
02653 mResult += i18n( "Due Time: %1\n", timeToString( todo->dtDue(), true, mSpec ) );
02654 }
02655 }
02656 QString details = todo->richDescription();
02657 if ( !details.isEmpty() ) {
02658 mResult += i18n( "Details:\n%1\n", details );
02659 }
02660 return !mResult.isEmpty();
02661 }
02662
02663 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
02664 {
02665 mResult = mailBodyIncidence( journal );
02666 mResult += i18n( "Date: %1\n", dateToString( journal->dtStart(), true, mSpec ) );
02667 if ( !journal->allDay() ) {
02668 mResult += i18n( "Time: %1\n", timeToString( journal->dtStart(), true, mSpec ) );
02669 }
02670 if ( !journal->description().isEmpty() ) {
02671 mResult += i18n( "Text of the journal:\n%1\n", journal->richDescription() );
02672 }
02673 return !mResult.isEmpty();
02674 }
02675
02676
02677 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
02678 {
02679 return mailBodyStr( incidence, KDateTime::Spec() );
02680 }
02681
02682 QString IncidenceFormatter::mailBodyStr( IncidenceBase *incidence,
02683 KDateTime::Spec spec )
02684 {
02685 if ( !incidence ) {
02686 return QString();
02687 }
02688
02689 MailBodyVisitor v;
02690 if ( v.act( incidence, spec ) ) {
02691 return v.result();
02692 }
02693 return QString();
02694 }
02695
02696
02697 static QString recurEnd( Incidence *incidence )
02698 {
02699 QString endstr;
02700 if ( incidence->allDay() ) {
02701 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
02702 } else {
02703 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
02704 }
02705 return endstr;
02706 }
02707
02708
02709
02710
02711
02712
02713 QString IncidenceFormatter::recurrenceString( Incidence *incidence )
02714 {
02715 if ( !incidence->recurs() ) {
02716 return i18n( "No recurrence" );
02717 }
02718 QStringList dayList;
02719 dayList.append( i18n( "31st Last" ) );
02720 dayList.append( i18n( "30th Last" ) );
02721 dayList.append( i18n( "29th Last" ) );
02722 dayList.append( i18n( "28th Last" ) );
02723 dayList.append( i18n( "27th Last" ) );
02724 dayList.append( i18n( "26th Last" ) );
02725 dayList.append( i18n( "25th Last" ) );
02726 dayList.append( i18n( "24th Last" ) );
02727 dayList.append( i18n( "23rd Last" ) );
02728 dayList.append( i18n( "22nd Last" ) );
02729 dayList.append( i18n( "21st Last" ) );
02730 dayList.append( i18n( "20th Last" ) );
02731 dayList.append( i18n( "19th Last" ) );
02732 dayList.append( i18n( "18th Last" ) );
02733 dayList.append( i18n( "17th Last" ) );
02734 dayList.append( i18n( "16th Last" ) );
02735 dayList.append( i18n( "15th Last" ) );
02736 dayList.append( i18n( "14th Last" ) );
02737 dayList.append( i18n( "13th Last" ) );
02738 dayList.append( i18n( "12th Last" ) );
02739 dayList.append( i18n( "11th Last" ) );
02740 dayList.append( i18n( "10th Last" ) );
02741 dayList.append( i18n( "9th Last" ) );
02742 dayList.append( i18n( "8th Last" ) );
02743 dayList.append( i18n( "7th Last" ) );
02744 dayList.append( i18n( "6th Last" ) );
02745 dayList.append( i18n( "5th Last" ) );
02746 dayList.append( i18n( "4th Last" ) );
02747 dayList.append( i18n( "3rd Last" ) );
02748 dayList.append( i18n( "2nd Last" ) );
02749 dayList.append( i18nc( "last day of the month", "Last" ) );
02750 dayList.append( i18nc( "unknown day of the month", "unknown" ) );
02751 dayList.append( i18n( "1st" ) );
02752 dayList.append( i18n( "2nd" ) );
02753 dayList.append( i18n( "3rd" ) );
02754 dayList.append( i18n( "4th" ) );
02755 dayList.append( i18n( "5th" ) );
02756 dayList.append( i18n( "6th" ) );
02757 dayList.append( i18n( "7th" ) );
02758 dayList.append( i18n( "8th" ) );
02759 dayList.append( i18n( "9th" ) );
02760 dayList.append( i18n( "10th" ) );
02761 dayList.append( i18n( "11th" ) );
02762 dayList.append( i18n( "12th" ) );
02763 dayList.append( i18n( "13th" ) );
02764 dayList.append( i18n( "14th" ) );
02765 dayList.append( i18n( "15th" ) );
02766 dayList.append( i18n( "16th" ) );
02767 dayList.append( i18n( "17th" ) );
02768 dayList.append( i18n( "18th" ) );
02769 dayList.append( i18n( "19th" ) );
02770 dayList.append( i18n( "20th" ) );
02771 dayList.append( i18n( "21st" ) );
02772 dayList.append( i18n( "22nd" ) );
02773 dayList.append( i18n( "23rd" ) );
02774 dayList.append( i18n( "24th" ) );
02775 dayList.append( i18n( "25th" ) );
02776 dayList.append( i18n( "26th" ) );
02777 dayList.append( i18n( "27th" ) );
02778 dayList.append( i18n( "28th" ) );
02779 dayList.append( i18n( "29th" ) );
02780 dayList.append( i18n( "30th" ) );
02781 dayList.append( i18n( "31st" ) );
02782 int weekStart = KGlobal::locale()->weekStartDay();
02783 QString dayNames;
02784 QString txt;
02785 const KCalendarSystem *calSys = KGlobal::locale()->calendar();
02786 Recurrence *recur = incidence->recurrence();
02787 switch ( recur->recurrenceType() ) {
02788 case Recurrence::rNone:
02789 return i18n( "No recurrence" );
02790 case Recurrence::rMinutely:
02791 if ( recur->duration() != -1 ) {
02792 txt = i18np( "Recurs every minute until %2",
02793 "Recurs every %1 minutes until %2",
02794 recur->frequency(), recurEnd( incidence ) );
02795 if ( recur->duration() > 0 ) {
02796 txt += i18nc( "number of occurrences",
02797 " (<numid>%1</numid> occurrences)",
02798 recur->duration() );
02799 }
02800 return txt;
02801 }
02802 return i18np( "Recurs every minute",
02803 "Recurs every %1 minutes", recur->frequency() );
02804 case Recurrence::rHourly:
02805 if ( recur->duration() != -1 ) {
02806 txt = i18np( "Recurs hourly until %2",
02807 "Recurs every %1 hours until %2",
02808 recur->frequency(), recurEnd( incidence ) );
02809 if ( recur->duration() > 0 ) {
02810 txt += i18nc( "number of occurrences",
02811 " (<numid>%1</numid> occurrences)",
02812 recur->duration() );
02813 }
02814 return txt;
02815 }
02816 return i18np( "Recurs hourly", "Recurs every %1 hours", recur->frequency() );
02817 case Recurrence::rDaily:
02818 if ( recur->duration() != -1 ) {
02819 txt = i18np( "Recurs daily until %2",
02820 "Recurs every %1 days until %2",
02821 recur->frequency(), recurEnd( incidence ) );
02822 if ( recur->duration() > 0 ) {
02823 txt += i18nc( "number of occurrences",
02824 " (<numid>%1</numid> occurrences)",
02825 recur->duration() );
02826 }
02827 return txt;
02828 }
02829 return i18np( "Recurs daily", "Recurs every %1 days", recur->frequency() );
02830 case Recurrence::rWeekly:
02831 {
02832 bool addSpace = false;
02833 for ( int i = 0; i < 7; ++i ) {
02834 if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
02835 if ( addSpace ) {
02836 dayNames.append( i18nc( "separator for list of days", ", " ) );
02837 }
02838 dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1,
02839 KCalendarSystem::ShortDayName ) );
02840 addSpace = true;
02841 }
02842 }
02843 if ( dayNames.isEmpty() ) {
02844 dayNames = i18nc( "Recurs weekly on no days", "no days" );
02845 }
02846 if ( recur->duration() != -1 ) {
02847 txt = i18ncp( "Recurs weekly on [list of days] until end-date",
02848 "Recurs weekly on %2 until %3",
02849 "Recurs every <numid>%1</numid> weeks on %2 until %3",
02850 recur->frequency(), dayNames, recurEnd( incidence ) );
02851 if ( recur->duration() > 0 ) {
02852 txt += i18nc( "number of occurrences",
02853 " (<numid>%1</numid> occurrences)",
02854 recur->duration() );
02855 }
02856 return txt;
02857 }
02858 return i18ncp( "Recurs weekly on [list of days]",
02859 "Recurs weekly on %2",
02860 "Recurs every <numid>%1</numid> weeks on %2",
02861 recur->frequency(), dayNames );
02862 }
02863 case Recurrence::rMonthlyPos:
02864 {
02865 KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0];
02866 if ( recur->duration() != -1 ) {
02867 txt = i18ncp( "Recurs every N months on the [2nd|3rd|...]"
02868 " weekdayname until end-date",
02869 "Recurs every month on the %2 %3 until %4",
02870 "Recurs every <numid>%1</numid> months on the %2 %3 until %4",
02871 recur->frequency(),
02872 dayList[rule.pos() + 31],
02873 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
02874 recurEnd( incidence ) );
02875 if ( recur->duration() > 0 ) {
02876 txt += i18nc( "number of occurrences",
02877 " (<numid>%1</numid> occurrences)",
02878 recur->duration() );
02879 }
02880 return txt;
02881 }
02882 return i18ncp( "Recurs every N months on the [2nd|3rd|...] weekdayname",
02883 "Recurs every month on the %2 %3",
02884 "Recurs every %1 months on the %2 %3",
02885 recur->frequency(),
02886 dayList[rule.pos() + 31],
02887 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ) );
02888 }
02889 case Recurrence::rMonthlyDay:
02890 {
02891 int days = recur->monthDays()[0];
02892 if ( recur->duration() != -1 ) {
02893 txt = i18ncp( "Recurs monthly on the [1st|2nd|...] day until end-date",
02894 "Recurs monthly on the %2 day until %3",
02895 "Recurs every %1 months on the %2 day until %3",
02896 recur->frequency(),
02897 dayList[days + 31],
02898 recurEnd( incidence ) );
02899 if ( recur->duration() > 0 ) {
02900 txt += i18nc( "number of occurrences",
02901 " (<numid>%1</numid> occurrences)",
02902 recur->duration() );
02903 }
02904 return txt;
02905 }
02906 return i18ncp( "Recurs monthly on the [1st|2nd|...] day",
02907 "Recurs monthly on the %2 day",
02908 "Recurs every <numid>%1</numid> month on the %2 day",
02909 recur->frequency(),
02910 dayList[days + 31] );
02911 }
02912 case Recurrence::rYearlyMonth:
02913 {
02914 if ( recur->duration() != -1 ) {
02915 if ( !recur->yearDates().isEmpty() ) {
02916 txt = i18ncp( "Recurs Every N years on month-name [1st|2nd|...]"
02917 " until end-date",
02918 "Recurs yearly on %2 %3 until %4",
02919 "Recurs every %1 years on %2 %3 until %4",
02920 recur->frequency(),
02921 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
02922 dayList[ recur->yearDates()[0] + 31 ],
02923 recurEnd( incidence ) );
02924 if ( recur->duration() > 0 ) {
02925 txt += i18nc( "number of occurrences",
02926 " (<numid>%1</numid> occurrences)",
02927 recur->duration() );
02928 }
02929 return txt;
02930 }
02931 }
02932 if ( !recur->yearDates().isEmpty() ) {
02933 return i18ncp( "Recurs Every N years on month-name [1st|2nd|...]",
02934 "Recurs yearly on %2 %3",
02935 "Recurs every %1 years on %2 %3",
02936 recur->frequency(),
02937 calSys->monthName( recur->yearMonths()[0],
02938 recur->startDate().year() ),
02939 dayList[ recur->yearDates()[0] + 31 ] );
02940 } else {
02941 if (!recur->yearMonths().isEmpty() ) {
02942 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
02943 "Recurs yearly on %1 %2",
02944 calSys->monthName( recur->yearMonths()[0],
02945 recur->startDate().year() ),
02946 dayList[ recur->startDate().day() + 31 ] );
02947 } else {
02948 return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
02949 "Recurs yearly on %1 %2",
02950 calSys->monthName( recur->startDate().month(),
02951 recur->startDate().year() ),
02952 dayList[ recur->startDate().day() + 31 ] );
02953 }
02954 }
02955 }
02956 case Recurrence::rYearlyDay:
02957 if ( recur->duration() != -1 ) {
02958 txt = i18ncp( "Recurs every N years on day N until end-date",
02959 "Recurs every year on day <numid>%2</numid> until %3",
02960 "Recurs every <numid>%1</numid> years"
02961 " on day <numid>%2</numid> until %3",
02962 recur->frequency(),
02963 recur->yearDays()[0],
02964 recurEnd( incidence ) );
02965 if ( recur->duration() > 0 ) {
02966 txt += i18nc( "number of occurrences",
02967 " (<numid>%1</numid> occurrences)",
02968 recur->duration() );
02969 }
02970 return txt;
02971 }
02972 return i18ncp( "Recurs every N YEAR[S] on day N",
02973 "Recurs every year on day <numid>%2</numid>",
02974 "Recurs every <numid>%1</numid> years"
02975 " on day <numid>%2</numid>",
02976 recur->frequency(), recur->yearDays()[0] );
02977 case Recurrence::rYearlyPos:
02978 {
02979 KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0];
02980 if ( recur->duration() != -1 ) {
02981 txt = i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
02982 "of monthname until end-date",
02983 "Every year on the %2 %3 of %4 until %5",
02984 "Every <numid>%1</numid> years on the %2 %3 of %4"
02985 " until %5",
02986 recur->frequency(),
02987 dayList[rule.pos() + 31],
02988 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
02989 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
02990 recurEnd( incidence ) );
02991 if ( recur->duration() > 0 ) {
02992 txt += i18nc( "number of occurrences",
02993 " (<numid>%1</numid> occurrences)",
02994 recur->duration() );
02995 }
02996 return txt;
02997 }
02998 return i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
02999 "of monthname",
03000 "Every year on the %2 %3 of %4",
03001 "Every <numid>%1</numid> years on the %2 %3 of %4",
03002 recur->frequency(),
03003 dayList[rule.pos() + 31],
03004 calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
03005 calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) );
03006 }
03007 default:
03008 return i18n( "Incidence recurs" );
03009 }
03010 }
03011
03012 QString IncidenceFormatter::timeToString( const KDateTime &date,
03013 bool shortfmt,
03014 const KDateTime::Spec &spec )
03015 {
03016 if ( spec.isValid() ) {
03017
03018 QString timeZone;
03019 if ( spec.timeZone() != KSystemTimeZones::local() ) {
03020 timeZone = ' ' + spec.timeZone().name();
03021 }
03022
03023 return KGlobal::locale()->formatTime( date.toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
03024 } else {
03025 return KGlobal::locale()->formatTime( date.time(), !shortfmt );
03026 }
03027 }
03028
03029 QString IncidenceFormatter::dateToString( const KDateTime &date,
03030 bool shortfmt,
03031 const KDateTime::Spec &spec )
03032 {
03033 if ( spec.isValid() ) {
03034
03035 QString timeZone;
03036 if ( spec.timeZone() != KSystemTimeZones::local() ) {
03037 timeZone = ' ' + spec.timeZone().name();
03038 }
03039
03040 return
03041 KGlobal::locale()->formatDate( date.toTimeSpec( spec ).date(),
03042 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) +
03043 timeZone;
03044 } else {
03045 return
03046 KGlobal::locale()->formatDate( date.date(),
03047 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
03048 }
03049 }
03050
03051 QString IncidenceFormatter::dateTimeToString( const KDateTime &date,
03052 bool allDay,
03053 bool shortfmt,
03054 const KDateTime::Spec &spec )
03055 {
03056 if ( allDay ) {
03057 return dateToString( date, shortfmt, spec );
03058 }
03059
03060 if ( spec.isValid() ) {
03061 QString timeZone;
03062 if ( spec.timeZone() != KSystemTimeZones::local() ) {
03063 timeZone = ' ' + spec.timeZone().name();
03064 }
03065
03066 return KGlobal::locale()->formatDateTime(
03067 date.toTimeSpec( spec ).dateTime(),
03068 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
03069 } else {
03070 return KGlobal::locale()->formatDateTime(
03071 date.dateTime(),
03072 ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
03073 }
03074 }
03075
03076 QString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence )
03077 {
03078 if ( !calendar || !incidence ) {
03079 return QString();
03080 }
03081
03082 CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar );
03083 if ( !calendarResource ) {
03084 return QString();
03085 }
03086
03087 ResourceCalendar *resourceCalendar = calendarResource->resource( incidence );
03088 if ( resourceCalendar ) {
03089 if ( !resourceCalendar->subresources().isEmpty() ) {
03090 QString subRes = resourceCalendar->subresourceIdentifier( incidence );
03091 if ( subRes.isEmpty() ) {
03092 return resourceCalendar->resourceName();
03093 } else {
03094 return resourceCalendar->labelForSubresource( subRes );
03095 }
03096 }
03097 return resourceCalendar->resourceName();
03098 }
03099
03100 return QString();
03101 }
03102