• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

KCal Library

  • sources
  • kde-4.12
  • kdepimlibs
  • kcal
incidenceformatter.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the kcal library.
3 
4  Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
5  Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6  Copyright (c) 2005 Rafal Rzepecki <divide@users.sourceforge.net>
7  Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
8 
9  This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Library General Public
11  License as published by the Free Software Foundation; either
12  version 2 of the License, or (at your option) any later version.
13 
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Library General Public License for more details.
18 
19  You should have received a copy of the GNU Library General Public License
20  along with this library; see the file COPYING.LIB. If not, write to
21  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  Boston, MA 02110-1301, USA.
23 */
37 #include "incidenceformatter.h"
38 #include "attachment.h"
39 #include "calendar.h"
40 #include "calendarlocal.h"
41 #ifndef KDEPIM_NO_KRESOURCES
42 #include "calendarresources.h"
43 #endif
44 #include "event.h"
45 #include "freebusy.h"
46 #include "icalformat.h"
47 #include "journal.h"
48 #include "todo.h"
49 
50 #include "kpimutils/email.h"
51 #include "kabc/phonenumber.h"
52 #include "kabc/vcardconverter.h"
53 #include "kabc/stdaddressbook.h"
54 
55 #include <kdatetime.h>
56 #include <kemailsettings.h>
57 
58 #include <kglobal.h>
59 #include <kiconloader.h>
60 #include <klocalizedstring.h>
61 #include <kcalendarsystem.h>
62 #include <ksystemtimezone.h>
63 #include <kmimetype.h>
64 
65 #include <QtCore/QBuffer>
66 #include <QtCore/QList>
67 #include <QTextDocument>
68 #include <QApplication>
69 
70 using namespace KCal;
71 using namespace IncidenceFormatter;
72 
73 /*******************
74  * General helpers
75  *******************/
76 
77 //@cond PRIVATE
78 static QString htmlAddLink( const QString &ref, const QString &text,
79  bool newline = true )
80 {
81  QString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" );
82  if ( newline ) {
83  tmpStr += '\n';
84  }
85  return tmpStr;
86 }
87 
88 static QString htmlAddTag( const QString &tag, const QString &text )
89 {
90  int numLineBreaks = text.count( "\n" );
91  QString str = '<' + tag + '>';
92  QString tmpText = text;
93  QString tmpStr = str;
94  if( numLineBreaks >= 0 ) {
95  if ( numLineBreaks > 0 ) {
96  int pos = 0;
97  QString tmp;
98  for ( int i = 0; i <= numLineBreaks; ++i ) {
99  pos = tmpText.indexOf( "\n" );
100  tmp = tmpText.left( pos );
101  tmpText = tmpText.right( tmpText.length() - pos - 1 );
102  tmpStr += tmp + "<br>";
103  }
104  } else {
105  tmpStr += tmpText;
106  }
107  }
108  tmpStr += "</" + tag + '>';
109  return tmpStr;
110 }
111 
112 static bool iamAttendee( Attendee *attendee )
113 {
114  // Check if I'm this attendee
115 
116  bool iam = false;
117  KEMailSettings settings;
118  QStringList profiles = settings.profiles();
119  for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
120  settings.setProfile( *it );
121  if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) {
122  iam = true;
123  break;
124  }
125  }
126  return iam;
127 }
128 
129 static bool iamOrganizer( Incidence *incidence )
130 {
131  // Check if I'm the organizer for this incidence
132 
133  if ( !incidence ) {
134  return false;
135  }
136 
137  bool iam = false;
138  KEMailSettings settings;
139  QStringList profiles = settings.profiles();
140  for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
141  settings.setProfile( *it );
142  if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) {
143  iam = true;
144  break;
145  }
146  }
147  return iam;
148 }
149 
150 static bool senderIsOrganizer( Incidence *incidence, const QString &sender )
151 {
152  // Check if the specified sender is the organizer
153 
154  if ( !incidence || sender.isEmpty() ) {
155  return true;
156  }
157 
158  bool isorg = true;
159  QString senderName, senderEmail;
160  if ( KPIMUtils::extractEmailAddressAndName( sender, senderEmail, senderName ) ) {
161  // for this heuristic, we say the sender is the organizer if either the name or the email match.
162  if ( incidence->organizer().email() != senderEmail &&
163  incidence->organizer().name() != senderName ) {
164  isorg = false;
165  }
166  }
167  return isorg;
168 }
169 
170 static QString firstAttendeeName( Incidence *incidence, const QString &defName )
171 {
172  QString name;
173  if ( !incidence ) {
174  return name;
175  }
176 
177  Attendee::List attendees = incidence->attendees();
178  if( attendees.count() > 0 ) {
179  Attendee *attendee = *attendees.begin();
180  name = attendee->name();
181  if ( name.isEmpty() ) {
182  name = attendee->email();
183  }
184  if ( name.isEmpty() ) {
185  name = defName;
186  }
187  }
188  return name;
189 }
190 //@endcond
191 
192 /*******************************************************************
193  * Helper functions for the extensive display (display viewer)
194  *******************************************************************/
195 
196 //@cond PRIVATE
197 static QString displayViewLinkPerson( const QString &email, QString name,
198  QString uid, const QString &iconPath )
199 {
200  // Make the search, if there is an email address to search on,
201  // and either name or uid is missing
202  if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
203 #ifndef KDEPIM_NO_KRESOURCES
204  KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
205  KABC::Addressee::List addressList = add_book->findByEmail( email );
206  KABC::Addressee o = ( !addressList.isEmpty() ? addressList.first() : KABC::Addressee() );
207  if ( !o.isEmpty() && addressList.size() < 2 ) {
208  if ( name.isEmpty() ) {
209  // No name set, so use the one from the addressbook
210  name = o.formattedName();
211  }
212  uid = o.uid();
213  } else {
214  // Email not found in the addressbook. Don't make a link
215  uid.clear();
216  }
217 #else
218  uid.clear();
219 #endif
220  }
221 
222  // Show the attendee
223  QString tmpString;
224  if ( !uid.isEmpty() ) {
225  // There is a UID, so make a link to the addressbook
226  if ( name.isEmpty() ) {
227  // Use the email address for text
228  tmpString += htmlAddLink( "uid:" + uid, email );
229  } else {
230  tmpString += htmlAddLink( "uid:" + uid, name );
231  }
232  } else {
233  // No UID, just show some text
234  tmpString += ( name.isEmpty() ? email : name );
235  }
236 
237  // Make the mailto link
238  if ( !email.isEmpty() && !iconPath.isNull() ) {
239  KUrl mailto;
240  mailto.setProtocol( "mailto" );
241  mailto.setPath( email );
242  tmpString += htmlAddLink( mailto.url(),
243  "<img valign=\"top\" src=\"" + iconPath + "\">" );
244  }
245 
246  return tmpString;
247 }
248 
249 static QString displayViewFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
250 {
251  QString tmpStr;
252  Attendee::List::ConstIterator it;
253  Attendee::List attendees = incidence->attendees();
254  KIconLoader *iconLoader = KIconLoader::global();
255  const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
256 
257  for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
258  Attendee *a = *it;
259  if ( a->role() != role ) {
260  // skip this role
261  continue;
262  }
263  if ( a->email() == incidence->organizer().email() ) {
264  // skip attendee that is also the organizer
265  continue;
266  }
267  tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid(), iconPath );
268  if ( !a->delegator().isEmpty() ) {
269  tmpStr += i18n( " (delegated by %1)", a->delegator() );
270  }
271  if ( !a->delegate().isEmpty() ) {
272  tmpStr += i18n( " (delegated to %1)", a->delegate() );
273  }
274  tmpStr += "<br>";
275  }
276  if ( tmpStr.endsWith( QLatin1String( "<br>" ) ) ) {
277  tmpStr.chop( 4 );
278  }
279  return tmpStr;
280 }
281 
282 static QString displayViewFormatAttendees( Incidence *incidence )
283 {
284  QString tmpStr, str;
285 
286  KIconLoader *iconLoader = KIconLoader::global();
287  const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
288 
289  // Add organizer link
290  int attendeeCount = incidence->attendees().count();
291  if ( attendeeCount > 1 ||
292  ( attendeeCount == 1 &&
293  incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
294  tmpStr += "<tr>";
295  tmpStr += "<td><b>" + i18n( "Organizer:" ) + "</b></td>";
296  tmpStr += "<td>" +
297  displayViewLinkPerson( incidence->organizer().email(),
298  incidence->organizer().name(),
299  QString(), iconPath ) +
300  "</td>";
301  tmpStr += "</tr>";
302  }
303 
304  // Add "chair"
305  str = displayViewFormatAttendeeRoleList( incidence, Attendee::Chair );
306  if ( !str.isEmpty() ) {
307  tmpStr += "<tr>";
308  tmpStr += "<td><b>" + i18n( "Chair:" ) + "</b></td>";
309  tmpStr += "<td>" + str + "</td>";
310  tmpStr += "</tr>";
311  }
312 
313  // Add required participants
314  str = displayViewFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
315  if ( !str.isEmpty() ) {
316  tmpStr += "<tr>";
317  tmpStr += "<td><b>" + i18n( "Required Participants:" ) + "</b></td>";
318  tmpStr += "<td>" + str + "</td>";
319  tmpStr += "</tr>";
320  }
321 
322  // Add optional participants
323  str = displayViewFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
324  if ( !str.isEmpty() ) {
325  tmpStr += "<tr>";
326  tmpStr += "<td><b>" + i18n( "Optional Participants:" ) + "</b></td>";
327  tmpStr += "<td>" + str + "</td>";
328  tmpStr += "</tr>";
329  }
330 
331  // Add observers
332  str = displayViewFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
333  if ( !str.isEmpty() ) {
334  tmpStr += "<tr>";
335  tmpStr += "<td><b>" + i18n( "Observers:" ) + "</b></td>";
336  tmpStr += "<td>" + str + "</td>";
337  tmpStr += "</tr>";
338  }
339 
340  return tmpStr;
341 }
342 
343 static QString displayViewFormatAttachments( Incidence *incidence )
344 {
345  QString tmpStr;
346  Attachment::List as = incidence->attachments();
347  Attachment::List::ConstIterator it;
348  int count = 0;
349  for ( it = as.constBegin(); it != as.constEnd(); ++it ) {
350  count++;
351  if ( (*it)->isUri() ) {
352  QString name;
353  if ( (*it)->uri().startsWith( QLatin1String( "kmail:" ) ) ) {
354  name = i18n( "Show mail" );
355  } else {
356  name = (*it)->label();
357  }
358  tmpStr += htmlAddLink( (*it)->uri(), name );
359  } else {
360  tmpStr += (*it)->label();
361  }
362  if ( count < as.count() ) {
363  tmpStr += "<br>";
364  }
365  }
366  return tmpStr;
367 }
368 
369 static QString displayViewFormatCategories( Incidence *incidence )
370 {
371  // We do not use Incidence::categoriesStr() since it does not have whitespace
372  return incidence->categories().join( ", " );
373 }
374 
375 static QString displayViewFormatCreationDate( Incidence *incidence, KDateTime::Spec spec )
376 {
377  KDateTime kdt = incidence->created().toTimeSpec( spec );
378  return i18n( "Creation date: %1", dateTimeToString( incidence->created(), false, true, spec ) );
379 }
380 
381 static QString displayViewFormatBirthday( Event *event )
382 {
383  if ( !event ) {
384  return QString();
385  }
386  if ( event->customProperty( "KABC", "BIRTHDAY" ) != "YES" &&
387  event->customProperty( "KABC", "ANNIVERSARY" ) != "YES" ) {
388  return QString();
389  }
390 
391  QString uid_1 = event->customProperty( "KABC", "UID-1" );
392  QString name_1 = event->customProperty( "KABC", "NAME-1" );
393  QString email_1= event->customProperty( "KABC", "EMAIL-1" );
394 
395  KIconLoader *iconLoader = KIconLoader::global();
396  const QString iconPath = iconLoader->iconPath( "mail-message-new", KIconLoader::Small );
397  //TODO: add a birthday cake icon
398  QString tmpStr = displayViewLinkPerson( email_1, name_1, uid_1, iconPath );
399 
400  return tmpStr;
401 }
402 
403 static QString displayViewFormatHeader( Incidence *incidence )
404 {
405  QString tmpStr = "<table><tr>";
406 
407  // show icons
408  KIconLoader *iconLoader = KIconLoader::global();
409  tmpStr += "<td>";
410 
411  // TODO: KDE5. Make the function QString Incidence::getPixmap() so we don't
412  // need downcasting.
413 
414  if ( incidence->type() == "Todo" ) {
415  tmpStr += "<img valign=\"top\" src=\"";
416  Todo *todo = static_cast<Todo *>( incidence );
417  if ( !todo->isCompleted() ) {
418  tmpStr += iconLoader->iconPath( "view-calendar-tasks", KIconLoader::Small );
419  } else {
420  tmpStr += iconLoader->iconPath( "task-complete", KIconLoader::Small );
421  }
422  tmpStr += "\">";
423  }
424 
425  if ( incidence->type() == "Event" ) {
426  QString iconPath;
427  if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
428  iconPath = iconLoader->iconPath( "view-calendar-birthday", KIconLoader::Small );
429  } else if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
430  iconPath = iconLoader->iconPath( "view-calendar-wedding-anniversary", KIconLoader::Small );
431  } else {
432  iconPath = iconLoader->iconPath( "view-calendar-day", KIconLoader::Small );
433  }
434  tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
435  }
436 
437  if ( incidence->type() == "Journal" ) {
438  tmpStr += "<img valign=\"top\" src=\"" +
439  iconLoader->iconPath( "view-pim-journal", KIconLoader::Small ) +
440  "\">";
441  }
442 
443  if ( incidence->isAlarmEnabled() ) {
444  tmpStr += "<img valign=\"top\" src=\"" +
445  iconLoader->iconPath( "preferences-desktop-notification-bell", KIconLoader::Small ) +
446  "\">";
447  }
448  if ( incidence->recurs() ) {
449  tmpStr += "<img valign=\"top\" src=\"" +
450  iconLoader->iconPath( "edit-redo", KIconLoader::Small ) +
451  "\">";
452  }
453  if ( incidence->isReadOnly() ) {
454  tmpStr += "<img valign=\"top\" src=\"" +
455  iconLoader->iconPath( "object-locked", KIconLoader::Small ) +
456  "\">";
457  }
458  tmpStr += "</td>";
459 
460  tmpStr += "<td>";
461  tmpStr += "<b><u>" + incidence->richSummary() + "</u></b>";
462  tmpStr += "</td>";
463 
464  tmpStr += "</tr></table>";
465 
466  return tmpStr;
467 }
468 
469 static QString displayViewFormatEvent( const QString &calStr, Event *event,
470  const QDate &date, KDateTime::Spec spec )
471 {
472  if ( !event ) {
473  return QString();
474  }
475 
476  QString tmpStr = displayViewFormatHeader( event );
477 
478  tmpStr += "<table>";
479  tmpStr += "<col width=\"25%\"/>";
480  tmpStr += "<col width=\"75%\"/>";
481 
482  if ( !calStr.isEmpty() ) {
483  tmpStr += "<tr>";
484  tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
485  tmpStr += "<td>" + calStr + "</td>";
486  tmpStr += "</tr>";
487  }
488 
489  if ( !event->location().isEmpty() ) {
490  tmpStr += "<tr>";
491  tmpStr += "<td><b>" + i18nc( "@title:column event location", "Location:" ) + "</b></td>";
492  tmpStr += "<td>" + event->richLocation() + "</td>";
493  tmpStr += "</tr>";
494  }
495 
496  KDateTime startDt = event->dtStart();
497  KDateTime endDt = event->dtEnd();
498  if ( event->recurs() ) {
499  if ( date.isValid() ) {
500  KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
501  int diffDays = startDt.daysTo( kdt );
502  kdt = kdt.addSecs( -1 );
503  startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
504  if ( event->hasEndDate() ) {
505  endDt = endDt.addDays( diffDays );
506  if ( startDt > endDt ) {
507  startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
508  endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
509  }
510  }
511  }
512  }
513 
514  tmpStr += "<tr>";
515  if ( event->allDay() ) {
516  if ( event->isMultiDay() ) {
517  tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
518  tmpStr += "<td>" +
519  i18nc( "<beginTime> - <endTime>","%1 - %2",
520  dateToString( startDt, false, spec ),
521  dateToString( endDt, false, spec ) ) +
522  "</td>";
523  } else {
524  tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
525  tmpStr += "<td>" +
526  i18nc( "date as string","%1",
527  dateToString( startDt, false, spec ) ) +
528  "</td>";
529  }
530  } else {
531  if ( event->isMultiDay() ) {
532  tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
533  tmpStr += "<td>" +
534  i18nc( "<beginTime> - <endTime>","%1 - %2",
535  dateToString( startDt, false, spec ),
536  dateToString( endDt, false, spec ) ) +
537  "</td>";
538  } else {
539  tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
540  tmpStr += "<td>" +
541  i18nc( "date as string", "%1",
542  dateToString( startDt, false, spec ) ) +
543  "</td>";
544 
545  tmpStr += "</tr><tr>";
546  tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>";
547  if ( event->hasEndDate() && startDt != endDt ) {
548  tmpStr += "<td>" +
549  i18nc( "<beginTime> - <endTime>","%1 - %2",
550  timeToString( startDt, true, spec ),
551  timeToString( endDt, true, spec ) ) +
552  "</td>";
553  } else {
554  tmpStr += "<td>" +
555  timeToString( startDt, true, spec ) +
556  "</td>";
557  }
558  }
559  }
560  tmpStr += "</tr>";
561 
562  QString durStr = durationString( event );
563  if ( !durStr.isEmpty() ) {
564  tmpStr += "<tr>";
565  tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
566  tmpStr += "<td>" + durStr + "</td>";
567  tmpStr += "</tr>";
568  }
569 
570  if ( event->recurs() ) {
571  tmpStr += "<tr>";
572  tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
573  tmpStr += "<td>" +
574  recurrenceString( event ) +
575  "</td>";
576  tmpStr += "</tr>";
577  }
578 
579  const bool isBirthday = event->customProperty( "KABC", "BIRTHDAY" ) == "YES";
580  const bool isAnniversary = event->customProperty( "KABC", "ANNIVERSARY" ) == "YES";
581 
582  if ( isBirthday || isAnniversary ) {
583  tmpStr += "<tr>";
584  if ( isAnniversary ) {
585  tmpStr += "<td><b>" + i18n( "Anniversary:" ) + "</b></td>";
586  } else {
587  tmpStr += "<td><b>" + i18n( "Birthday:" ) + "</b></td>";
588  }
589  tmpStr += "<td>" + displayViewFormatBirthday( event ) + "</td>";
590  tmpStr += "</tr>";
591  tmpStr += "</table>";
592  return tmpStr;
593  }
594 
595  if ( !event->description().isEmpty() ) {
596  tmpStr += "<tr>";
597  tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
598  tmpStr += "<td>" + event->richDescription() + "</td>";
599  tmpStr += "</tr>";
600  }
601 
602  // TODO: print comments?
603 
604  int reminderCount = event->alarms().count();
605  if ( reminderCount > 0 && event->isAlarmEnabled() ) {
606  tmpStr += "<tr>";
607  tmpStr += "<td><b>" +
608  i18np( "Reminder:", "Reminders:", reminderCount ) +
609  "</b></td>";
610  tmpStr += "<td>" + reminderStringList( event ).join( "<br>" ) + "</td>";
611  tmpStr += "</tr>";
612  }
613 
614  tmpStr += displayViewFormatAttendees( event );
615 
616  int categoryCount = event->categories().count();
617  if ( categoryCount > 0 ) {
618  tmpStr += "<tr>";
619  tmpStr += "<td><b>";
620  tmpStr += i18np( "Category:", "Categories:", categoryCount ) +
621  "</b></td>";
622  tmpStr += "<td>" + displayViewFormatCategories( event ) + "</td>";
623  tmpStr += "</tr>";
624  }
625 
626  int attachmentCount = event->attachments().count();
627  if ( attachmentCount > 0 ) {
628  tmpStr += "<tr>";
629  tmpStr += "<td><b>" +
630  i18np( "Attachment:", "Attachments:", attachmentCount ) +
631  "</b></td>";
632  tmpStr += "<td>" + displayViewFormatAttachments( event ) + "</td>";
633  tmpStr += "</tr>";
634  }
635  tmpStr += "</table>";
636 
637  tmpStr += "<p><em>" + displayViewFormatCreationDate( event, spec ) + "</em>";
638 
639  return tmpStr;
640 }
641 
642 static QString displayViewFormatTodo( const QString &calStr, Todo *todo,
643  const QDate &date, KDateTime::Spec spec )
644 {
645  if ( !todo ) {
646  return QString();
647  }
648 
649  QString tmpStr = displayViewFormatHeader( todo );
650 
651  tmpStr += "<table>";
652  tmpStr += "<col width=\"25%\"/>";
653  tmpStr += "<col width=\"75%\"/>";
654 
655  if ( !calStr.isEmpty() ) {
656  tmpStr += "<tr>";
657  tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
658  tmpStr += "<td>" + calStr + "</td>";
659  tmpStr += "</tr>";
660  }
661 
662  if ( !todo->location().isEmpty() ) {
663  tmpStr += "<tr>";
664  tmpStr += "<td><b>" + i18nc( "@title:column to-do location", "Location:" ) + "</b></td>";
665  tmpStr += "<td>" + todo->richLocation() + "</td>";
666  tmpStr += "</tr>";
667  }
668 
669  if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
670  KDateTime startDt = todo->dtStart();
671  if ( todo->recurs() ) {
672  if ( date.isValid() ) {
673  startDt.setDate( date );
674  }
675  }
676  tmpStr += "<tr>";
677  tmpStr += "<td><b>" +
678  i18nc( "to-do start date/time", "Start:" ) +
679  "</b></td>";
680  tmpStr += "<td>" +
681  dateTimeToString( startDt, todo->allDay(), false, spec ) +
682  "</td>";
683  tmpStr += "</tr>";
684  }
685 
686  if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
687  KDateTime dueDt = todo->dtDue();
688  if ( todo->recurs() ) {
689  if ( date.isValid() ) {
690  KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
691  kdt = kdt.addSecs( -1 );
692  dueDt.setDate( todo->recurrence()->getNextDateTime( kdt ).date() );
693  }
694  }
695  tmpStr += "<tr>";
696  tmpStr += "<td><b>" +
697  i18nc( "to-do due date/time", "Due:" ) +
698  "</b></td>";
699  tmpStr += "<td>" +
700  dateTimeToString( dueDt, todo->allDay(), false, spec ) +
701  "</td>";
702  tmpStr += "</tr>";
703  }
704 
705  QString durStr = durationString( todo );
706  if ( !durStr.isEmpty() ) {
707  tmpStr += "<tr>";
708  tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>";
709  tmpStr += "<td>" + durStr + "</td>";
710  tmpStr += "</tr>";
711  }
712 
713  if ( todo->recurs() ) {
714  tmpStr += "<tr>";
715  tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>";
716  tmpStr += "<td>" +
717  recurrenceString( todo ) +
718  "</td>";
719  tmpStr += "</tr>";
720  }
721 
722  if ( !todo->description().isEmpty() ) {
723  tmpStr += "<tr>";
724  tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
725  tmpStr += "<td>" + todo->richDescription() + "</td>";
726  tmpStr += "</tr>";
727  }
728 
729  // TODO: print comments?
730 
731  int reminderCount = todo->alarms().count();
732  if ( reminderCount > 0 && todo->isAlarmEnabled() ) {
733  tmpStr += "<tr>";
734  tmpStr += "<td><b>" +
735  i18np( "Reminder:", "Reminders:", reminderCount ) +
736  "</b></td>";
737  tmpStr += "<td>" + reminderStringList( todo ).join( "<br>" ) + "</td>";
738  tmpStr += "</tr>";
739  }
740 
741  tmpStr += displayViewFormatAttendees( todo );
742 
743  int categoryCount = todo->categories().count();
744  if ( categoryCount > 0 ) {
745  tmpStr += "<tr>";
746  tmpStr += "<td><b>" +
747  i18np( "Category:", "Categories:", categoryCount ) +
748  "</b></td>";
749  tmpStr += "<td>" + displayViewFormatCategories( todo ) + "</td>";
750  tmpStr += "</tr>";
751  }
752 
753  if ( todo->priority() > 0 ) {
754  tmpStr += "<tr>";
755  tmpStr += "<td><b>" + i18n( "Priority:" ) + "</b></td>";
756  tmpStr += "<td>";
757  tmpStr += QString::number( todo->priority() );
758  tmpStr += "</td>";
759  tmpStr += "</tr>";
760  }
761 
762  tmpStr += "<tr>";
763  if ( todo->isCompleted() ) {
764  tmpStr += "<td><b>" + i18nc( "Completed: date", "Completed:" ) + "</b></td>";
765  tmpStr += "<td>";
766  tmpStr += todo->completedStr();
767  } else {
768  tmpStr += "<td><b>" + i18n( "Percent Done:" ) + "</b></td>";
769  tmpStr += "<td>";
770  tmpStr += i18n( "%1%", todo->percentComplete() );
771  }
772  tmpStr += "</td>";
773  tmpStr += "</tr>";
774 
775  int attachmentCount = todo->attachments().count();
776  if ( attachmentCount > 0 ) {
777  tmpStr += "<tr>";
778  tmpStr += "<td><b>" +
779  i18np( "Attachment:", "Attachments:", attachmentCount ) +
780  "</b></td>";
781  tmpStr += "<td>" + displayViewFormatAttachments( todo ) + "</td>";
782  tmpStr += "</tr>";
783  }
784  tmpStr += "</table>";
785 
786  tmpStr += "<p><em>" + displayViewFormatCreationDate( todo, spec ) + "</em>";
787 
788  return tmpStr;
789 }
790 
791 static QString displayViewFormatJournal( const QString &calStr, Journal *journal,
792  KDateTime::Spec spec )
793 {
794  if ( !journal ) {
795  return QString();
796  }
797 
798  QString tmpStr = displayViewFormatHeader( journal );
799 
800  tmpStr += "<table>";
801  tmpStr += "<col width=\"25%\"/>";
802  tmpStr += "<col width=\"75%\"/>";
803 
804  if ( !calStr.isEmpty() ) {
805  tmpStr += "<tr>";
806  tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>";
807  tmpStr += "<td>" + calStr + "</td>";
808  tmpStr += "</tr>";
809  }
810 
811  tmpStr += "<tr>";
812  tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>";
813  tmpStr += "<td>" +
814  dateToString( journal->dtStart(), false, spec ) +
815  "</td>";
816  tmpStr += "</tr>";
817 
818  if ( !journal->description().isEmpty() ) {
819  tmpStr += "<tr>";
820  tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>";
821  tmpStr += "<td>" + journal->richDescription() + "</td>";
822  tmpStr += "</tr>";
823  }
824 
825  int categoryCount = journal->categories().count();
826  if ( categoryCount > 0 ) {
827  tmpStr += "<tr>";
828  tmpStr += "<td><b>" +
829  i18np( "Category:", "Categories:", categoryCount ) +
830  "</b></td>";
831  tmpStr += "<td>" + displayViewFormatCategories( journal ) + "</td>";
832  tmpStr += "</tr>";
833  }
834 
835  tmpStr += "</table>";
836 
837  tmpStr += "<p><em>" + displayViewFormatCreationDate( journal, spec ) + "</em>";
838 
839  return tmpStr;
840 }
841 
842 static QString displayViewFormatFreeBusy( const QString &calStr, FreeBusy *fb,
843  KDateTime::Spec spec )
844 {
845  Q_UNUSED( calStr );
846  if ( !fb ) {
847  return QString();
848  }
849 
850  QString tmpStr(
851  htmlAddTag(
852  "h2", i18n( "Free/Busy information for %1", fb->organizer().fullName() ) ) );
853 
854  tmpStr += htmlAddTag( "h4",
855  i18n( "Busy times in date range %1 - %2:",
856  dateToString( fb->dtStart(), true, spec ),
857  dateToString( fb->dtEnd(), true, spec ) ) );
858 
859  QList<Period> periods = fb->busyPeriods();
860 
861  QString text =
862  htmlAddTag( "em",
863  htmlAddTag( "b", i18nc( "tag for busy periods list", "Busy:" ) ) );
864 
865  QList<Period>::iterator it;
866  for ( it = periods.begin(); it != periods.end(); ++it ) {
867  Period per = *it;
868  if ( per.hasDuration() ) {
869  int dur = per.duration().asSeconds();
870  QString cont;
871  if ( dur >= 3600 ) {
872  cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
873  dur %= 3600;
874  }
875  if ( dur >= 60 ) {
876  cont += i18ncp( "minutes part duration", "1 minute ", "%1 minutes ", dur / 60 );
877  dur %= 60;
878  }
879  if ( dur > 0 ) {
880  cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
881  }
882  text += i18nc( "startDate for duration", "%1 for %2",
883  dateTimeToString( per.start(), false, true, spec ),
884  cont );
885  text += "<br>";
886  } else {
887  if ( per.start().date() == per.end().date() ) {
888  text += i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
889  dateToString( per.start(), true, spec ),
890  timeToString( per.start(), true, spec ),
891  timeToString( per.end(), true, spec ) );
892  } else {
893  text += i18nc( "fromDateTime - toDateTime", "%1 - %2",
894  dateTimeToString( per.start(), false, true, spec ),
895  dateTimeToString( per.end(), false, true, spec ) );
896  }
897  text += "<br>";
898  }
899  }
900  tmpStr += htmlAddTag( "p", text );
901  return tmpStr;
902 }
903 //@endcond
904 
905 //@cond PRIVATE
906 class KCal::IncidenceFormatter::EventViewerVisitor
907  : public IncidenceBase::Visitor
908 {
909  public:
910  EventViewerVisitor()
911  : mCalendar( 0 ), mSpec( KDateTime::Spec() ), mResult( "" ) {}
912 
913  bool act( Calendar *calendar, IncidenceBase *incidence, const QDate &date,
914  KDateTime::Spec spec=KDateTime::Spec() )
915  {
916  mCalendar = calendar;
917  mSourceName.clear();
918  mDate = date;
919  mSpec = spec;
920  mResult = "";
921  return incidence->accept( *this );
922  }
923 
924  bool act( const QString &sourceName, IncidenceBase *incidence, const QDate &date,
925  KDateTime::Spec spec=KDateTime::Spec() )
926  {
927  mCalendar = 0;
928  mSourceName = sourceName;
929  mDate = date;
930  mSpec = spec;
931  mResult = "";
932  return incidence->accept( *this );
933  }
934 
935  QString result() const { return mResult; }
936 
937  protected:
938  bool visit( Event *event )
939  {
940  const QString calStr = mCalendar ? resourceString( mCalendar, event ) : mSourceName;
941  mResult = displayViewFormatEvent( calStr, event, mDate, mSpec );
942  return !mResult.isEmpty();
943  }
944  bool visit( Todo *todo )
945  {
946  const QString calStr = mCalendar ? resourceString( mCalendar, todo ) : mSourceName;
947  mResult = displayViewFormatTodo( calStr, todo, mDate, mSpec );
948  return !mResult.isEmpty();
949  }
950  bool visit( Journal *journal )
951  {
952  const QString calStr = mCalendar ? resourceString( mCalendar, journal ) : mSourceName;
953  mResult = displayViewFormatJournal( calStr, journal, mSpec );
954  return !mResult.isEmpty();
955  }
956  bool visit( FreeBusy *fb )
957  {
958  mResult = displayViewFormatFreeBusy( mSourceName, fb, mSpec );
959  return !mResult.isEmpty();
960  }
961 
962  protected:
963  Calendar *mCalendar;
964  QString mSourceName;
965  QDate mDate;
966  KDateTime::Spec mSpec;
967  QString mResult;
968 };
969 //@endcond
970 
971 QString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence )
972 {
973  return extensiveDisplayStr( 0, incidence, QDate(), KDateTime::Spec() );
974 }
975 
976 QString IncidenceFormatter::extensiveDisplayStr( IncidenceBase *incidence,
977  KDateTime::Spec spec )
978 {
979  if ( !incidence ) {
980  return QString();
981  }
982 
983  EventViewerVisitor v;
984  if ( v.act( 0, incidence, QDate(), spec ) ) {
985  return v.result();
986  } else {
987  return QString();
988  }
989 }
990 
991 QString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar,
992  IncidenceBase *incidence,
993  const QDate &date,
994  KDateTime::Spec spec )
995 {
996  if ( !incidence ) {
997  return QString();
998  }
999 
1000  EventViewerVisitor v;
1001  if ( v.act( calendar, incidence, date, spec ) ) {
1002  return v.result();
1003  } else {
1004  return QString();
1005  }
1006 }
1007 
1008 QString IncidenceFormatter::extensiveDisplayStr( const QString &sourceName,
1009  IncidenceBase *incidence,
1010  const QDate &date,
1011  KDateTime::Spec spec )
1012 {
1013  if ( !incidence ) {
1014  return QString();
1015  }
1016 
1017  EventViewerVisitor v;
1018  if ( v.act( sourceName, incidence, date, spec ) ) {
1019  return v.result();
1020  } else {
1021  return QString();
1022  }
1023 }
1024 /***********************************************************************
1025  * Helper functions for the body part formatter of kmail (Invitations)
1026  ***********************************************************************/
1027 
1028 //@cond PRIVATE
1029 static QString string2HTML( const QString &str )
1030 {
1031  return Qt::convertFromPlainText( str, Qt::WhiteSpaceNormal );
1032 }
1033 
1034 static QString cleanHtml( const QString &html )
1035 {
1036  QRegExp rx( "<body[^>]*>(.*)</body>", Qt::CaseInsensitive );
1037  rx.indexIn( html );
1038  QString body = rx.cap( 1 );
1039 
1040  return Qt::escape( body.remove( QRegExp( "<[^>]*>" ) ).trimmed() );
1041 }
1042 
1043 static QString eventStartTimeStr( Event *event )
1044 {
1045  QString tmp;
1046  if ( !event->allDay() ) {
1047  tmp = i18nc( "%1: Start Date, %2: Start Time", "%1 %2",
1048  dateToString( event->dtStart(), true, KSystemTimeZones::local() ),
1049  timeToString( event->dtStart(), true, KSystemTimeZones::local() ) );
1050  } else {
1051  tmp = i18nc( "%1: Start Date", "%1 (all day)",
1052  dateToString( event->dtStart(), true, KSystemTimeZones::local() ) );
1053  }
1054  return tmp;
1055 }
1056 
1057 static QString eventEndTimeStr( Event *event )
1058 {
1059  QString tmp;
1060  if ( event->hasEndDate() && event->dtEnd().isValid() ) {
1061  if ( !event->allDay() ) {
1062  tmp = i18nc( "%1: End Date, %2: End Time", "%1 %2",
1063  dateToString( event->dtEnd(), true, KSystemTimeZones::local() ),
1064  timeToString( event->dtEnd(), true, KSystemTimeZones::local() ) );
1065  } else {
1066  tmp = i18nc( "%1: End Date", "%1 (all day)",
1067  dateToString( event->dtEnd(), true, KSystemTimeZones::local() ) );
1068  }
1069  }
1070  return tmp;
1071 }
1072 
1073 static QString invitationRow( const QString &cell1, const QString &cell2 )
1074 {
1075  return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n";
1076 }
1077 
1078 static Attendee *findDelegatedFromMyAttendee( Incidence *incidence )
1079 {
1080  // Return the first attendee that was delegated-from me
1081 
1082  Attendee *attendee = 0;
1083  if ( !incidence ) {
1084  return attendee;
1085  }
1086 
1087  KEMailSettings settings;
1088  QStringList profiles = settings.profiles();
1089  for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
1090  settings.setProfile( *it );
1091 
1092  QString delegatorName, delegatorEmail;
1093  Attendee::List attendees = incidence->attendees();
1094  Attendee::List::ConstIterator it2;
1095  for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) {
1096  Attendee *a = *it2;
1097  KPIMUtils::extractEmailAddressAndName( a->delegator(), delegatorEmail, delegatorName );
1098  if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) {
1099  attendee = a;
1100  break;
1101  }
1102  }
1103  }
1104  return attendee;
1105 }
1106 
1107 static Attendee *findMyAttendee( Incidence *incidence )
1108 {
1109  // Return the attendee for the incidence that is probably me
1110 
1111  Attendee *attendee = 0;
1112  if ( !incidence ) {
1113  return attendee;
1114  }
1115 
1116  KEMailSettings settings;
1117  QStringList profiles = settings.profiles();
1118  for ( QStringList::Iterator it=profiles.begin(); it != profiles.end(); ++it ) {
1119  settings.setProfile( *it );
1120 
1121  Attendee::List attendees = incidence->attendees();
1122  Attendee::List::ConstIterator it2;
1123  for ( it2 = attendees.constBegin(); it2 != attendees.constEnd(); ++it2 ) {
1124  Attendee *a = *it2;
1125  if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) {
1126  attendee = a;
1127  break;
1128  }
1129  }
1130  }
1131  return attendee;
1132 }
1133 
1134 static Attendee *findAttendee( Incidence *incidence, const QString &email )
1135 {
1136  // Search for an attendee by email address
1137 
1138  Attendee *attendee = 0;
1139  if ( !incidence ) {
1140  return attendee;
1141  }
1142 
1143  Attendee::List attendees = incidence->attendees();
1144  Attendee::List::ConstIterator it;
1145  for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
1146  Attendee *a = *it;
1147  if ( email == a->email() ) {
1148  attendee = a;
1149  break;
1150  }
1151  }
1152  return attendee;
1153 }
1154 
1155 static bool rsvpRequested( Incidence *incidence )
1156 {
1157  if ( !incidence ) {
1158  return false;
1159  }
1160 
1161  //use a heuristic to determine if a response is requested.
1162 
1163  bool rsvp = true; // better send superfluously than not at all
1164  Attendee::List attendees = incidence->attendees();
1165  Attendee::List::ConstIterator it;
1166  for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
1167  if ( it == attendees.constBegin() ) {
1168  rsvp = (*it)->RSVP(); // use what the first one has
1169  } else {
1170  if ( (*it)->RSVP() != rsvp ) {
1171  rsvp = true; // they differ, default
1172  break;
1173  }
1174  }
1175  }
1176  return rsvp;
1177 }
1178 
1179 static QString rsvpRequestedStr( bool rsvpRequested, const QString &role )
1180 {
1181  if ( rsvpRequested ) {
1182  if ( role.isEmpty() ) {
1183  return i18n( "Your response is requested" );
1184  } else {
1185  return i18n( "Your response as <b>%1</b> is requested", role );
1186  }
1187  } else {
1188  if ( role.isEmpty() ) {
1189  return i18n( "No response is necessary" );
1190  } else {
1191  return i18n( "No response as <b>%1</b> is necessary", role );
1192  }
1193  }
1194 }
1195 
1196 static QString myStatusStr( Incidence *incidence )
1197 {
1198  QString ret;
1199  Attendee *a = findMyAttendee( incidence );
1200  if ( a &&
1201  a->status() != Attendee::NeedsAction && a->status() != Attendee::Delegated ) {
1202  ret = i18n( "(<b>Note</b>: the Organizer preset your response to <b>%1</b>)",
1203  Attendee::statusName( a->status() ) );
1204  }
1205  return ret;
1206 }
1207 
1208 static QString invitationPerson( const QString &email, QString name, QString uid )
1209 {
1210  // Make the search, if there is an email address to search on,
1211  // and either name or uid is missing
1212  if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) {
1213 #ifndef KDEPIM_NO_KRESOURCES
1214  KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
1215  KABC::Addressee::List addressList = add_book->findByEmail( email );
1216  if ( !addressList.isEmpty() ) {
1217  KABC::Addressee o = addressList.first();
1218  if ( !o.isEmpty() && addressList.size() < 2 ) {
1219  if ( name.isEmpty() ) {
1220  // No name set, so use the one from the addressbook
1221  name = o.formattedName();
1222  }
1223  uid = o.uid();
1224  } else {
1225  // Email not found in the addressbook. Don't make a link
1226  uid.clear();
1227  }
1228  }
1229 #else
1230  uid.clear();
1231 #endif
1232  }
1233 
1234  // Show the attendee
1235  QString tmpString;
1236  if ( !uid.isEmpty() ) {
1237  // There is a UID, so make a link to the addressbook
1238  if ( name.isEmpty() ) {
1239  // Use the email address for text
1240  tmpString += htmlAddLink( "uid:" + uid, email );
1241  } else {
1242  tmpString += htmlAddLink( "uid:" + uid, name );
1243  }
1244  } else {
1245  // No UID, just show some text
1246  tmpString += ( name.isEmpty() ? email : name );
1247  }
1248  tmpString += '\n';
1249 
1250  // Make the mailto link
1251  if ( !email.isEmpty() ) {
1252  KCal::Person person( name, email );
1253  KUrl mailto;
1254  mailto.setProtocol( "mailto" );
1255  mailto.setPath( person.fullName() );
1256  const QString iconPath =
1257  KIconLoader::global()->iconPath( "mail-message-new", KIconLoader::Small );
1258  tmpString += htmlAddLink( mailto.url(),
1259  "<img valign=\"top\" src=\"" + iconPath + "\">" );
1260  }
1261  tmpString += '\n';
1262 
1263  return tmpString;
1264 }
1265 
1266 static QString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode )
1267 {
1268  // if description and comment -> use both
1269  // if description, but no comment -> use the desc as the comment (and no desc)
1270  // if comment, but no description -> use the comment and no description
1271 
1272  QString html;
1273  QString descr;
1274  QStringList comments;
1275 
1276  if ( incidence->comments().isEmpty() ) {
1277  if ( !incidence->description().isEmpty() ) {
1278  // use description as comments
1279  if ( !incidence->descriptionIsRich() ) {
1280  comments << string2HTML( incidence->description() );
1281  } else {
1282  comments << incidence->richDescription();
1283  if ( noHtmlMode ) {
1284  comments[0] = cleanHtml( comments[0] );
1285  }
1286  comments[0] = htmlAddTag( "p", comments[0] );
1287  }
1288  }
1289  //else desc and comments are empty
1290  } else {
1291  // non-empty comments
1292  foreach ( const QString &c, incidence->comments() ) {
1293  if ( !c.isEmpty() ) {
1294  // kcal doesn't know about richtext comments, so we need to guess
1295  if ( !Qt::mightBeRichText( c ) ) {
1296  comments << string2HTML( c );
1297  } else {
1298  if ( noHtmlMode ) {
1299  comments << cleanHtml( cleanHtml( "<body>" + c + "</body>" ) );
1300  } else {
1301  comments << c;
1302  }
1303  }
1304  }
1305  }
1306  if ( !incidence->description().isEmpty() ) {
1307  // use description too
1308  if ( !incidence->descriptionIsRich() ) {
1309  descr = string2HTML( incidence->description() );
1310  } else {
1311  descr = incidence->richDescription();
1312  if ( noHtmlMode ) {
1313  descr = cleanHtml( descr );
1314  }
1315  descr = htmlAddTag( "p", descr );
1316  }
1317  }
1318  }
1319 
1320  if( !descr.isEmpty() ) {
1321  html += "<p>";
1322  html += "<table border=\"0\" style=\"margin-top:4px;\">";
1323  html += "<tr><td><center>" +
1324  htmlAddTag( "u", i18n( "Description:" ) ) +
1325  "</center></td></tr>";
1326  html += "<tr><td>" + descr + "</td></tr>";
1327  html += "</table>";
1328  }
1329 
1330  if ( !comments.isEmpty() ) {
1331  html += "<p>";
1332  html += "<table border=\"0\" style=\"margin-top:4px;\">";
1333  html += "<tr><td><center>" +
1334  htmlAddTag( "u", i18n( "Comments:" ) ) +
1335  "</center></td></tr>";
1336  html += "<tr><td>";
1337  if ( comments.count() > 1 ) {
1338  html += "<ul>";
1339  for ( int i=0; i < comments.count(); ++i ) {
1340  html += "<li>" + comments[i] + "</li>";
1341  }
1342  html += "</ul>";
1343  } else {
1344  html += comments[0];
1345  }
1346  html += "</td></tr>";
1347  html += "</table>";
1348  }
1349  return html;
1350 }
1351 
1352 static QString invitationDetailsEvent( Event *event, bool noHtmlMode, KDateTime::Spec spec )
1353 {
1354  // Invitation details are formatted into an HTML table
1355  if ( !event ) {
1356  return QString();
1357  }
1358 
1359  QString sSummary = i18n( "Summary unspecified" );
1360  if ( !event->summary().isEmpty() ) {
1361  if ( !event->summaryIsRich() ) {
1362  sSummary = Qt::escape( event->summary() );
1363  } else {
1364  sSummary = event->richSummary();
1365  if ( noHtmlMode ) {
1366  sSummary = cleanHtml( sSummary );
1367  }
1368  }
1369  }
1370 
1371  QString sLocation = i18nc( "event location", "Location unspecified" );
1372  if ( !event->location().isEmpty() ) {
1373  if ( !event->locationIsRich() ) {
1374  sLocation = Qt::escape( event->location() );
1375  } else {
1376  sLocation = event->richLocation();
1377  if ( noHtmlMode ) {
1378  sLocation = cleanHtml( sLocation );
1379  }
1380  }
1381  }
1382 
1383  QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" );
1384  QString html = QString( "<div dir=\"%1\">\n" ).arg( dir );
1385  html += "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
1386 
1387  // Invitation summary & location rows
1388  html += invitationRow( i18n( "What:" ), sSummary );
1389  html += invitationRow( i18n( "Where:" ), sLocation );
1390 
1391  // If a 1 day event
1392  if ( event->dtStart().date() == event->dtEnd().date() ) {
1393  html += invitationRow( i18n( "Date:" ), dateToString( event->dtStart(), false, spec ) );
1394  if ( !event->allDay() ) {
1395  html += invitationRow( i18n( "Time:" ),
1396  timeToString( event->dtStart(), true, spec ) +
1397  " - " +
1398  timeToString( event->dtEnd(), true, spec ) );
1399  }
1400  } else {
1401  html += invitationRow( i18nc( "starting date", "From:" ),
1402  dateToString( event->dtStart(), false, spec ) );
1403  if ( !event->allDay() ) {
1404  html += invitationRow( i18nc( "starting time", "At:" ),
1405  timeToString( event->dtStart(), true, spec ) );
1406  }
1407  if ( event->hasEndDate() ) {
1408  html += invitationRow( i18nc( "ending date", "To:" ),
1409  dateToString( event->dtEnd(), false, spec ) );
1410  if ( !event->allDay() ) {
1411  html += invitationRow( i18nc( "ending time", "At:" ),
1412  timeToString( event->dtEnd(), true, spec ) );
1413  }
1414  } else {
1415  html += invitationRow( i18nc( "ending date", "To:" ),
1416  i18n( "no end date specified" ) );
1417  }
1418  }
1419 
1420  // Invitation Duration Row
1421  QString durStr = durationString( event );
1422  if ( !durStr.isEmpty() ) {
1423  html += invitationRow( i18n( "Duration:" ), durStr );
1424  }
1425 
1426  if ( event->recurs() ) {
1427  html += invitationRow( i18n( "Recurrence:" ), recurrenceString( event ) );
1428  }
1429 
1430  html += "</table></div>\n";
1431  html += invitationsDetailsIncidence( event, noHtmlMode );
1432 
1433  return html;
1434 }
1435 
1436 static QString invitationDetailsTodo( Todo *todo, bool noHtmlMode, KDateTime::Spec spec )
1437 {
1438  // To-do details are formatted into an HTML table
1439  if ( !todo ) {
1440  return QString();
1441  }
1442 
1443  QString sSummary = i18n( "Summary unspecified" );
1444  if ( !todo->summary().isEmpty() ) {
1445  if ( !todo->summaryIsRich() ) {
1446  sSummary = Qt::escape( todo->summary() );
1447  } else {
1448  sSummary = todo->richSummary();
1449  if ( noHtmlMode ) {
1450  sSummary = cleanHtml( sSummary );
1451  }
1452  }
1453  }
1454 
1455  QString sLocation = i18nc( "todo location", "Location unspecified" );
1456  if ( !todo->location().isEmpty() ) {
1457  if ( !todo->locationIsRich() ) {
1458  sLocation = Qt::escape( todo->location() );
1459  } else {
1460  sLocation = todo->richLocation();
1461  if ( noHtmlMode ) {
1462  sLocation = cleanHtml( sLocation );
1463  }
1464  }
1465  }
1466 
1467  QString dir = ( QApplication::isRightToLeft() ? "rtl" : "ltr" );
1468  QString html = QString( "<div dir=\"%1\">\n" ).arg( dir );
1469  html += "<table cellspacing=\"4\" style=\"border-width:4px; border-style:groove\">";
1470 
1471  // Invitation summary & location rows
1472  html += invitationRow( i18n( "What:" ), sSummary );
1473  html += invitationRow( i18n( "Where:" ), sLocation );
1474 
1475  if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
1476  html += invitationRow( i18n( "Start Date:" ), dateToString( todo->dtStart(), false, spec ) );
1477  if ( !todo->allDay() ) {
1478  html += invitationRow( i18n( "Start Time:" ), timeToString( todo->dtStart(), false, spec ) );
1479  }
1480  }
1481  if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
1482  html += invitationRow( i18n( "Due Date:" ), dateToString( todo->dtDue(), false, spec ) );
1483  if ( !todo->allDay() ) {
1484  html += invitationRow( i18n( "Due Time:" ), timeToString( todo->dtDue(), false, spec ) );
1485  }
1486  } else {
1487  html += invitationRow( i18n( "Due Date:" ), i18nc( "no to-do due date", "None" ) );
1488  }
1489 
1490  html += "</table></div>\n";
1491  html += invitationsDetailsIncidence( todo, noHtmlMode );
1492 
1493  return html;
1494 }
1495 
1496 static QString invitationDetailsJournal( Journal *journal, bool noHtmlMode, KDateTime::Spec spec )
1497 {
1498  if ( !journal ) {
1499  return QString();
1500  }
1501 
1502  QString sSummary = i18n( "Summary unspecified" );
1503  QString sDescr = i18n( "Description unspecified" );
1504  if ( ! journal->summary().isEmpty() ) {
1505  sSummary = journal->richSummary();
1506  if ( noHtmlMode ) {
1507  sSummary = cleanHtml( sSummary );
1508  }
1509  }
1510  if ( ! journal->description().isEmpty() ) {
1511  sDescr = journal->richDescription();
1512  if ( noHtmlMode ) {
1513  sDescr = cleanHtml( sDescr );
1514  }
1515  }
1516  QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
1517  html += invitationRow( i18n( "Summary:" ), sSummary );
1518  html += invitationRow( i18n( "Date:" ), dateToString( journal->dtStart(), false, spec ) );
1519  html += invitationRow( i18n( "Description:" ), sDescr );
1520  html += "</table>\n";
1521  html += invitationsDetailsIncidence( journal, noHtmlMode );
1522 
1523  return html;
1524 }
1525 
1526 static QString invitationDetailsFreeBusy( FreeBusy *fb, bool noHtmlMode, KDateTime::Spec spec )
1527 {
1528  Q_UNUSED( noHtmlMode );
1529 
1530  if ( !fb ) {
1531  return QString();
1532  }
1533 
1534  QString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" );
1535  html += invitationRow( i18n( "Person:" ), fb->organizer().fullName() );
1536  html += invitationRow( i18n( "Start date:" ), dateToString( fb->dtStart(), true, spec ) );
1537  html += invitationRow( i18n( "End date:" ), dateToString( fb->dtEnd(), true, spec ) );
1538 
1539  html += "<tr><td colspan=2><hr></td></tr>\n";
1540  html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n";
1541 
1542  QList<Period> periods = fb->busyPeriods();
1543  QList<Period>::iterator it;
1544  for ( it = periods.begin(); it != periods.end(); ++it ) {
1545  Period per = *it;
1546  if ( per.hasDuration() ) {
1547  int dur = per.duration().asSeconds();
1548  QString cont;
1549  if ( dur >= 3600 ) {
1550  cont += i18ncp( "hours part of duration", "1 hour ", "%1 hours ", dur / 3600 );
1551  dur %= 3600;
1552  }
1553  if ( dur >= 60 ) {
1554  cont += i18ncp( "minutes part of duration", "1 minute", "%1 minutes ", dur / 60 );
1555  dur %= 60;
1556  }
1557  if ( dur > 0 ) {
1558  cont += i18ncp( "seconds part of duration", "1 second", "%1 seconds", dur );
1559  }
1560  html += invitationRow(
1561  QString(), i18nc( "startDate for duration", "%1 for %2",
1562  KGlobal::locale()->formatDateTime(
1563  per.start().dateTime(), KLocale::LongDate ), cont ) );
1564  } else {
1565  QString cont;
1566  if ( per.start().date() == per.end().date() ) {
1567  cont = i18nc( "date, fromTime - toTime ", "%1, %2 - %3",
1568  KGlobal::locale()->formatDate( per.start().date() ),
1569  KGlobal::locale()->formatTime( per.start().time() ),
1570  KGlobal::locale()->formatTime( per.end().time() ) );
1571  } else {
1572  cont = i18nc( "fromDateTime - toDateTime", "%1 - %2",
1573  KGlobal::locale()->formatDateTime(
1574  per.start().dateTime(), KLocale::LongDate ),
1575  KGlobal::locale()->formatDateTime(
1576  per.end().dateTime(), KLocale::LongDate ) );
1577  }
1578 
1579  html += invitationRow( QString(), cont );
1580  }
1581  }
1582 
1583  html += "</table>\n";
1584  return html;
1585 }
1586 
1587 static bool replyMeansCounter( Incidence */*incidence*/ )
1588 {
1589  return false;
1604 }
1605 
1606 static QString invitationHeaderEvent( Event *event, Incidence *existingIncidence,
1607  ScheduleMessage *msg, const QString &sender )
1608 {
1609  if ( !msg || !event ) {
1610  return QString();
1611  }
1612 
1613  switch ( msg->method() ) {
1614  case iTIPPublish:
1615  return i18n( "This invitation has been published" );
1616  case iTIPRequest:
1617  if ( existingIncidence && event->revision() > 0 ) {
1618  return i18n( "This invitation has been updated by the organizer %1",
1619  event->organizer().fullName() );
1620  }
1621  if ( iamOrganizer( event ) ) {
1622  return i18n( "I created this invitation" );
1623  } else {
1624  if ( senderIsOrganizer( event, sender ) ) {
1625  if ( !event->organizer().fullName().isEmpty() ) {
1626  return i18n( "You received an invitation from %1",
1627  event->organizer().fullName() );
1628  } else {
1629  return i18n( "You received an invitation" );
1630  }
1631  } else {
1632  if ( !event->organizer().fullName().isEmpty() ) {
1633  return i18n( "You received an invitation from %1 as a representative of %2",
1634  sender, event->organizer().fullName() );
1635  } else {
1636  return i18n( "You received an invitation from %1 as the organizer's representative",
1637  sender );
1638  }
1639  }
1640  }
1641  case iTIPRefresh:
1642  return i18n( "This invitation was refreshed" );
1643  case iTIPCancel:
1644  return i18n( "This invitation has been canceled" );
1645  case iTIPAdd:
1646  return i18n( "Addition to the invitation" );
1647  case iTIPReply:
1648  {
1649  if ( replyMeansCounter( event ) ) {
1650  return i18n( "%1 makes this counter proposal",
1651  firstAttendeeName( event, i18n( "Sender" ) ) );
1652  }
1653 
1654  Attendee::List attendees = event->attendees();
1655  if( attendees.count() == 0 ) {
1656  kDebug() << "No attendees in the iCal reply!";
1657  return QString();
1658  }
1659  if ( attendees.count() != 1 ) {
1660  kDebug() << "Warning: attendeecount in the reply should be 1"
1661  << "but is" << attendees.count();
1662  }
1663  QString attendeeName = firstAttendeeName( event, i18n( "Sender" ) );
1664 
1665  QString delegatorName, dummy;
1666  Attendee *attendee = *attendees.begin();
1667  KPIMUtils::extractEmailAddressAndName( attendee->delegator(), dummy, delegatorName );
1668  if ( delegatorName.isEmpty() ) {
1669  delegatorName = attendee->delegator();
1670  }
1671 
1672  switch( attendee->status() ) {
1673  case Attendee::NeedsAction:
1674  return i18n( "%1 indicates this invitation still needs some action", attendeeName );
1675  case Attendee::Accepted:
1676  if ( event->revision() > 0 ) {
1677  if ( !sender.isEmpty() ) {
1678  return i18n( "This invitation has been updated by attendee %1", sender );
1679  } else {
1680  return i18n( "This invitation has been updated by an attendee" );
1681  }
1682  } else {
1683  if ( delegatorName.isEmpty() ) {
1684  return i18n( "%1 accepts this invitation", attendeeName );
1685  } else {
1686  return i18n( "%1 accepts this invitation on behalf of %2",
1687  attendeeName, delegatorName );
1688  }
1689  }
1690  case Attendee::Tentative:
1691  if ( delegatorName.isEmpty() ) {
1692  return i18n( "%1 tentatively accepts this invitation", attendeeName );
1693  } else {
1694  return i18n( "%1 tentatively accepts this invitation on behalf of %2",
1695  attendeeName, delegatorName );
1696  }
1697  case Attendee::Declined:
1698  if ( delegatorName.isEmpty() ) {
1699  return i18n( "%1 declines this invitation", attendeeName );
1700  } else {
1701  return i18n( "%1 declines this invitation on behalf of %2",
1702  attendeeName, delegatorName );
1703  }
1704  case Attendee::Delegated:
1705  {
1706  QString delegate, dummy;
1707  KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
1708  if ( delegate.isEmpty() ) {
1709  delegate = attendee->delegate();
1710  }
1711  if ( !delegate.isEmpty() ) {
1712  return i18n( "%1 has delegated this invitation to %2", attendeeName, delegate );
1713  } else {
1714  return i18n( "%1 has delegated this invitation", attendeeName );
1715  }
1716  }
1717  case Attendee::Completed:
1718  return i18n( "This invitation is now completed" );
1719  case Attendee::InProcess:
1720  return i18n( "%1 is still processing the invitation", attendeeName );
1721  case Attendee::None:
1722  return i18n( "Unknown response to this invitation" );
1723  }
1724  break;
1725  }
1726  case iTIPCounter:
1727  return i18n( "%1 makes this counter proposal",
1728  firstAttendeeName( event, i18n( "Sender" ) ) );
1729 
1730  case iTIPDeclineCounter:
1731  return i18n( "%1 declines the counter proposal",
1732  firstAttendeeName( event, i18n( "Sender" ) ) );
1733 
1734  case iTIPNoMethod:
1735  return i18n( "Error: Event iTIP message with unknown method" );
1736  }
1737  kError() << "encountered an iTIP method that we do not support";
1738  return QString();
1739 }
1740 
1741 static QString invitationHeaderTodo( Todo *todo, Incidence *existingIncidence,
1742  ScheduleMessage *msg, const QString &sender )
1743 {
1744  if ( !msg || !todo ) {
1745  return QString();
1746  }
1747 
1748  switch ( msg->method() ) {
1749  case iTIPPublish:
1750  return i18n( "This to-do has been published" );
1751  case iTIPRequest:
1752  if ( existingIncidence && todo->revision() > 0 ) {
1753  return i18n( "This to-do has been updated by the organizer %1",
1754  todo->organizer().fullName() );
1755  } else {
1756  if ( iamOrganizer( todo ) ) {
1757  return i18n( "I created this to-do" );
1758  } else {
1759  if ( senderIsOrganizer( todo, sender ) ) {
1760  if ( !todo->organizer().fullName().isEmpty() ) {
1761  return i18n( "You have been assigned this to-do by %1", todo->organizer().fullName() );
1762  } else {
1763  return i18n( "You have been assigned this to-do" );
1764  }
1765  } else {
1766  if ( !todo->organizer().fullName().isEmpty() ) {
1767  return i18n( "You have been assigned this to-do by %1 as a representative of %2",
1768  sender, todo->organizer().fullName() );
1769  } else {
1770  return i18n( "You have been assigned this to-do by %1 as the "
1771  "organizer's representative", sender );
1772  }
1773  }
1774  }
1775  }
1776  case iTIPRefresh:
1777  return i18n( "This to-do was refreshed" );
1778  case iTIPCancel:
1779  return i18n( "This to-do was canceled" );
1780  case iTIPAdd:
1781  return i18n( "Addition to the to-do" );
1782  case iTIPReply:
1783  {
1784  if ( replyMeansCounter( todo ) ) {
1785  return i18n( "%1 makes this counter proposal",
1786  firstAttendeeName( todo, i18n( "Sender" ) ) );
1787  }
1788 
1789  Attendee::List attendees = todo->attendees();
1790  if ( attendees.count() == 0 ) {
1791  kDebug() << "No attendees in the iCal reply!";
1792  return QString();
1793  }
1794  if ( attendees.count() != 1 ) {
1795  kDebug() << "Warning: attendeecount in the reply should be 1"
1796  << "but is" << attendees.count();
1797  }
1798  QString attendeeName = firstAttendeeName( todo, i18n( "Sender" ) );
1799 
1800  QString delegatorName, dummy;
1801  Attendee *attendee = *attendees.begin();
1802  KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegatorName );
1803  if ( delegatorName.isEmpty() ) {
1804  delegatorName = attendee->delegator();
1805  }
1806 
1807  switch( attendee->status() ) {
1808  case Attendee::NeedsAction:
1809  return i18n( "%1 indicates this to-do assignment still needs some action",
1810  attendeeName );
1811  case Attendee::Accepted:
1812  if ( todo->revision() > 0 ) {
1813  if ( !sender.isEmpty() ) {
1814  if ( todo->isCompleted() ) {
1815  return i18n( "This to-do has been completed by assignee %1", sender );
1816  } else {
1817  return i18n( "This to-do has been updated by assignee %1", sender );
1818  }
1819  } else {
1820  if ( todo->isCompleted() ) {
1821  return i18n( "This to-do has been completed by an assignee" );
1822  } else {
1823  return i18n( "This to-do has been updated by an assignee" );
1824  }
1825  }
1826  } else {
1827  if ( delegatorName.isEmpty() ) {
1828  return i18n( "%1 accepts this to-do", attendeeName );
1829  } else {
1830  return i18n( "%1 accepts this to-do on behalf of %2",
1831  attendeeName, delegatorName );
1832  }
1833  }
1834  case Attendee::Tentative:
1835  if ( delegatorName.isEmpty() ) {
1836  return i18n( "%1 tentatively accepts this to-do", attendeeName );
1837  } else {
1838  return i18n( "%1 tentatively accepts this to-do on behalf of %2",
1839  attendeeName, delegatorName );
1840  }
1841  case Attendee::Declined:
1842  if ( delegatorName.isEmpty() ) {
1843  return i18n( "%1 declines this to-do", attendeeName );
1844  } else {
1845  return i18n( "%1 declines this to-do on behalf of %2",
1846  attendeeName, delegatorName );
1847  }
1848  case Attendee::Delegated:
1849  {
1850  QString delegate, dummy;
1851  KPIMUtils::extractEmailAddressAndName( attendee->delegate(), dummy, delegate );
1852  if ( delegate.isEmpty() ) {
1853  delegate = attendee->delegate();
1854  }
1855  if ( !delegate.isEmpty() ) {
1856  return i18n( "%1 has delegated this to-do to %2", attendeeName, delegate );
1857  } else {
1858  return i18n( "%1 has delegated this to-do", attendeeName );
1859  }
1860  }
1861  case Attendee::Completed:
1862  return i18n( "The request for this to-do is now completed" );
1863  case Attendee::InProcess:
1864  return i18n( "%1 is still processing the to-do", attendeeName );
1865  case Attendee::None:
1866  return i18n( "Unknown response to this to-do" );
1867  }
1868  break;
1869  }
1870  case iTIPCounter:
1871  return i18n( "%1 makes this counter proposal",
1872  firstAttendeeName( todo, i18n( "Sender" ) ) );
1873 
1874  case iTIPDeclineCounter:
1875  return i18n( "%1 declines the counter proposal",
1876  firstAttendeeName( todo, i18n( "Sender" ) ) );
1877 
1878  case iTIPNoMethod:
1879  return i18n( "Error: To-do iTIP message with unknown method" );
1880  }
1881  kError() << "encountered an iTIP method that we do not support";
1882  return QString();
1883 }
1884 
1885 static QString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg )
1886 {
1887  if ( !msg || !journal ) {
1888  return QString();
1889  }
1890 
1891  switch ( msg->method() ) {
1892  case iTIPPublish:
1893  return i18n( "This journal has been published" );
1894  case iTIPRequest:
1895  return i18n( "You have been assigned this journal" );
1896  case iTIPRefresh:
1897  return i18n( "This journal was refreshed" );
1898  case iTIPCancel:
1899  return i18n( "This journal was canceled" );
1900  case iTIPAdd:
1901  return i18n( "Addition to the journal" );
1902  case iTIPReply:
1903  {
1904  if ( replyMeansCounter( journal ) ) {
1905  return i18n( "Sender makes this counter proposal" );
1906  }
1907 
1908  Attendee::List attendees = journal->attendees();
1909  if ( attendees.count() == 0 ) {
1910  kDebug() << "No attendees in the iCal reply!";
1911  return QString();
1912  }
1913  if( attendees.count() != 1 ) {
1914  kDebug() << "Warning: attendeecount in the reply should be 1 "
1915  << "but is " << attendees.count();
1916  }
1917  Attendee *attendee = *attendees.begin();
1918 
1919  switch( attendee->status() ) {
1920  case Attendee::NeedsAction:
1921  return i18n( "Sender indicates this journal assignment still needs some action" );
1922  case Attendee::Accepted:
1923  return i18n( "Sender accepts this journal" );
1924  case Attendee::Tentative:
1925  return i18n( "Sender tentatively accepts this journal" );
1926  case Attendee::Declined:
1927  return i18n( "Sender declines this journal" );
1928  case Attendee::Delegated:
1929  return i18n( "Sender has delegated this request for the journal" );
1930  case Attendee::Completed:
1931  return i18n( "The request for this journal is now completed" );
1932  case Attendee::InProcess:
1933  return i18n( "Sender is still processing the invitation" );
1934  case Attendee::None:
1935  return i18n( "Unknown response to this journal" );
1936  }
1937  break;
1938  }
1939  case iTIPCounter:
1940  return i18n( "Sender makes this counter proposal" );
1941  case iTIPDeclineCounter:
1942  return i18n( "Sender declines the counter proposal" );
1943  case iTIPNoMethod:
1944  return i18n( "Error: Journal iTIP message with unknown method" );
1945  }
1946  kError() << "encountered an iTIP method that we do not support";
1947  return QString();
1948 }
1949 
1950 static QString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg )
1951 {
1952  if ( !msg || !fb ) {
1953  return QString();
1954  }
1955 
1956  switch ( msg->method() ) {
1957  case iTIPPublish:
1958  return i18n( "This free/busy list has been published" );
1959  case iTIPRequest:
1960  return i18n( "The free/busy list has been requested" );
1961  case iTIPRefresh:
1962  return i18n( "This free/busy list was refreshed" );
1963  case iTIPCancel:
1964  return i18n( "This free/busy list was canceled" );
1965  case iTIPAdd:
1966  return i18n( "Addition to the free/busy list" );
1967  case iTIPReply:
1968  return i18n( "Reply to the free/busy list" );
1969  case iTIPCounter:
1970  return i18n( "Sender makes this counter proposal" );
1971  case iTIPDeclineCounter:
1972  return i18n( "Sender declines the counter proposal" );
1973  case iTIPNoMethod:
1974  return i18n( "Error: Free/Busy iTIP message with unknown method" );
1975  }
1976  kError() << "encountered an iTIP method that we do not support";
1977  return QString();
1978 }
1979 //@endcond
1980 
1981 static QString invitationAttendees( Incidence *incidence )
1982 {
1983  QString tmpStr;
1984  if ( !incidence ) {
1985  return tmpStr;
1986  }
1987 
1988  tmpStr += i18n( "Invitation List" );
1989 
1990  int count=0;
1991  Attendee::List attendees = incidence->attendees();
1992  if ( !attendees.isEmpty() ) {
1993 
1994  Attendee::List::ConstIterator it;
1995  for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
1996  Attendee *a = *it;
1997  if ( !iamAttendee( a ) ) {
1998  count++;
1999  if ( count == 1 ) {
2000  tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\">";
2001  }
2002  tmpStr += "<tr>";
2003  tmpStr += "<td>";
2004  tmpStr += invitationPerson( a->email(), a->name(), QString() );
2005  if ( !a->delegator().isEmpty() ) {
2006  tmpStr += i18n( " (delegated by %1)", a->delegator() );
2007  }
2008  if ( !a->delegate().isEmpty() ) {
2009  tmpStr += i18n( " (delegated to %1)", a->delegate() );
2010  }
2011  tmpStr += "</td>";
2012  tmpStr += "<td>" + a->statusStr() + "</td>";
2013  tmpStr += "</tr>";
2014  }
2015  }
2016  }
2017  if ( count ) {
2018  tmpStr += "</table>";
2019  } else {
2020  tmpStr += "<i> " + i18nc( "no attendees", "None" ) + "</i>";
2021  }
2022 
2023  return tmpStr;
2024 }
2025 
2026 static QString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence )
2027 {
2028  QString tmpStr;
2029  if ( !incidence ) {
2030  return tmpStr;
2031  }
2032 
2033  Attachment::List attachments = incidence->attachments();
2034  if ( !attachments.isEmpty() ) {
2035  tmpStr += i18n( "Attached Documents:" ) + "<ol>";
2036 
2037  Attachment::List::ConstIterator it;
2038  for ( it = attachments.constBegin(); it != attachments.constEnd(); ++it ) {
2039  Attachment *a = *it;
2040  tmpStr += "<li>";
2041  // Attachment icon
2042  KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() );
2043  const QString iconStr = ( mimeType ?
2044  mimeType->iconName( a->uri() ) :
2045  QString( "application-octet-stream" ) );
2046  const QString iconPath = KIconLoader::global()->iconPath( iconStr, KIconLoader::Small );
2047  if ( !iconPath.isEmpty() ) {
2048  tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">";
2049  }
2050  tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() );
2051  tmpStr += "</li>";
2052  }
2053  tmpStr += "</ol>";
2054  }
2055 
2056  return tmpStr;
2057 }
2058 
2059 //@cond PRIVATE
2060 class KCal::IncidenceFormatter::ScheduleMessageVisitor
2061  : public IncidenceBase::Visitor
2062 {
2063  public:
2064  ScheduleMessageVisitor() : mExistingIncidence( 0 ), mMessage( 0 ) { mResult = ""; }
2065  bool act( IncidenceBase *incidence, Incidence *existingIncidence,
2066  ScheduleMessage *msg, const QString &sender )
2067  {
2068  mExistingIncidence = existingIncidence;
2069  mMessage = msg;
2070  mSender = sender;
2071  return incidence->accept( *this );
2072  }
2073  QString result() const { return mResult; }
2074 
2075  protected:
2076  QString mResult;
2077  Incidence *mExistingIncidence;
2078  ScheduleMessage *mMessage;
2079  QString mSender;
2080 };
2081 
2082 class KCal::IncidenceFormatter::InvitationHeaderVisitor :
2083  public IncidenceFormatter::ScheduleMessageVisitor
2084 {
2085  protected:
2086  bool visit( Event *event )
2087  {
2088  mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender );
2089  return !mResult.isEmpty();
2090  }
2091  bool visit( Todo *todo )
2092  {
2093  mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender );
2094  return !mResult.isEmpty();
2095  }
2096  bool visit( Journal *journal )
2097  {
2098  mResult = invitationHeaderJournal( journal, mMessage );
2099  return !mResult.isEmpty();
2100  }
2101  bool visit( FreeBusy *fb )
2102  {
2103  mResult = invitationHeaderFreeBusy( fb, mMessage );
2104  return !mResult.isEmpty();
2105  }
2106 };
2107 
2108 class KCal::IncidenceFormatter::InvitationBodyVisitor
2109  : public IncidenceFormatter::ScheduleMessageVisitor
2110 {
2111  public:
2112  InvitationBodyVisitor( bool noHtmlMode, KDateTime::Spec spec )
2113  : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ), mSpec( spec ) {}
2114 
2115  protected:
2116  bool visit( Event *event )
2117  {
2118  mResult = invitationDetailsEvent( event, mNoHtmlMode, mSpec );
2119  return !mResult.isEmpty();
2120  }
2121  bool visit( Todo *todo )
2122  {
2123  mResult = invitationDetailsTodo( todo, mNoHtmlMode, mSpec );
2124  return !mResult.isEmpty();
2125  }
2126  bool visit( Journal *journal )
2127  {
2128  mResult = invitationDetailsJournal( journal, mNoHtmlMode, mSpec );
2129  return !mResult.isEmpty();
2130  }
2131  bool visit( FreeBusy *fb )
2132  {
2133  mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode, mSpec );
2134  return !mResult.isEmpty();
2135  }
2136 
2137  private:
2138  bool mNoHtmlMode;
2139  KDateTime::Spec mSpec;
2140 };
2141 //@endcond
2142 
2143 QString InvitationFormatterHelper::generateLinkURL( const QString &id )
2144 {
2145  return id;
2146 }
2147 
2148 //@cond PRIVATE
2149 class IncidenceFormatter::IncidenceCompareVisitor
2150  : public IncidenceBase::Visitor
2151 {
2152  public:
2153  IncidenceCompareVisitor() : mExistingIncidence( 0 ) {}
2154  bool act( IncidenceBase *incidence, Incidence *existingIncidence, iTIPMethod method )
2155  {
2156  if ( !existingIncidence ) {
2157  return false;
2158  }
2159  Incidence *inc = dynamic_cast<Incidence *>( incidence );
2160  if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() ) {
2161  return false;
2162  }
2163  mExistingIncidence = existingIncidence;
2164  mMethod = method;
2165  return incidence->accept( *this );
2166  }
2167 
2168  QString result() const
2169  {
2170  if ( mChanges.isEmpty() ) {
2171  return QString();
2172  }
2173  QString html = "<div align=\"left\"><ul><li>";
2174  html += mChanges.join( "</li><li>" );
2175  html += "</li><ul></div>";
2176  return html;
2177  }
2178 
2179  protected:
2180  bool visit( Event *event )
2181  {
2182  compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) );
2183  compareIncidences( event, mExistingIncidence, mMethod );
2184  return !mChanges.isEmpty();
2185  }
2186  bool visit( Todo *todo )
2187  {
2188  compareTodos( todo, dynamic_cast<Todo*>( mExistingIncidence ) );
2189  compareIncidences( todo, mExistingIncidence, mMethod );
2190  return !mChanges.isEmpty();
2191  }
2192  bool visit( Journal *journal )
2193  {
2194  compareIncidences( journal, mExistingIncidence, mMethod );
2195  return !mChanges.isEmpty();
2196  }
2197  bool visit( FreeBusy *fb )
2198  {
2199  Q_UNUSED( fb );
2200  return !mChanges.isEmpty();
2201  }
2202 
2203  private:
2204  void compareEvents( Event *newEvent, Event *oldEvent )
2205  {
2206  if ( !oldEvent || !newEvent ) {
2207  return;
2208  }
2209  if ( oldEvent->dtStart() != newEvent->dtStart() ||
2210  oldEvent->allDay() != newEvent->allDay() ) {
2211  mChanges += i18n( "The invitation starting time has been changed from %1 to %2",
2212  eventStartTimeStr( oldEvent ), eventStartTimeStr( newEvent ) );
2213  }
2214  if ( oldEvent->dtEnd() != newEvent->dtEnd() ||
2215  oldEvent->allDay() != newEvent->allDay() ) {
2216  mChanges += i18n( "The invitation ending time has been changed from %1 to %2",
2217  eventEndTimeStr( oldEvent ), eventEndTimeStr( newEvent ) );
2218  }
2219  }
2220 
2221  void compareTodos( Todo *newTodo, Todo *oldTodo )
2222  {
2223  if ( !oldTodo || !newTodo ) {
2224  return;
2225  }
2226 
2227  if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) {
2228  mChanges += i18n( "The to-do has been completed" );
2229  }
2230  if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) {
2231  mChanges += i18n( "The to-do is no longer completed" );
2232  }
2233  if ( oldTodo->percentComplete() != newTodo->percentComplete() ) {
2234  const QString oldPer = i18n( "%1%", oldTodo->percentComplete() );
2235  const QString newPer = i18n( "%1%", newTodo->percentComplete() );
2236  mChanges += i18n( "The task completed percentage has changed from %1 to %2",
2237  oldPer, newPer );
2238  }
2239 
2240  if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) {
2241  mChanges += i18n( "A to-do starting time has been added" );
2242  }
2243  if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) {
2244  mChanges += i18n( "The to-do starting time has been removed" );
2245  }
2246  if ( oldTodo->hasStartDate() && newTodo->hasStartDate() &&
2247  oldTodo->dtStart() != newTodo->dtStart() ) {
2248  mChanges += i18n( "The to-do starting time has been changed from %1 to %2",
2249  dateTimeToString( oldTodo->dtStart(), oldTodo->allDay(), false ),
2250  dateTimeToString( newTodo->dtStart(), newTodo->allDay(), false ) );
2251  }
2252 
2253  if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) {
2254  mChanges += i18n( "A to-do due time has been added" );
2255  }
2256  if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) {
2257  mChanges += i18n( "The to-do due time has been removed" );
2258  }
2259  if ( oldTodo->hasDueDate() && newTodo->hasDueDate() &&
2260  oldTodo->dtDue() != newTodo->dtDue() ) {
2261  mChanges += i18n( "The to-do due time has been changed from %1 to %2",
2262  dateTimeToString( oldTodo->dtDue(), oldTodo->allDay(), false ),
2263  dateTimeToString( newTodo->dtDue(), newTodo->allDay(), false ) );
2264  }
2265  }
2266 
2267  void compareIncidences( Incidence *newInc, Incidence *oldInc, iTIPMethod method )
2268  {
2269  if ( !oldInc || !newInc ) {
2270  return;
2271  }
2272 
2273  if ( oldInc->summary() != newInc->summary() ) {
2274  mChanges += i18n( "The summary has been changed to: \"%1\"",
2275  newInc->richSummary() );
2276  }
2277 
2278  if ( oldInc->location() != newInc->location() ) {
2279  mChanges += i18nc( "event/todo location", "The location has been changed to: \"%1\"",
2280  newInc->richLocation() );
2281  }
2282 
2283  if ( oldInc->description() != newInc->description() ) {
2284  mChanges += i18n( "The description has been changed to: \"%1\"",
2285  newInc->richDescription() );
2286  }
2287 
2288  Attendee::List oldAttendees = oldInc->attendees();
2289  Attendee::List newAttendees = newInc->attendees();
2290  for ( Attendee::List::ConstIterator it = newAttendees.constBegin();
2291  it != newAttendees.constEnd(); ++it ) {
2292  Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() );
2293  if ( !oldAtt ) {
2294  mChanges += i18n( "Attendee %1 has been added", (*it)->fullName() );
2295  } else {
2296  if ( oldAtt->status() != (*it)->status() ) {
2297  mChanges += i18n( "The status of attendee %1 has been changed to: %2",
2298  (*it)->fullName(), (*it)->statusStr() );
2299  }
2300  }
2301  }
2302 
2303  if ( method == iTIPRequest ) {
2304  for ( Attendee::List::ConstIterator it = oldAttendees.constBegin();
2305  it != oldAttendees.constEnd(); ++it ) {
2306  if ( (*it)->email() != oldInc->organizer().email() ) {
2307  Attendee *newAtt = newInc->attendeeByMail( (*it)->email() );
2308  if ( !newAtt ) {
2309  mChanges += i18n( "Attendee %1 has been removed", (*it)->fullName() );
2310  }
2311  }
2312  }
2313  }
2314  }
2315 
2316  private:
2317  Incidence *mExistingIncidence;
2318  iTIPMethod mMethod;
2319  QStringList mChanges;
2320 };
2321 //@endcond
2322 
2323 QString InvitationFormatterHelper::makeLink( const QString &id, const QString &text )
2324 {
2325  if ( !id.startsWith( QLatin1String( "ATTACH:" ) ) ) {
2326  QString res = QString( "<a href=\"%1\"><b>%2</b></a>" ).
2327  arg( generateLinkURL( id ), text );
2328  return res;
2329  } else {
2330  // draw the attachment links in non-bold face
2331  QString res = QString( "<a href=\"%1\">%2</a>" ).
2332  arg( generateLinkURL( id ), text );
2333  return res;
2334  }
2335 }
2336 
2337 // Check if the given incidence is likely one that we own instead one from
2338 // a shared calendar (Kolab-specific)
2339 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence )
2340 {
2341 #ifndef KDEPIM_NO_KRESOURCES
2342  CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar );
2343  if ( !cal || !incidence ) {
2344  return true;
2345  }
2346  ResourceCalendar *res = cal->resource( incidence );
2347  if ( !res ) {
2348  return true;
2349  }
2350  const QString subRes = res->subresourceIdentifier( incidence );
2351  if ( !subRes.contains( "/.INBOX.directory/" ) ) {
2352  return false;
2353  }
2354 #endif
2355  return true;
2356 }
2357 
2358 // The open & close table cell tags for the invitation buttons
2359 static QString tdOpen = "<td style=\"border-width:2px;border-style:outset\">";
2360 static QString tdClose = "</td>";
2361 
2362 static QString responseButtons( Incidence *inc, bool rsvpReq, bool rsvpRec,
2363  InvitationFormatterHelper *helper )
2364 {
2365  QString html;
2366  if ( !helper ) {
2367  return html;
2368  }
2369 
2370  if ( !rsvpReq && ( inc && inc->revision() == 0 ) ) {
2371  // Record only
2372  html += tdOpen;
2373  html += helper->makeLink( "record", i18n( "[Record]" ) );
2374  html += tdClose;
2375 
2376  // Move to trash
2377  html += tdOpen;
2378  html += helper->makeLink( "delete", i18n( "[Move to Trash]" ) );
2379  html += tdClose;
2380 
2381  } else {
2382 
2383  // Accept
2384  html += tdOpen;
2385  html += helper->makeLink( "accept", i18nc( "accept invitation", "Accept" ) );
2386  html += tdClose;
2387 
2388  // Tentative
2389  html += tdOpen;
2390  html += helper->makeLink( "accept_conditionally",
2391  i18nc( "Accept invitation conditionally", "Accept cond." ) );
2392  html += tdClose;
2393 
2394  // Counter proposal
2395  html += tdOpen;
2396  html += helper->makeLink( "counter",
2397  i18nc( "invitation counter proposal", "Counter proposal" ) );
2398  html += tdClose;
2399 
2400  // Decline
2401  html += tdOpen;
2402  html += helper->makeLink( "decline",
2403  i18nc( "decline invitation", "Decline" ) );
2404  html += tdClose;
2405  }
2406 
2407  if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) {
2408  // Delegate
2409  html += tdOpen;
2410  html += helper->makeLink( "delegate",
2411  i18nc( "delegate inviation to another", "Delegate" ) );
2412  html += tdClose;
2413 
2414  // Forward
2415  html += tdOpen;
2416  html += helper->makeLink( "forward",
2417  i18nc( "forward request to another", "Forward" ) );
2418  html += tdClose;
2419 
2420  // Check calendar
2421  if ( inc && inc->type() == "Event" ) {
2422  html += tdOpen;
2423  html += helper->makeLink( "check_calendar",
2424  i18nc( "look for scheduling conflicts", "Check my calendar" ) );
2425  html += tdClose;
2426  }
2427  }
2428  return html;
2429 }
2430 
2431 static QString counterButtons( Incidence *incidence,
2432  InvitationFormatterHelper *helper )
2433 {
2434  QString html;
2435  if ( !helper ) {
2436  return html;
2437  }
2438 
2439  // Accept proposal
2440  html += tdOpen;
2441  html += helper->makeLink( "accept_counter", i18n( "[Accept]" ) );
2442  html += tdClose;
2443 
2444  // Decline proposal
2445  html += tdOpen;
2446  html += helper->makeLink( "decline_counter", i18n( "[Decline]" ) );
2447  html += tdClose;
2448 
2449  // Check calendar
2450  if ( incidence && incidence->type() == "Event" ) {
2451  html += tdOpen;
2452  html += helper->makeLink( "check_calendar", i18n( "[Check my calendar] " ) );
2453  html += tdClose;
2454  }
2455  return html;
2456 }
2457 
2458 Calendar *InvitationFormatterHelper::calendar() const
2459 {
2460  return 0;
2461 }
2462 
2463 static QString formatICalInvitationHelper( QString invitation,
2464  Calendar *mCalendar,
2465  InvitationFormatterHelper *helper,
2466  bool noHtmlMode,
2467  KDateTime::Spec spec,
2468  const QString &sender )
2469 {
2470  if ( invitation.isEmpty() ) {
2471  return QString();
2472  }
2473 
2474  ICalFormat format;
2475  // parseScheduleMessage takes the tz from the calendar,
2476  // no need to set it manually here for the format!
2477  ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation );
2478 
2479  if( !msg ) {
2480  kDebug() << "Failed to parse the scheduling message";
2481  Q_ASSERT( format.exception() );
2482  kDebug() << format.exception()->message();
2483  return QString();
2484  }
2485 
2486  IncidenceBase *incBase = msg->event();
2487  incBase->shiftTimes( mCalendar->timeSpec(), KDateTime::Spec::LocalZone() );
2488 
2489  // Determine if this incidence is in my calendar (and owned by me)
2490  Incidence *existingIncidence = 0;
2491  if ( incBase && helper->calendar() ) {
2492  existingIncidence = helper->calendar()->incidence( incBase->uid() );
2493  if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) {
2494  existingIncidence = 0;
2495  }
2496  if ( !existingIncidence ) {
2497  const Incidence::List list = helper->calendar()->incidences();
2498  for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
2499  if ( (*it)->schedulingID() == incBase->uid() &&
2500  incidenceOwnedByMe( helper->calendar(), *it ) ) {
2501  existingIncidence = *it;
2502  break;
2503  }
2504  }
2505  }
2506  }
2507 
2508  // First make the text of the message
2509  QString html;
2510  html += "<div align=\"center\" style=\"border:solid 1px;\">";
2511 
2512  IncidenceFormatter::InvitationHeaderVisitor headerVisitor;
2513  // The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled
2514  if ( !headerVisitor.act( incBase, existingIncidence, msg, sender ) ) {
2515  return QString();
2516  }
2517  html += htmlAddTag( "h3", headerVisitor.result() );
2518 
2519  IncidenceFormatter::InvitationBodyVisitor bodyVisitor( noHtmlMode, spec );
2520  if ( !bodyVisitor.act( incBase, existingIncidence, msg, sender ) ) {
2521  return QString();
2522  }
2523  html += bodyVisitor.result();
2524 
2525  if ( msg->method() == iTIPRequest ) {
2526  IncidenceFormatter::IncidenceCompareVisitor compareVisitor;
2527  if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
2528  html += "<p align=\"left\">";
2529  html += i18n( "The following changes have been made by the organizer:" );
2530  html += "</p>";
2531  html += compareVisitor.result();
2532  }
2533  }
2534  if ( msg->method() == iTIPReply ) {
2535  IncidenceCompareVisitor compareVisitor;
2536  if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) {
2537  html += "<p align=\"left\">";
2538  if ( !sender.isEmpty() ) {
2539  html += i18n( "The following changes have been made by %1:", sender );
2540  } else {
2541  html += i18n( "The following changes have been made by an attendee:" );
2542  }
2543  html += "</p>";
2544  html += compareVisitor.result();
2545  }
2546  }
2547 
2548  Incidence *inc = dynamic_cast<Incidence*>( incBase );
2549 
2550  // determine if I am the organizer for this invitation
2551  bool myInc = iamOrganizer( inc );
2552 
2553  // determine if the invitation response has already been recorded
2554  bool rsvpRec = false;
2555  Attendee *ea = 0;
2556  if ( !myInc ) {
2557  Incidence *rsvpIncidence = existingIncidence;
2558  if ( !rsvpIncidence && inc && inc->revision() > 0 ) {
2559  rsvpIncidence = inc;
2560  }
2561  if ( rsvpIncidence ) {
2562  ea = findMyAttendee( rsvpIncidence );
2563  }
2564  if ( ea &&
2565  ( ea->status() == Attendee::Accepted ||
2566  ea->status() == Attendee::Declined ||
2567  ea->status() == Attendee::Tentative ) ) {
2568  rsvpRec = true;
2569  }
2570  }
2571 
2572  // determine invitation role
2573  QString role;
2574  bool isDelegated = false;
2575  Attendee *a = findMyAttendee( inc );
2576  if ( !a && inc ) {
2577  if ( !inc->attendees().isEmpty() ) {
2578  a = inc->attendees().first();
2579  }
2580  }
2581  if ( a ) {
2582  isDelegated = ( a->status() == Attendee::Delegated );
2583  role = Attendee::roleName( a->role() );
2584  }
2585 
2586  // Print if RSVP needed, not-needed, or response already recorded
2587  bool rsvpReq = rsvpRequested( inc );
2588  if ( !myInc && a ) {
2589  html += "<br/>";
2590  html += "<i><u>";
2591  if ( rsvpRec && inc ) {
2592  if ( inc->revision() == 0 ) {
2593  html += i18n( "Your <b>%1</b> response has already been recorded", ea->statusStr() );
2594  } else {
2595  html += i18n( "Your status for this invitation is <b>%1</b>", ea->statusStr() );
2596  }
2597  rsvpReq = false;
2598  } else if ( msg->method() == iTIPCancel ) {
2599  html += i18n( "This invitation was declined" );
2600  } else if ( msg->method() == iTIPAdd ) {
2601  html += i18n( "This invitation was accepted" );
2602  } else {
2603  if ( !isDelegated ) {
2604  html += rsvpRequestedStr( rsvpReq, role );
2605  } else {
2606  html += i18n( "Awaiting delegation response" );
2607  }
2608  }
2609  html += "</u></i>";
2610  }
2611 
2612  // Print if the organizer gave you a preset status
2613  if ( !myInc ) {
2614  if ( inc && inc->revision() == 0 ) {
2615  QString statStr = myStatusStr( inc );
2616  if ( !statStr.isEmpty() ) {
2617  html += "<br/>";
2618  html += "<i>";
2619  html += statStr;
2620  html += "</i>";
2621  }
2622  }
2623  }
2624 
2625  // Add groupware links
2626 
2627  html += "<p>";
2628  html += "<table border=\"0\" align=\"center\" cellspacing=\"4\"><tr>";
2629 
2630  switch ( msg->method() ) {
2631  case iTIPPublish:
2632  case iTIPRequest:
2633  case iTIPRefresh:
2634  case iTIPAdd:
2635  {
2636  if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) {
2637  if ( inc->type() == "Todo" ) {
2638  html += helper->makeLink( "reply", i18n( "[Record invitation in my to-do list]" ) );
2639  } else {
2640  html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) );
2641  }
2642  }
2643 
2644  if ( !myInc && a ) {
2645  html += responseButtons( inc, rsvpReq, rsvpRec, helper );
2646  }
2647  break;
2648  }
2649 
2650  case iTIPCancel:
2651  // Remove invitation
2652  if ( inc ) {
2653  html += tdOpen;
2654  if ( inc->type() == "Todo" ) {
2655  html += helper->makeLink( "cancel",
2656  i18n( "Remove invitation from my to-do list" ) );
2657  } else {
2658  html += helper->makeLink( "cancel",
2659  i18n( "Remove invitation from my calendar" ) );
2660  }
2661  html += tdClose;
2662  }
2663  break;
2664 
2665  case iTIPReply:
2666  {
2667  // Record invitation response
2668  Attendee *a = 0;
2669  Attendee *ea = 0;
2670  if ( inc ) {
2671  // First, determine if this reply is really a counter in disguise.
2672  if ( replyMeansCounter( inc ) ) {
2673  html += "<tr>" + counterButtons( inc, helper ) + "</tr>";
2674  break;
2675  }
2676 
2677  // Next, maybe this is a declined reply that was delegated from me?
2678  // find first attendee who is delegated-from me
2679  // look a their PARTSTAT response, if the response is declined,
2680  // then we need to start over which means putting all the action
2681  // buttons and NOT putting on the [Record response..] button
2682  a = findDelegatedFromMyAttendee( inc );
2683  if ( a ) {
2684  if ( a->status() != Attendee::Accepted ||
2685  a->status() != Attendee::Tentative ) {
2686  html += responseButtons( inc, rsvpReq, rsvpRec, helper );
2687  break;
2688  }
2689  }
2690 
2691  // Finally, simply allow a Record of the reply
2692  if ( !inc->attendees().isEmpty() ) {
2693  a = inc->attendees().first();
2694  }
2695  if ( a && helper->calendar() ) {
2696  ea = findAttendee( existingIncidence, a->email() );
2697  }
2698  }
2699  if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) {
2700  html += tdOpen;
2701  html += htmlAddTag( "i", i18n( "The response has already been recorded" ) );
2702  html += tdClose;
2703  } else {
2704  if ( inc ) {
2705  if ( inc->type() == "Todo" ) {
2706  html += helper->makeLink( "reply", i18n( "[Record response in my to-do list]" ) );
2707  } else {
2708  html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) );
2709  }
2710  }
2711  }
2712  break;
2713  }
2714 
2715  case iTIPCounter:
2716  // Counter proposal
2717  html += counterButtons( inc, helper );
2718  break;
2719 
2720  case iTIPDeclineCounter:
2721  case iTIPNoMethod:
2722  break;
2723  }
2724 
2725  // close the groupware table
2726  html += "</tr></table>";
2727 
2728  // Add the attendee list if I am the organizer
2729  if ( myInc && helper->calendar() ) {
2730  html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) );
2731  }
2732 
2733  // close the top-level
2734  html += "</div>";
2735 
2736  // Add the attachment list
2737  html += invitationAttachments( helper, inc );
2738 
2739  return html;
2740 }
2741 //@endcond
2742 
2743 QString IncidenceFormatter::formatICalInvitation( QString invitation,
2744  Calendar *calendar,
2745  InvitationFormatterHelper *helper )
2746 {
2747  return formatICalInvitationHelper( invitation, calendar, helper, false,
2748  KSystemTimeZones::local(), QString() );
2749 }
2750 
2751 QString IncidenceFormatter::formatICalInvitationNoHtml( QString invitation,
2752  Calendar *calendar,
2753  InvitationFormatterHelper *helper )
2754 {
2755  return formatICalInvitationHelper( invitation, calendar, helper, true,
2756  KSystemTimeZones::local(), QString() );
2757 }
2758 
2759 QString IncidenceFormatter::formatICalInvitationNoHtml( const QString &invitation,
2760  Calendar *calendar,
2761  InvitationFormatterHelper *helper,
2762  const QString &sender )
2763 {
2764  return formatICalInvitationHelper( invitation, calendar, helper, true,
2765  KSystemTimeZones::local(), sender );
2766 }
2767 
2768 /*******************************************************************
2769  * Helper functions for the Incidence tooltips
2770  *******************************************************************/
2771 
2772 //@cond PRIVATE
2773 class KCal::IncidenceFormatter::ToolTipVisitor
2774  : public IncidenceBase::Visitor
2775 {
2776  public:
2777  ToolTipVisitor()
2778  : mRichText( true ), mSpec( KDateTime::Spec() ), mResult( "" ) {}
2779 
2780  bool act( Calendar *calendar, IncidenceBase *incidence,
2781  const QDate &date=QDate(), bool richText=true,
2782  KDateTime::Spec spec=KDateTime::Spec() )
2783  {
2784  mCalendar = calendar;
2785  mLocation.clear();
2786  mDate = date;
2787  mRichText = richText;
2788  mSpec = spec;
2789  mResult = "";
2790  return incidence ? incidence->accept( *this ) : false;
2791  }
2792 
2793  bool act( const QString &location, IncidenceBase *incidence,
2794  const QDate &date=QDate(), bool richText=true,
2795  KDateTime::Spec spec=KDateTime::Spec() )
2796  {
2797  mCalendar = 0;
2798  mLocation = location;
2799  mDate = date;
2800  mRichText = richText;
2801  mSpec = spec;
2802  mResult = "";
2803  return incidence ? incidence->accept( *this ) : false;
2804  }
2805 
2806  QString result() const { return mResult; }
2807 
2808  protected:
2809  bool visit( Event *event );
2810  bool visit( Todo *todo );
2811  bool visit( Journal *journal );
2812  bool visit( FreeBusy *fb );
2813 
2814  QString dateRangeText( Event *event, const QDate &date );
2815  QString dateRangeText( Todo *todo, const QDate &date );
2816  QString dateRangeText( Journal *journal );
2817  QString dateRangeText( FreeBusy *fb );
2818 
2819  QString generateToolTip( Incidence *incidence, QString dtRangeText );
2820 
2821  protected:
2822  Calendar *mCalendar;
2823  QString mLocation;
2824  QDate mDate;
2825  bool mRichText;
2826  KDateTime::Spec mSpec;
2827  QString mResult;
2828 };
2829 
2830 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const QDate &date )
2831 {
2832  //FIXME: support mRichText==false
2833  QString ret;
2834  QString tmp;
2835 
2836  KDateTime startDt = event->dtStart();
2837  KDateTime endDt = event->dtEnd();
2838  if ( event->recurs() ) {
2839  if ( date.isValid() ) {
2840  KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
2841  int diffDays = startDt.daysTo( kdt );
2842  kdt = kdt.addSecs( -1 );
2843  startDt.setDate( event->recurrence()->getNextDateTime( kdt ).date() );
2844  if ( event->hasEndDate() ) {
2845  endDt = endDt.addDays( diffDays );
2846  if ( startDt > endDt ) {
2847  startDt.setDate( event->recurrence()->getPreviousDateTime( kdt ).date() );
2848  endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) );
2849  }
2850  }
2851  }
2852  }
2853 
2854  if ( event->isMultiDay() ) {
2855  tmp = dateToString( startDt, true, mSpec );
2856  ret += "<br>" + i18nc( "Event start", "<i>From:</i> %1", tmp );
2857 
2858  tmp = dateToString( endDt, true, mSpec );
2859  ret += "<br>" + i18nc( "Event end","<i>To:</i> %1", tmp );
2860 
2861  } else {
2862 
2863  ret += "<br>" +
2864  i18n( "<i>Date:</i> %1", dateToString( startDt, false, mSpec ) );
2865  if ( !event->allDay() ) {
2866  const QString dtStartTime = timeToString( startDt, true, mSpec );
2867  const QString dtEndTime = timeToString( endDt, true, mSpec );
2868  if ( dtStartTime == dtEndTime ) {
2869  // to prevent 'Time: 17:00 - 17:00'
2870  tmp = "<br>" +
2871  i18nc( "time for event", "<i>Time:</i> %1",
2872  dtStartTime );
2873  } else {
2874  tmp = "<br>" +
2875  i18nc( "time range for event",
2876  "<i>Time:</i> %1 - %2",
2877  dtStartTime, dtEndTime );
2878  }
2879  ret += tmp;
2880  }
2881  }
2882  return ret.replace( ' ', "&nbsp;" );
2883 }
2884 
2885 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const QDate &date )
2886 {
2887  //FIXME: support mRichText==false
2888  QString ret;
2889  if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
2890  KDateTime startDt = todo->dtStart();
2891  if ( todo->recurs() ) {
2892  if ( date.isValid() ) {
2893  startDt.setDate( date );
2894  }
2895  }
2896  ret += "<br>" +
2897  i18n( "<i>Start:</i> %1", dateToString( startDt, false, mSpec ) );
2898  }
2899 
2900  if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
2901  KDateTime dueDt = todo->dtDue();
2902  if ( todo->recurs() ) {
2903  if ( date.isValid() ) {
2904  KDateTime kdt( date, QTime( 0, 0, 0 ), KSystemTimeZones::local() );
2905  kdt = kdt.addSecs( -1 );
2906  dueDt.setDate( todo->recurrence()->getNextDateTime( kdt ).date() );
2907  }
2908  }
2909  ret += "<br>" +
2910  i18n( "<i>Due:</i> %1",
2911  dateTimeToString( dueDt, todo->allDay(), false, mSpec ) );
2912  }
2913 
2914  // Print priority and completed info here, for lack of a better place
2915 
2916  if ( todo->priority() > 0 ) {
2917  ret += "<br>";
2918  ret += "<i>" + i18n( "Priority:" ) + "</i>" + "&nbsp;";
2919  ret += QString::number( todo->priority() );
2920  }
2921 
2922  ret += "<br>";
2923  if ( todo->isCompleted() ) {
2924  ret += "<i>" + i18nc( "Completed: date", "Completed:" ) + "</i>" + "&nbsp;";
2925  ret += todo->completedStr().replace( ' ', "&nbsp;" );
2926  } else {
2927  ret += "<i>" + i18n( "Percent Done:" ) + "</i>" + "&nbsp;";
2928  ret += i18n( "%1%", todo->percentComplete() );
2929  }
2930 
2931  return ret.replace( ' ', "&nbsp;" );
2932 }
2933 
2934 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal *journal )
2935 {
2936  //FIXME: support mRichText==false
2937  QString ret;
2938  if ( journal->dtStart().isValid() ) {
2939  ret += "<br>" +
2940  i18n( "<i>Date:</i> %1", dateToString( journal->dtStart(), false, mSpec ) );
2941  }
2942  return ret.replace( ' ', "&nbsp;" );
2943 }
2944 
2945 QString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb )
2946 {
2947  //FIXME: support mRichText==false
2948  QString ret;
2949  ret = "<br>" +
2950  i18n( "<i>Period start:</i> %1",
2951  KGlobal::locale()->formatDateTime( fb->dtStart().dateTime() ) );
2952  ret += "<br>" +
2953  i18n( "<i>Period start:</i> %1",
2954  KGlobal::locale()->formatDateTime( fb->dtEnd().dateTime() ) );
2955  return ret.replace( ' ', "&nbsp;" );
2956 }
2957 
2958 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event )
2959 {
2960  mResult = generateToolTip( event, dateRangeText( event, mDate ) );
2961  return !mResult.isEmpty();
2962 }
2963 
2964 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo )
2965 {
2966  mResult = generateToolTip( todo, dateRangeText( todo, mDate ) );
2967  return !mResult.isEmpty();
2968 }
2969 
2970 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal )
2971 {
2972  mResult = generateToolTip( journal, dateRangeText( journal ) );
2973  return !mResult.isEmpty();
2974 }
2975 
2976 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb )
2977 {
2978  //FIXME: support mRichText==false
2979  mResult = "<qt><b>" + i18n( "Free/Busy information for %1", fb->organizer().fullName() ) + "</b>";
2980  mResult += dateRangeText( fb );
2981  mResult += "</qt>";
2982  return !mResult.isEmpty();
2983 }
2984 
2985 static QString tooltipPerson( const QString &email, QString name )
2986 {
2987  // Make the search, if there is an email address to search on,
2988  // and name is missing
2989  if ( name.isEmpty() && !email.isEmpty() ) {
2990 #ifndef KDEPIM_NO_KRESOURCES
2991  KABC::AddressBook *add_book = KABC::StdAddressBook::self( true );
2992  KABC::Addressee::List addressList = add_book->findByEmail( email );
2993  if ( !addressList.isEmpty() ) {
2994  KABC::Addressee o = addressList.first();
2995  if ( !o.isEmpty() && addressList.size() < 2 ) {
2996  // use the name from the addressbook
2997  name = o.formattedName();
2998  }
2999  }
3000 #endif
3001  }
3002 
3003  // Show the attendee
3004  QString tmpString = ( name.isEmpty() ? email : name );
3005 
3006  return tmpString;
3007 }
3008 
3009 static QString tooltipFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role )
3010 {
3011  int maxNumAtts = 8; // maximum number of people to print per attendee role
3012  QString sep = i18nc( "separator for lists of people names", ", " );
3013  int sepLen = sep.length();
3014 
3015  int i = 0;
3016  QString tmpStr;
3017  Attendee::List::ConstIterator it;
3018  Attendee::List attendees = incidence->attendees();
3019 
3020  for ( it = attendees.constBegin(); it != attendees.constEnd(); ++it ) {
3021  Attendee *a = *it;
3022  if ( a->role() != role ) {
3023  // skip not this role
3024  continue;
3025  }
3026  if ( a->email() == incidence->organizer().email() ) {
3027  // skip attendee that is also the organizer
3028  continue;
3029  }
3030  if ( i == maxNumAtts ) {
3031  static QString etc = i18nc( "elipsis", "..." );
3032  tmpStr += etc;
3033  break;
3034  }
3035  tmpStr += tooltipPerson( a->email(), a->name() );
3036  if ( !a->delegator().isEmpty() ) {
3037  tmpStr += i18n( " (delegated by %1)", a->delegator() );
3038  }
3039  if ( !a->delegate().isEmpty() ) {
3040  tmpStr += i18n( " (delegated to %1)", a->delegate() );
3041  }
3042  tmpStr += sep;
3043  i++;
3044  }
3045  if ( tmpStr.endsWith( sep ) ) {
3046  tmpStr.truncate( tmpStr.length() - sepLen );
3047  }
3048  return tmpStr;
3049 }
3050 
3051 static QString tooltipFormatAttendees( Incidence *incidence )
3052 {
3053  QString tmpStr, str;
3054 
3055  // Add organizer link
3056  int attendeeCount = incidence->attendees().count();
3057  if ( attendeeCount > 1 ||
3058  ( attendeeCount == 1 &&
3059  incidence->organizer().email() != incidence->attendees().first()->email() ) ) {
3060  tmpStr += "<i>" + i18n( "Organizer:" ) + "</i>" + "&nbsp;";
3061  tmpStr += tooltipPerson( incidence->organizer().email(),
3062  incidence->organizer().name() );
3063  }
3064 
3065  // Add "chair"
3066  str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair );
3067  if ( !str.isEmpty() ) {
3068  tmpStr += "<br><i>" + i18n( "Chair:" ) + "</i>" + "&nbsp;";
3069  tmpStr += str;
3070  }
3071 
3072  // Add required participants
3073  str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant );
3074  if ( !str.isEmpty() ) {
3075  tmpStr += "<br><i>" + i18n( "Required Participants:" ) + "</i>" + "&nbsp;";
3076  tmpStr += str;
3077  }
3078 
3079  // Add optional participants
3080  str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant );
3081  if ( !str.isEmpty() ) {
3082  tmpStr += "<br><i>" + i18n( "Optional Participants:" ) + "</i>" + "&nbsp;";
3083  tmpStr += str;
3084  }
3085 
3086  // Add observers
3087  str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant );
3088  if ( !str.isEmpty() ) {
3089  tmpStr += "<br><i>" + i18n( "Observers:" ) + "</i>" + "&nbsp;";
3090  tmpStr += str;
3091  }
3092 
3093  return tmpStr;
3094 }
3095 
3096 QString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence *incidence,
3097  QString dtRangeText )
3098 {
3099  int maxDescLen = 120; // maximum description chars to print (before elipsis)
3100 
3101  //FIXME: support mRichText==false
3102  if ( !incidence ) {
3103  return QString();
3104  }
3105 
3106  QString tmp = "<qt>";
3107 
3108  // header
3109  tmp += "<b>" + incidence->richSummary() + "</b>";
3110  tmp += "<hr>";
3111 
3112  QString calStr = mLocation;
3113  if ( mCalendar ) {
3114  calStr = resourceString( mCalendar, incidence );
3115  }
3116  if ( !calStr.isEmpty() ) {
3117  tmp += "<i>" + i18n( "Calendar:" ) + "</i>" + "&nbsp;";
3118  tmp += calStr;
3119  }
3120 
3121  tmp += dtRangeText;
3122 
3123  if ( !incidence->location().isEmpty() ) {
3124  tmp += "<br>";
3125  tmp += "<i>" + i18nc( "event/todo location", "Location:" ) + "</i>" + "&nbsp;";
3126  tmp += incidence->richLocation();
3127  }
3128 
3129  QString durStr = durationString( incidence );
3130  if ( !durStr.isEmpty() ) {
3131  tmp += "<br>";
3132  tmp += "<i>" + i18n( "Duration:" ) + "</i>" + "&nbsp;";
3133  tmp += durStr;
3134  }
3135 
3136  if ( incidence->recurs() ) {
3137  tmp += "<br>";
3138  tmp += "<i>" + i18n( "Recurrence:" ) + "</i>" + "&nbsp;";
3139  tmp += recurrenceString( incidence );
3140  }
3141 
3142  if ( !incidence->description().isEmpty() ) {
3143  QString desc( incidence->description() );
3144  if ( !incidence->descriptionIsRich() ) {
3145  if ( desc.length() > maxDescLen ) {
3146  static QString etc = i18nc( "elipsis", "..." );
3147  desc = desc.left( maxDescLen ) + etc;
3148  }
3149  desc = Qt::escape( desc ).replace( '\n', "<br>" );
3150  } else {
3151  // TODO: truncate the description when it's rich text
3152  }
3153  tmp += "<hr>";
3154  tmp += "<i>" + i18n( "Description:" ) + "</i>" + "<br>";
3155  tmp += desc;
3156  tmp += "<hr>";
3157  }
3158 
3159  int reminderCount = incidence->alarms().count();
3160  if ( reminderCount > 0 && incidence->isAlarmEnabled() ) {
3161  tmp += "<br>";
3162  tmp += "<i>" + i18np( "Reminder:", "Reminders:", reminderCount ) + "</i>" + "&nbsp;";
3163  tmp += reminderStringList( incidence ).join( ", " );
3164  }
3165 
3166  tmp += "<br>";
3167  tmp += tooltipFormatAttendees( incidence );
3168 
3169  int categoryCount = incidence->categories().count();
3170  if ( categoryCount > 0 ) {
3171  tmp += "<br>";
3172  tmp += "<i>" + i18np( "Category:", "Categories:", categoryCount ) + "</i>" + "&nbsp;";
3173  tmp += incidence->categories().join( ", " );
3174  }
3175 
3176  tmp += "</qt>";
3177  return tmp;
3178 }
3179 //@endcond
3180 
3181 QString IncidenceFormatter::toolTipString( IncidenceBase *incidence,
3182  bool richText )
3183 {
3184  return toolTipStr( 0, incidence, QDate(), richText, KDateTime::Spec() );
3185 }
3186 
3187 QString IncidenceFormatter::toolTipStr( IncidenceBase *incidence,
3188  bool richText, KDateTime::Spec spec )
3189 {
3190  ToolTipVisitor v;
3191  if ( v.act( 0, incidence, QDate(), richText, spec ) ) {
3192  return v.result();
3193  } else {
3194  return QString();
3195  }
3196 }
3197 
3198 QString IncidenceFormatter::toolTipStr( Calendar *calendar,
3199  IncidenceBase *incidence,
3200  const QDate &date,
3201  bool richText, KDateTime::Spec spec )
3202 {
3203  ToolTipVisitor v;
3204  if ( v.act( calendar, incidence, date, richText, spec ) ) {
3205  return v.result();
3206  } else {
3207  return QString();
3208  }
3209 }
3210 
3211 QString IncidenceFormatter::toolTipStr( const QString &sourceName,
3212  IncidenceBase *incidence,
3213  const QDate &date,
3214  bool richText, KDateTime::Spec spec )
3215 {
3216  ToolTipVisitor v;
3217  if ( v.act( sourceName, incidence, date, richText, spec ) ) {
3218  return v.result();
3219  } else {
3220  return QString();
3221  }
3222 }
3223 
3224 /*******************************************************************
3225  * Helper functions for the Incidence tooltips
3226  *******************************************************************/
3227 
3228 //@cond PRIVATE
3229 static QString mailBodyIncidence( Incidence *incidence )
3230 {
3231  QString body;
3232  if ( !incidence->summary().isEmpty() ) {
3233  body += i18n( "Summary: %1\n", incidence->richSummary() );
3234  }
3235  if ( !incidence->organizer().isEmpty() ) {
3236  body += i18n( "Organizer: %1\n", incidence->organizer().fullName() );
3237  }
3238  if ( !incidence->location().isEmpty() ) {
3239  body += i18nc( "event/todo location", "Location: %1\n", incidence->richLocation() );
3240  }
3241  return body;
3242 }
3243 //@endcond
3244 
3245 //@cond PRIVATE
3246 class KCal::IncidenceFormatter::MailBodyVisitor
3247  : public IncidenceBase::Visitor
3248 {
3249  public:
3250  MailBodyVisitor()
3251  : mSpec( KDateTime::Spec() ), mResult( "" ) {}
3252 
3253  bool act( IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec() )
3254  {
3255  mSpec = spec;
3256  mResult = "";
3257  return incidence ? incidence->accept( *this ) : false;
3258  }
3259  QString result() const
3260  {
3261  return mResult;
3262  }
3263 
3264  protected:
3265  bool visit( Event *event );
3266  bool visit( Todo *todo );
3267  bool visit( Journal *journal );
3268  bool visit( FreeBusy * )
3269  {
3270  mResult = i18n( "This is a Free Busy Object" );
3271  return !mResult.isEmpty();
3272  }
3273  protected:
3274  KDateTime::Spec mSpec;
3275  QString mResult;
3276 };
3277 
3278 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event )
3279 {
3280  QString recurrence[]= {
3281  i18nc( "no recurrence", "None" ),
3282  i18nc( "event recurs by minutes", "Minutely" ),
3283  i18nc( "event recurs by hours", "Hourly" ),
3284  i18nc( "event recurs by days", "Daily" ),
3285  i18nc( "event recurs by weeks", "Weekly" ),
3286  i18nc( "event recurs same position (e.g. first monday) each month", "Monthly Same Position" ),
3287  i18nc( "event recurs same day each month", "Monthly Same Day" ),
3288  i18nc( "event recurs same month each year", "Yearly Same Month" ),
3289  i18nc( "event recurs same day each year", "Yearly Same Day" ),
3290  i18nc( "event recurs same position (e.g. first monday) each year", "Yearly Same Position" )
3291  };
3292 
3293  mResult = mailBodyIncidence( event );
3294  mResult += i18n( "Start Date: %1\n", dateToString( event->dtStart(), true, mSpec ) );
3295  if ( !event->allDay() ) {
3296  mResult += i18n( "Start Time: %1\n", timeToString( event->dtStart(), true, mSpec ) );
3297  }
3298  if ( event->dtStart() != event->dtEnd() ) {
3299  mResult += i18n( "End Date: %1\n", dateToString( event->dtEnd(), true, mSpec ) );
3300  }
3301  if ( !event->allDay() ) {
3302  mResult += i18n( "End Time: %1\n", timeToString( event->dtEnd(), true, mSpec ) );
3303  }
3304  if ( event->recurs() ) {
3305  Recurrence *recur = event->recurrence();
3306  // TODO: Merge these two to one of the form "Recurs every 3 days"
3307  mResult += i18n( "Recurs: %1\n", recurrence[ recur->recurrenceType() ] );
3308  mResult += i18n( "Frequency: %1\n", event->recurrence()->frequency() );
3309 
3310  if ( recur->duration() > 0 ) {
3311  mResult += i18np( "Repeats once", "Repeats %1 times", recur->duration() );
3312  mResult += '\n';
3313  } else {
3314  if ( recur->duration() != -1 ) {
3315 // TODO_Recurrence: What to do with all-day
3316  QString endstr;
3317  if ( event->allDay() ) {
3318  endstr = KGlobal::locale()->formatDate( recur->endDate() );
3319  } else {
3320  endstr = KGlobal::locale()->formatDateTime( recur->endDateTime().dateTime() );
3321  }
3322  mResult += i18n( "Repeat until: %1\n", endstr );
3323  } else {
3324  mResult += i18n( "Repeats forever\n" );
3325  }
3326  }
3327  }
3328 
3329  QString details = event->richDescription();
3330  if ( !details.isEmpty() ) {
3331  mResult += i18n( "Details:\n%1\n", details );
3332  }
3333  return !mResult.isEmpty();
3334 }
3335 
3336 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo )
3337 {
3338  mResult = mailBodyIncidence( todo );
3339 
3340  if ( todo->hasStartDate() && todo->dtStart().isValid() ) {
3341  mResult += i18n( "Start Date: %1\n", dateToString( todo->dtStart( false ), true, mSpec ) );
3342  if ( !todo->allDay() ) {
3343  mResult += i18n( "Start Time: %1\n", timeToString( todo->dtStart( false ), true, mSpec ) );
3344  }
3345  }
3346  if ( todo->hasDueDate() && todo->dtDue().isValid() ) {
3347  mResult += i18n( "Due Date: %1\n", dateToString( todo->dtDue(), true, mSpec ) );
3348  if ( !todo->allDay() ) {
3349  mResult += i18n( "Due Time: %1\n", timeToString( todo->dtDue(), true, mSpec ) );
3350  }
3351  }
3352  QString details = todo->richDescription();
3353  if ( !details.isEmpty() ) {
3354  mResult += i18n( "Details:\n%1\n", details );
3355  }
3356  return !mResult.isEmpty();
3357 }
3358 
3359 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal )
3360 {
3361  mResult = mailBodyIncidence( journal );
3362  mResult += i18n( "Date: %1\n", dateToString( journal->dtStart(), true, mSpec ) );
3363  if ( !journal->allDay() ) {
3364  mResult += i18n( "Time: %1\n", timeToString( journal->dtStart(), true, mSpec ) );
3365  }
3366  if ( !journal->description().isEmpty() ) {
3367  mResult += i18n( "Text of the journal:\n%1\n", journal->richDescription() );
3368  }
3369  return !mResult.isEmpty();
3370 }
3371 //@endcond
3372 
3373 QString IncidenceFormatter::mailBodyString( IncidenceBase *incidence )
3374 {
3375  return mailBodyStr( incidence, KDateTime::Spec() );
3376 }
3377 
3378 QString IncidenceFormatter::mailBodyStr( IncidenceBase *incidence,
3379  KDateTime::Spec spec )
3380 {
3381  if ( !incidence ) {
3382  return QString();
3383  }
3384 
3385  MailBodyVisitor v;
3386  if ( v.act( incidence, spec ) ) {
3387  return v.result();
3388  }
3389  return QString();
3390 }
3391 
3392 //@cond PRIVATE
3393 static QString recurEnd( Incidence *incidence )
3394 {
3395  QString endstr;
3396  if ( incidence->allDay() ) {
3397  endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() );
3398  } else {
3399  endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() );
3400  }
3401  return endstr;
3402 }
3403 //@endcond
3404 
3405 /************************************
3406  * More static formatting functions
3407  ************************************/
3408 
3409 QString IncidenceFormatter::recurrenceString( Incidence *incidence )
3410 {
3411  if ( !incidence->recurs() ) {
3412  return i18n( "No recurrence" );
3413  }
3414  QStringList dayList;
3415  dayList.append( i18n( "31st Last" ) );
3416  dayList.append( i18n( "30th Last" ) );
3417  dayList.append( i18n( "29th Last" ) );
3418  dayList.append( i18n( "28th Last" ) );
3419  dayList.append( i18n( "27th Last" ) );
3420  dayList.append( i18n( "26th Last" ) );
3421  dayList.append( i18n( "25th Last" ) );
3422  dayList.append( i18n( "24th Last" ) );
3423  dayList.append( i18n( "23rd Last" ) );
3424  dayList.append( i18n( "22nd Last" ) );
3425  dayList.append( i18n( "21st Last" ) );
3426  dayList.append( i18n( "20th Last" ) );
3427  dayList.append( i18n( "19th Last" ) );
3428  dayList.append( i18n( "18th Last" ) );
3429  dayList.append( i18n( "17th Last" ) );
3430  dayList.append( i18n( "16th Last" ) );
3431  dayList.append( i18n( "15th Last" ) );
3432  dayList.append( i18n( "14th Last" ) );
3433  dayList.append( i18n( "13th Last" ) );
3434  dayList.append( i18n( "12th Last" ) );
3435  dayList.append( i18n( "11th Last" ) );
3436  dayList.append( i18n( "10th Last" ) );
3437  dayList.append( i18n( "9th Last" ) );
3438  dayList.append( i18n( "8th Last" ) );
3439  dayList.append( i18n( "7th Last" ) );
3440  dayList.append( i18n( "6th Last" ) );
3441  dayList.append( i18n( "5th Last" ) );
3442  dayList.append( i18n( "4th Last" ) );
3443  dayList.append( i18n( "3rd Last" ) );
3444  dayList.append( i18n( "2nd Last" ) );
3445  dayList.append( i18nc( "last day of the month", "Last" ) );
3446  dayList.append( i18nc( "unknown day of the month", "unknown" ) ); //#31 - zero offset from UI
3447  dayList.append( i18n( "1st" ) );
3448  dayList.append( i18n( "2nd" ) );
3449  dayList.append( i18n( "3rd" ) );
3450  dayList.append( i18n( "4th" ) );
3451  dayList.append( i18n( "5th" ) );
3452  dayList.append( i18n( "6th" ) );
3453  dayList.append( i18n( "7th" ) );
3454  dayList.append( i18n( "8th" ) );
3455  dayList.append( i18n( "9th" ) );
3456  dayList.append( i18n( "10th" ) );
3457  dayList.append( i18n( "11th" ) );
3458  dayList.append( i18n( "12th" ) );
3459  dayList.append( i18n( "13th" ) );
3460  dayList.append( i18n( "14th" ) );
3461  dayList.append( i18n( "15th" ) );
3462  dayList.append( i18n( "16th" ) );
3463  dayList.append( i18n( "17th" ) );
3464  dayList.append( i18n( "18th" ) );
3465  dayList.append( i18n( "19th" ) );
3466  dayList.append( i18n( "20th" ) );
3467  dayList.append( i18n( "21st" ) );
3468  dayList.append( i18n( "22nd" ) );
3469  dayList.append( i18n( "23rd" ) );
3470  dayList.append( i18n( "24th" ) );
3471  dayList.append( i18n( "25th" ) );
3472  dayList.append( i18n( "26th" ) );
3473  dayList.append( i18n( "27th" ) );
3474  dayList.append( i18n( "28th" ) );
3475  dayList.append( i18n( "29th" ) );
3476  dayList.append( i18n( "30th" ) );
3477  dayList.append( i18n( "31st" ) );
3478  int weekStart = KGlobal::locale()->weekStartDay();
3479  QString dayNames;
3480  QString txt;
3481  const KCalendarSystem *calSys = KGlobal::locale()->calendar();
3482  Recurrence *recur = incidence->recurrence();
3483  switch ( recur->recurrenceType() ) {
3484  case Recurrence::rNone:
3485  return i18n( "No recurrence" );
3486  case Recurrence::rMinutely:
3487  if ( recur->duration() != -1 ) {
3488  txt = i18np( "Recurs every minute until %2",
3489  "Recurs every %1 minutes until %2",
3490  recur->frequency(), recurEnd( incidence ) );
3491  if ( recur->duration() > 0 ) {
3492  txt += i18nc( "number of occurrences",
3493  " (<numid>%1</numid> occurrences)",
3494  recur->duration() );
3495  }
3496  return txt;
3497  }
3498  return i18np( "Recurs every minute",
3499  "Recurs every %1 minutes", recur->frequency() );
3500  case Recurrence::rHourly:
3501  if ( recur->duration() != -1 ) {
3502  txt = i18np( "Recurs hourly until %2",
3503  "Recurs every %1 hours until %2",
3504  recur->frequency(), recurEnd( incidence ) );
3505  if ( recur->duration() > 0 ) {
3506  txt += i18nc( "number of occurrences",
3507  " (<numid>%1</numid> occurrences)",
3508  recur->duration() );
3509  }
3510  return txt;
3511  }
3512  return i18np( "Recurs hourly", "Recurs every %1 hours", recur->frequency() );
3513  case Recurrence::rDaily:
3514  if ( recur->duration() != -1 ) {
3515  txt = i18np( "Recurs daily until %2",
3516  "Recurs every %1 days until %2",
3517  recur->frequency(), recurEnd( incidence ) );
3518  if ( recur->duration() > 0 ) {
3519  txt += i18nc( "number of occurrences",
3520  " (<numid>%1</numid> occurrences)",
3521  recur->duration() );
3522  }
3523  return txt;
3524  }
3525  return i18np( "Recurs daily", "Recurs every %1 days", recur->frequency() );
3526  case Recurrence::rWeekly:
3527  {
3528  bool addSpace = false;
3529  for ( int i = 0; i < 7; ++i ) {
3530  if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) {
3531  if ( addSpace ) {
3532  dayNames.append( i18nc( "separator for list of days", ", " ) );
3533  }
3534  dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1,
3535  KCalendarSystem::ShortDayName ) );
3536  addSpace = true;
3537  }
3538  }
3539  if ( dayNames.isEmpty() ) {
3540  dayNames = i18nc( "Recurs weekly on no days", "no days" );
3541  }
3542  if ( recur->duration() != -1 ) {
3543  txt = i18ncp( "Recurs weekly on [list of days] until end-date",
3544  "Recurs weekly on %2 until %3",
3545  "Recurs every <numid>%1</numid> weeks on %2 until %3",
3546  recur->frequency(), dayNames, recurEnd( incidence ) );
3547  if ( recur->duration() > 0 ) {
3548  txt += i18nc( "number of occurrences",
3549  " (<numid>%1</numid> occurrences)",
3550  recur->duration() );
3551  }
3552  return txt;
3553  }
3554  return i18ncp( "Recurs weekly on [list of days]",
3555  "Recurs weekly on %2",
3556  "Recurs every <numid>%1</numid> weeks on %2",
3557  recur->frequency(), dayNames );
3558  }
3559  case Recurrence::rMonthlyPos:
3560  {
3561  if ( !recur->monthPositions().isEmpty() ) {
3562  KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0];
3563  if ( recur->duration() != -1 ) {
3564  txt = i18ncp( "Recurs every N months on the [2nd|3rd|...]"
3565  " weekdayname until end-date",
3566  "Recurs every month on the %2 %3 until %4",
3567  "Recurs every <numid>%1</numid> months on the %2 %3 until %4",
3568  recur->frequency(),
3569  dayList[rule.pos() + 31],
3570  calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
3571  recurEnd( incidence ) );
3572  if ( recur->duration() > 0 ) {
3573  txt += i18nc( "number of occurrences",
3574  " (<numid>%1</numid> occurrences)",
3575  recur->duration() );
3576  }
3577  return txt;
3578  }
3579  return i18ncp( "Recurs every N months on the [2nd|3rd|...] weekdayname",
3580  "Recurs every month on the %2 %3",
3581  "Recurs every %1 months on the %2 %3",
3582  recur->frequency(),
3583  dayList[rule.pos() + 31],
3584  calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ) );
3585  }
3586  break;
3587  }
3588  case Recurrence::rMonthlyDay:
3589  {
3590  if ( !recur->monthDays().isEmpty() ) {
3591  int days = recur->monthDays()[0];
3592  if ( recur->duration() != -1 ) {
3593  txt = i18ncp( "Recurs monthly on the [1st|2nd|...] day until end-date",
3594  "Recurs monthly on the %2 day until %3",
3595  "Recurs every %1 months on the %2 day until %3",
3596  recur->frequency(),
3597  dayList[days + 31],
3598  recurEnd( incidence ) );
3599  if ( recur->duration() > 0 ) {
3600  txt += i18nc( "number of occurrences",
3601  " (<numid>%1</numid> occurrences)",
3602  recur->duration() );
3603  }
3604  return txt;
3605  }
3606  return i18ncp( "Recurs monthly on the [1st|2nd|...] day",
3607  "Recurs monthly on the %2 day",
3608  "Recurs every <numid>%1</numid> month on the %2 day",
3609  recur->frequency(),
3610  dayList[days + 31] );
3611  }
3612  break;
3613  }
3614  case Recurrence::rYearlyMonth:
3615  {
3616  if ( recur->duration() != -1 ) {
3617  if ( !recur->yearDates().isEmpty() && !recur->yearMonths().isEmpty() ) {
3618  txt = i18ncp( "Recurs Every N years on month-name [1st|2nd|...]"
3619  " until end-date",
3620  "Recurs yearly on %2 %3 until %4",
3621  "Recurs every %1 years on %2 %3 until %4",
3622  recur->frequency(),
3623  calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
3624  dayList[ recur->yearDates()[0] + 31 ],
3625  recurEnd( incidence ) );
3626  if ( recur->duration() > 0 ) {
3627  txt += i18nc( "number of occurrences",
3628  " (<numid>%1</numid> occurrences)",
3629  recur->duration() );
3630  }
3631  return txt;
3632  }
3633  }
3634  if ( !recur->yearDates().isEmpty() && !recur->yearMonths().isEmpty() ) {
3635  return i18ncp( "Recurs Every N years on month-name [1st|2nd|...]",
3636  "Recurs yearly on %2 %3",
3637  "Recurs every %1 years on %2 %3",
3638  recur->frequency(),
3639  calSys->monthName( recur->yearMonths()[0],
3640  recur->startDate().year() ),
3641  dayList[ recur->yearDates()[0] + 31 ] );
3642  } else {
3643  if (!recur->yearMonths().isEmpty() ) {
3644  return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
3645  "Recurs yearly on %1 %2",
3646  calSys->monthName( recur->yearMonths()[0],
3647  recur->startDate().year() ),
3648  dayList[ recur->startDate().day() + 31 ] );
3649  } else {
3650  return i18nc( "Recurs Every year on month-name [1st|2nd|...]",
3651  "Recurs yearly on %1 %2",
3652  calSys->monthName( recur->startDate().month(),
3653  recur->startDate().year() ),
3654  dayList[ recur->startDate().day() + 31 ] );
3655  }
3656  }
3657  break;
3658  }
3659  case Recurrence::rYearlyDay:
3660  if ( !recur->yearDays().isEmpty() ) {
3661  if ( recur->duration() != -1 ) {
3662  txt = i18ncp( "Recurs every N years on day N until end-date",
3663  "Recurs every year on day <numid>%2</numid> until %3",
3664  "Recurs every <numid>%1</numid> years"
3665  " on day <numid>%2</numid> until %3",
3666  recur->frequency(),
3667  recur->yearDays()[0],
3668  recurEnd( incidence ) );
3669  if ( recur->duration() > 0 ) {
3670  txt += i18nc( "number of occurrences",
3671  " (<numid>%1</numid> occurrences)",
3672  recur->duration() );
3673  }
3674  return txt;
3675  }
3676  return i18ncp( "Recurs every N YEAR[S] on day N",
3677  "Recurs every year on day <numid>%2</numid>",
3678  "Recurs every <numid>%1</numid> years"
3679  " on day <numid>%2</numid>",
3680  recur->frequency(), recur->yearDays()[0] );
3681  }
3682  break;
3683  case Recurrence::rYearlyPos:
3684  {
3685  if ( !recur->yearMonths().isEmpty() && !recur->yearPositions().isEmpty() ) {
3686  KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0];
3687  if ( recur->duration() != -1 ) {
3688  txt = i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
3689  "of monthname until end-date",
3690  "Every year on the %2 %3 of %4 until %5",
3691  "Every <numid>%1</numid> years on the %2 %3 of %4"
3692  " until %5",
3693  recur->frequency(),
3694  dayList[rule.pos() + 31],
3695  calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
3696  calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ),
3697  recurEnd( incidence ) );
3698  if ( recur->duration() > 0 ) {
3699  txt += i18nc( "number of occurrences",
3700  " (<numid>%1</numid> occurrences)",
3701  recur->duration() );
3702  }
3703  return txt;
3704  }
3705  return i18ncp( "Every N years on the [2nd|3rd|...] weekdayname "
3706  "of monthname",
3707  "Every year on the %2 %3 of %4",
3708  "Every <numid>%1</numid> years on the %2 %3 of %4",
3709  recur->frequency(),
3710  dayList[rule.pos() + 31],
3711  calSys->weekDayName( rule.day(), KCalendarSystem::LongDayName ),
3712  calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) );
3713  }
3714  }
3715  break;
3716  }
3717  return i18n( "Incidence recurs" );
3718 }
3719 
3720 QString IncidenceFormatter::timeToString( const KDateTime &date,
3721  bool shortfmt,
3722  const KDateTime::Spec &spec )
3723 {
3724  if ( spec.isValid() ) {
3725 
3726  QString timeZone;
3727  if ( spec.timeZone() != KSystemTimeZones::local() ) {
3728  timeZone = ' ' + spec.timeZone().name();
3729  }
3730 
3731  return KGlobal::locale()->formatTime( date.toTimeSpec( spec ).time(), !shortfmt ) + timeZone;
3732  } else {
3733  return KGlobal::locale()->formatTime( date.time(), !shortfmt );
3734  }
3735 }
3736 
3737 QString IncidenceFormatter::dateToString( const KDateTime &date,
3738  bool shortfmt,
3739  const KDateTime::Spec &spec )
3740 {
3741  if ( spec.isValid() ) {
3742 
3743  QString timeZone;
3744  if ( spec.timeZone() != KSystemTimeZones::local() ) {
3745  timeZone = ' ' + spec.timeZone().name();
3746  }
3747 
3748  return
3749  KGlobal::locale()->formatDate( date.toTimeSpec( spec ).date(),
3750  ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) +
3751  timeZone;
3752  } else {
3753  return
3754  KGlobal::locale()->formatDate( date.date(),
3755  ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
3756  }
3757 }
3758 
3759 QString IncidenceFormatter::dateTimeToString( const KDateTime &date,
3760  bool allDay,
3761  bool shortfmt,
3762  const KDateTime::Spec &spec )
3763 {
3764  if ( allDay ) {
3765  return dateToString( date, shortfmt, spec );
3766  }
3767 
3768  if ( spec.isValid() ) {
3769  QString timeZone;
3770  if ( spec.timeZone() != KSystemTimeZones::local() ) {
3771  timeZone = ' ' + spec.timeZone().name();
3772  }
3773 
3774  return KGlobal::locale()->formatDateTime(
3775  date.toTimeSpec( spec ).dateTime(),
3776  ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) ) + timeZone;
3777  } else {
3778  return KGlobal::locale()->formatDateTime(
3779  date.dateTime(),
3780  ( shortfmt ? KLocale::ShortDate : KLocale::LongDate ) );
3781  }
3782 }
3783 
3784 QString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence )
3785 {
3786 #ifndef KDEPIM_NO_KRESOURCES
3787  if ( !calendar || !incidence ) {
3788  return QString();
3789  }
3790 
3791  CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar );
3792  if ( !calendarResource ) {
3793  return QString();
3794  }
3795 
3796  ResourceCalendar *resourceCalendar = calendarResource->resource( incidence );
3797  if ( resourceCalendar ) {
3798  if ( !resourceCalendar->subresources().isEmpty() ) {
3799  QString subRes = resourceCalendar->subresourceIdentifier( incidence );
3800  if ( subRes.isEmpty() ) {
3801  return resourceCalendar->resourceName();
3802  } else {
3803  return resourceCalendar->labelForSubresource( subRes );
3804  }
3805  }
3806  return resourceCalendar->resourceName();
3807  }
3808 #endif
3809  return QString();
3810 }
3811 
3812 static QString secs2Duration( int secs )
3813 {
3814  QString tmp;
3815  int days = secs / 86400;
3816  if ( days > 0 ) {
3817  tmp += i18np( "1 day", "%1 days", days );
3818  tmp += ' ';
3819  secs -= ( days * 86400 );
3820  }
3821  int hours = secs / 3600;
3822  if ( hours > 0 ) {
3823  tmp += i18np( "1 hour", "%1 hours", hours );
3824  tmp += ' ';
3825  secs -= ( hours * 3600 );
3826  }
3827  int mins = secs / 60;
3828  if ( mins > 0 ) {
3829  tmp += i18np( "1 minute", "%1 minutes", mins );
3830  }
3831  return tmp;
3832 }
3833 
3834 QString IncidenceFormatter::durationString( Incidence *incidence )
3835 {
3836  QString tmp;
3837  if ( incidence->type() == "Event" ) {
3838  Event *event = static_cast<Event *>( incidence );
3839  if ( event->hasEndDate() ) {
3840  if ( !event->allDay() ) {
3841  tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) );
3842  } else {
3843  tmp = i18np( "1 day", "%1 days",
3844  event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 );
3845  }
3846  } else {
3847  tmp = i18n( "forever" );
3848  }
3849  } else if ( incidence->type() == "Todo" ) {
3850  Todo *todo = static_cast<Todo *>( incidence );
3851  if ( todo->hasDueDate() ) {
3852  if ( todo->hasStartDate() ) {
3853  if ( !todo->allDay() ) {
3854  tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) );
3855  } else {
3856  tmp = i18np( "1 day", "%1 days",
3857  todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 );
3858  }
3859  }
3860  }
3861  }
3862  return tmp;
3863 }
3864 
3865 QStringList IncidenceFormatter::reminderStringList( Incidence *incidence, bool shortfmt )
3866 {
3867  //TODO: implement shortfmt=false
3868  Q_UNUSED( shortfmt );
3869 
3870  QStringList reminderStringList;
3871 
3872  if ( incidence ) {
3873  Alarm::List alarms = incidence->alarms();
3874  Alarm::List::ConstIterator it;
3875  for ( it = alarms.constBegin(); it != alarms.constEnd(); ++it ) {
3876  Alarm *alarm = *it;
3877  int offset = 0;
3878  QString remStr, atStr, offsetStr;
3879  if ( alarm->hasTime() ) {
3880  offset = 0;
3881  if ( alarm->time().isValid() ) {
3882  atStr = KGlobal::locale()->formatDateTime( alarm->time() );
3883  }
3884  } else if ( alarm->hasStartOffset() ) {
3885  offset = alarm->startOffset().asSeconds();
3886  if ( offset < 0 ) {
3887  offset = -offset;
3888  offsetStr = i18nc( "N days/hours/minutes before the start datetime",
3889  "%1 before the start", secs2Duration( offset ) );
3890  } else if ( offset > 0 ) {
3891  offsetStr = i18nc( "N days/hours/minutes after the start datetime",
3892  "%1 after the start", secs2Duration( offset ) );
3893  } else { //offset is 0
3894  if ( incidence->dtStart().isValid() ) {
3895  atStr = KGlobal::locale()->formatDateTime( incidence->dtStart() );
3896  }
3897  }
3898  } else if ( alarm->hasEndOffset() ) {
3899  offset = alarm->endOffset().asSeconds();
3900  if ( offset < 0 ) {
3901  offset = -offset;
3902  if ( incidence->type() == "Todo" ) {
3903  offsetStr = i18nc( "N days/hours/minutes before the due datetime",
3904  "%1 before the to-do is due", secs2Duration( offset ) );
3905  } else {
3906  offsetStr = i18nc( "N days/hours/minutes before the end datetime",
3907  "%1 before the end", secs2Duration( offset ) );
3908  }
3909  } else if ( offset > 0 ) {
3910  if ( incidence->type() == "Todo" ) {
3911  offsetStr = i18nc( "N days/hours/minutes after the due datetime",
3912  "%1 after the to-do is due", secs2Duration( offset ) );
3913  } else {
3914  offsetStr = i18nc( "N days/hours/minutes after the end datetime",
3915  "%1 after the end", secs2Duration( offset ) );
3916  }
3917  } else { //offset is 0
3918  if ( incidence->type() == "Todo" ) {
3919  Todo *t = static_cast<Todo *>( incidence );
3920  if ( t->dtDue().isValid() ) {
3921  atStr = KGlobal::locale()->formatDateTime( t->dtDue() );
3922  }
3923  } else {
3924  Event *e = static_cast<Event *>( incidence );
3925  if ( e->dtEnd().isValid() ) {
3926  atStr = KGlobal::locale()->formatDateTime( e->dtEnd() );
3927  }
3928  }
3929  }
3930  }
3931  if ( offset == 0 ) {
3932  if ( !atStr.isEmpty() ) {
3933  remStr = i18nc( "reminder occurs at datetime", "at %1", atStr );
3934  }
3935  } else {
3936  remStr = offsetStr;
3937  }
3938 
3939  if ( alarm->repeatCount() > 0 ) {
3940  QString countStr = i18np( "repeats once", "repeats %1 times", alarm->repeatCount() );
3941  QString intervalStr = i18nc( "interval is N days/hours/minutes",
3942  "interval is %1",
3943  secs2Duration( alarm->snoozeTime().asSeconds() ) );
3944  QString repeatStr = i18nc( "(repeat string, interval string)",
3945  "(%1, %2)", countStr, intervalStr );
3946  remStr = remStr + ' ' + repeatStr;
3947 
3948  }
3949  reminderStringList << remStr;
3950  }
3951  }
3952 
3953  return reminderStringList;
3954 }
KCal::Incidence::description
QString description() const
Returns the incidence description.
Definition: incidence.cpp:390
KCal::IncidenceFormatter::recurrenceString
KCAL_DEPRECATED_EXPORT QString recurrenceString(Incidence *incidence)
Build a pretty QString representation of an Incidence's recurrence info.
Definition: incidenceformatter.cpp:3409
KCal::Person::email
QString email() const
Returns the email address for this person.
Definition: person.cpp:144
KCal::ICalDrag::mimeType
KCAL_DEPRECATED_EXPORT QString mimeType()
Mime-type of iCalendar.
Definition: icaldrag.cpp:34
calendarlocal.h
This file is part of the API for handling calendar data and defines the CalendarLocal class...
KCal::Incidence::richDescription
QString richDescription() const
Returns the incidence description in rich text format.
Definition: incidence.cpp:395
KCal::IncidenceBase::isReadOnly
bool isReadOnly() const
Returns true the object is read-only; false otherwise.
Definition: incidencebase.h:318
KCal::Recurrence::yearMonths
QList< int > yearMonths() const
Returns the months within a yearly recurrence.
Definition: recurrence.cpp:679
KCal::ResourceCalendar::subresourceIdentifier
virtual QString subresourceIdentifier(Incidence *incidence)
Get the identifier of the subresource associated with a specified incidence.
Definition: resourcecalendar.cpp:292
KCal::Period::end
KDateTime end() const
Returns when this period ends.
Definition: period.cpp:111
KCal::IncidenceBase::attendees
const Attendee::List & attendees() const
Returns a list of incidence attendees.
Definition: incidencebase.cpp:378
KCal::Todo
Provides a To-do in the sense of RFC2445.
Definition: todo.h:44
KCal::Calendar
Represents the main calendar class.
Definition: calendar.h:119
KCal::Recurrence::duration
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
Definition: recurrence.cpp:485
KCal::IncidenceBase
An abstract class that provides a common base for all calendar incidence classes. ...
Definition: incidencebase.h:102
KCal::IncidenceFormatter::formatICalInvitationNoHtml
KCAL_DEPRECATED_EXPORT QString formatICalInvitationNoHtml(QString invitation, Calendar *calendar, InvitationFormatterHelper *helper)
Deliver an HTML formatted string displaying an invitation.
KCal::Recurrence::yearDays
QList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
Definition: recurrence.cpp:668
KCal::IncidenceFormatter::timeToString
KCAL_DEPRECATED_EXPORT QString timeToString(const KDateTime &date, bool shortfmt=true, const KDateTime::Spec &spec=KDateTime::Spec())
Build a QString time representation of a KDateTime object.
Definition: incidenceformatter.cpp:3720
KCal::IncidenceFormatter::resourceString
KCAL_DEPRECATED_EXPORT QString resourceString(Calendar *calendar, Incidence *incidence)
Returns a Calendar Resource label name for the specified Incidence.
Definition: incidenceformatter.cpp:3784
KCal::Incidence::descriptionIsRich
bool descriptionIsRich() const
Returns true if incidence description contains RichText; false otherwise.
Definition: incidence.cpp:404
KCal::IncidenceBase::dtStart
virtual KDateTime dtStart() const
Returns an incidence's starting date/time as a KDateTime.
Definition: incidencebase.cpp:248
KCal::IncidenceFormatter::dateTimeToString
KCAL_DEPRECATED_EXPORT QString dateTimeToString(const KDateTime &date, bool dateOnly=false, bool shortfmt=true, const KDateTime::Spec &spec=KDateTime::Spec())
Build a QString date/time representation of a KDateTime object.
Definition: incidenceformatter.cpp:3759
KCal::Alarm::repeatCount
int repeatCount() const
Returns how many times an alarm may repeats after its initial occurrence.
Definition: alarm.cpp:488
KCal::Recurrence::monthPositions
QList< RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
Definition: recurrence.cpp:660
KCal::Attendee
Represents information related to an attendee of an Calendar Incidence, typically a meeting or task (...
Definition: attendee.h:58
KCal::CalendarResources
This class provides a Calendar which is composed of other Calendars known as "Resources".
Definition: calendarresources.h:57
KCal::Event
This class provides an Event in the sense of RFC2445.
Definition: event.h:41
KCal::ScheduleMessage
A Scheduling message class.
Definition: scheduler.h:58
KCal::Recurrence::getPreviousDateTime
KDateTime getPreviousDateTime(const KDateTime &afterDateTime) const
Returns the date and time of the last previous recurrence, before the specified date/time.
Definition: recurrence.cpp:1087
KCal::Attendee::InProcess
To-do in process of being completed.
Definition: attendee.h:78
KCal::IncidenceBase::uid
QString uid() const
Returns the unique id (uid) for the incidence.
Definition: incidencebase.cpp:184
KCal::Recurrence::frequency
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
Definition: recurrence.cpp:600
KCal::Attendee::Chair
Chairperson.
Definition: attendee.h:89
KCal::Incidence::isAlarmEnabled
bool isAlarmEnabled() const
Returns true if any of the incidence alarms are enabled; false otherwise.
Definition: incidence.cpp:936
KCal::Calendar::timeSpec
KDateTime::Spec timeSpec() const
Get the time specification (time zone etc.) used for creating or modifying incidences in the Calendar...
Definition: calendar.cpp:145
KCal::Todo::dtStart
virtual KDateTime dtStart() const
Definition: todo.cpp:289
KCal::Recurrence::days
QBitArray days() const
Returns week day mask (bit 0 = Monday).
Definition: recurrence.cpp:630
KCal::Duration::asSeconds
int asSeconds() const
Returns the length of the duration in seconds.
Definition: duration.cpp:199
KCal::IncidenceFormatter::toolTipString
KCAL_DEPRECATED_EXPORT QString toolTipString(IncidenceBase *incidence, bool richText=true)
Create a QString representation of an Incidence in a nice format suitable for using in a tooltip...
Definition: incidenceformatter.cpp:3181
KCal::IncidenceFormatter::mailBodyStr
KCAL_DEPRECATED_EXPORT QString mailBodyStr(IncidenceBase *incidence, KDateTime::Spec spec=KDateTime::Spec())
Create a QString representation of an Incidence in format suitable for including inside a mail messag...
Definition: incidenceformatter.cpp:3378
KCal::Todo::dtDue
KDateTime dtDue(bool first=false) const
Returns due date and time.
Definition: todo.cpp:181
KCal::ResourceCalendar
This class provides the interfaces for a calendar resource.
Definition: resourcecalendar.h:50
KCal::Incidence::created
KDateTime created() const
Returns the incidence creation date/time.
Definition: incidence.cpp:328
KCal::Incidence::richLocation
QString richLocation() const
Returns the incidence location in rich text format.
Definition: incidence.cpp:967
KCal::Attendee::Completed
To-do completed.
Definition: attendee.h:77
KCal::Attendee::status
PartStat status() const
Returns the PartStat of the attendee.
Definition: attendee.cpp:125
KCal::IncidenceBase::Visitor
This class provides the interface for a visitor of calendar components.
Definition: incidencebase.h:112
KCal::Todo::isCompleted
bool isCompleted() const
Returns true if the todo is 100% completed, otherwise return false.
Definition: todo.cpp:411
KCal::IncidenceFormatter::mailBodyString
KCAL_DEPRECATED_EXPORT QString mailBodyString(IncidenceBase *incidence)
Create a QString representation of an Incidence in format suitable for including inside a mail messag...
Definition: incidenceformatter.cpp:3373
KCal::IncidenceBase::shiftTimes
virtual void shiftTimes(const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec)
Shift the times of the incidence so that they appear at the same clock time as before but in a new ti...
Definition: incidencebase.cpp:324
KCal::Attachment::label
QString label() const
Returns the attachment label string.
Definition: attachment.cpp:194
KCal::Event::isMultiDay
bool isMultiDay(const KDateTime::Spec &spec=KDateTime::Spec()) const
Returns true if the event spans multiple days, otherwise return false.
Definition: event.cpp:231
KCal::Attendee::Declined
Event, to-do or journal declined.
Definition: attendee.h:74
incidenceformatter.h
This file is part of the API for handling calendar data and provides static functions for formatting ...
KCal::Attachment::mimeType
QString mimeType() const
Returns the MIME-type of the attachment.
Definition: attachment.cpp:174
KCal::IncidenceFormatter::formatICalInvitation
KCAL_DEPRECATED_EXPORT QString formatICalInvitation(QString invitation, Calendar *calendar, InvitationFormatterHelper *helper)
Deliver an HTML formatted string displaying an invitation.
KCal::ErrorFormat::message
QString message()
Returns the format error message.
Definition: exceptions.cpp:84
KCal::Person::isEmpty
bool isEmpty() const
Returns true if the person name and email address are empty.
Definition: person.cpp:149
KCal::Alarm::hasTime
bool hasTime() const
Returns true if the alarm has a trigger date/time.
Definition: alarm.cpp:450
KCal::Attendee::Role
Role
The different types of participation roles.
Definition: attendee.h:85
KCal::Period::start
KDateTime start() const
Returns when this period starts.
Definition: period.cpp:106
KCal::RecurrenceRule::WDayPos
structure for describing the n-th weekday of the month/year.
Definition: recurrencerule.h:71
KCal::Recurrence::startDate
QDate startDate() const
Return the start date/time of the recurrence.
Definition: recurrence.cpp:577
KCal::Incidence::recurs
bool recurs() const
Definition: incidence.cpp:573
KCal::Attendee::NonParticipant
Non-Participant; copied for information purposes.
Definition: attendee.h:88
KCal::Period::duration
Duration duration() const
Returns the duration of the period.
Definition: period.cpp:116
KCal::IncidenceBase::allDay
bool allDay() const
Returns true or false depending on whether the incidence is all-day.
Definition: incidencebase.cpp:310
KCal::ICalFormat
iCalendar format implementation.
Definition: icalformat.h:52
calendarresources.h
This file is part of the API for handling calendar data and defines the CalendarResources class...
KCal::IncidenceBase::type
virtual QByteArray type() const =0
Prints the type of Incidence as a string.
KCal::Person
Represents a person, by name ane email address.
Definition: person.h:48
todo.h
This file is part of the API for handling calendar data and defines the Todo class.
KCal::Incidence::attachments
Attachment::List attachments() const
Returns a list of all incidence attachments.
Definition: incidence.cpp:715
KCal::Period::hasDuration
bool hasDuration() const
Returns true if this period has a set duration, false if it just has a start and an end...
Definition: period.cpp:131
KCal::Event::hasEndDate
bool hasEndDate() const
Returns whether the event has an end date/time.
Definition: event.cpp:226
calendar.h
This file is part of the API for handling calendar data and defines the Calendar class.
KCal::Person::name
QString name() const
Returns the person name string.
Definition: person.cpp:139
KCal::Alarm::hasStartOffset
bool hasStartOffset() const
Returns whether the alarm is defined in terms of an offset relative to the start of the parent Incide...
Definition: alarm.cpp:606
KCal::IncidenceFormatter::reminderStringList
KCAL_DEPRECATED_EXPORT QStringList reminderStringList(Incidence *incidence, bool shortfmt=true)
Returns a reminder string computed for the specified Incidence.
Definition: incidenceformatter.cpp:3865
KCal::CustomProperties::customProperty
QString customProperty(const QByteArray &app, const QByteArray &key) const
Return the value of a custom calendar property.
Definition: customproperties.cpp:115
KCal::ResourceCalendar::subresources
virtual QStringList subresources() const
If this resource has subresources, return a QStringList of them.
Definition: resourcecalendar.cpp:270
KCal::IncidenceBase::organizer
Person organizer() const
Returns the Person associated with this incidence.
Definition: incidencebase.cpp:230
KCal::Incidence
Provides the abstract base class common to non-FreeBusy (Events, To-dos, Journals) calendar component...
Definition: incidence.h:68
KCal::ListBase
This class provides a template for lists of pointers.
Definition: listbase.h:44
KCal::Alarm::hasEndOffset
bool hasEndOffset() const
Returns whether the alarm is defined in terms of an offset relative to the end of the event...
Definition: alarm.cpp:611
KCal::Incidence::richSummary
QString richSummary() const
Returns the incidence summary in rich text format.
Definition: incidence.cpp:429
freebusy.h
This file is part of the API for handling calendar data and defines the FreeBusy class.
KCal::Attachment
Represents information related to an attachment for a Calendar Incidence.
Definition: attachment.h:57
KCal::Todo::hasDueDate
bool hasDueDate() const
Returns true if the todo has a due date, otherwise return false.
Definition: todo.cpp:252
KCal::Journal
Provides a Journal in the sense of RFC2445.
Definition: journal.h:43
KCal::ScheduleMessage::event
IncidenceBase * event()
Returns the event associated with this message.
Definition: scheduler.cpp:70
KCal::Alarm::endOffset
Duration endOffset() const
Returns offset of alarm in time relative to the end of the event.
Definition: alarm.cpp:626
KCal::CalendarResources::resource
ResourceCalendar * resource(Incidence *incidence)
Returns the Resource associated with a specified Incidence.
Definition: calendarresources.cpp:782
KCal::Alarm::startOffset
Duration startOffset() const
Returns offset of alarm in time relative to the start of the parent Incidence.
Definition: alarm.cpp:601
KCal::Incidence::alarms
const Alarm::List & alarms() const
Returns a list of all incidence alarms.
Definition: incidence.cpp:906
KCal::Recurrence::endDateTime
KDateTime endDateTime() const
Returns the date/time of the last recurrence.
Definition: recurrence.cpp:432
KCal::Recurrence::yearDates
QList< int > yearDates() const
Returns the dates within a yearly recurrence.
Definition: recurrence.cpp:674
KCal::IncidenceFormatter::toolTipStr
KCAL_DEPRECATED_EXPORT QString toolTipStr(Calendar *calendar, IncidenceBase *incidence, const QDate &date=QDate(), bool richText=true, KDateTime::Spec spec=KDateTime::Spec())
Create a QString representation of an Incidence in a nice format suitable for using in a tooltip...
Definition: incidenceformatter.cpp:3198
KCal::Period
The period can be defined by either a start time and an end time or by a start time and a duration...
Definition: period.h:47
KCal::Incidence::locationIsRich
bool locationIsRich() const
Returns true if incidence location contains RichText; false otherwise.
Definition: incidence.cpp:976
KCal::IncidenceBase::accept
virtual bool accept(Visitor &v)
Accept IncidenceVisitor.
Definition: incidencebase.h:228
KCal::Attendee::uid
QString uid() const
Returns the UID of the attendee.
Definition: attendee.cpp:200
journal.h
This file is part of the API for handling calendar data and defines the Journal class.
KCal::Attachment::uri
QString uri() const
Returns the URI of the attachment.
Definition: attachment.cpp:108
KCal::Attendee::Accepted
Event, to-do or journal accepted.
Definition: attendee.h:73
KCal::Attendee::statusName
static QString statusName(PartStat status)
Returns the specified PartStat status as a text string.
Definition: attendee.cpp:135
KCal::Attendee::NeedsAction
Event, to-do or journal needs action (default)
Definition: attendee.h:72
KCal::ResourceCalendar::labelForSubresource
virtual QString labelForSubresource(const QString &resource) const
What is the label for this subresource?
Definition: resourcecalendar.cpp:286
KCal::Attendee::delegate
QString delegate() const
Returns the delegate.
Definition: attendee.cpp:240
KCal::Recurrence::getNextDateTime
KDateTime getNextDateTime(const KDateTime &preDateTime) const
Returns the date and time of the next recurrence, after the specified date/time.
Definition: recurrence.cpp:1012
KCal::Attendee::delegator
QString delegator() const
Returns the delegator.
Definition: attendee.cpp:250
KCal::ICalFormat::parseScheduleMessage
ScheduleMessage * parseScheduleMessage(Calendar *calendar, const QString &string)
Parses a Calendar scheduling message string into ScheduleMessage object.
Definition: icalformat.cpp:403
attachment.h
This file is part of the API for handling calendar data and defines the Attachment class...
KCal::Alarm::snoozeTime
Duration snoozeTime() const
Returns the snooze time interval.
Definition: alarm.cpp:475
KCal::Attendee::ReqParticipant
Participation is required (default)
Definition: attendee.h:86
KCal::FreeBusy::busyPeriods
Period::List busyPeriods() const
Returns the list of all periods within the free/busy.
Definition: freebusy.cpp:244
KCal::Attendee::role
Role role() const
Returns the Role of the attendee.
Definition: attendee.cpp:185
KCal::Recurrence::endDate
QDate endDate() const
Returns the date of the last recurrence.
Definition: recurrence.cpp:457
event.h
This file is part of the API for handling calendar data and defines the Event class.
KRES::Resource::resourceName
virtual QString resourceName() const
KCal::Person::fullName
QString fullName() const
Returns the full name of this person.
Definition: person.cpp:114
KCal::Todo::hasStartDate
bool hasStartDate() const
Returns true if the todo has a start date, otherwise return false.
Definition: todo.cpp:266
KCal::Attendee::OptParticipant
Participation is optional.
Definition: attendee.h:87
KCal::IncidenceFormatter::dateToString
KCAL_DEPRECATED_EXPORT QString dateToString(const KDateTime &date, bool shortfmt=true, const KDateTime::Spec &spec=KDateTime::Spec())
Build a QString date representation of a KDateTime object.
Definition: incidenceformatter.cpp:3737
KCal::Recurrence::yearPositions
QList< RecurrenceRule::WDayPos > yearPositions() const
Returns the positions within a yearly recurrence.
Definition: recurrence.cpp:685
KCal::IncidenceFormatter::extensiveDisplayString
KCAL_DEPRECATED_EXPORT QString extensiveDisplayString(IncidenceBase *incidence)
Create a RichText QString representation of an Incidence in a nice format suitable for using in a vie...
Definition: incidenceformatter.cpp:971
KCal::Incidence::recurrence
Recurrence * recurrence() const
Returns the recurrence rule associated with this incidence.
Definition: incidence.cpp:545
KCal::Todo::percentComplete
int percentComplete() const
Returns what percentage of the to-do is completed.
Definition: todo.cpp:463
KCal::ScheduleMessage::method
iTIPMethod method()
Returns the iTIP method associated with this message.
Definition: scheduler.cpp:75
KCal::Incidence::priority
int priority() const
Returns the incidence priority.
Definition: incidence.cpp:795
KCal::Attendee::statusStr
QString statusStr() const
Returns the attendee PartStat as a text string.
Definition: attendee.cpp:130
KCal::Incidence::summaryIsRich
bool summaryIsRich() const
Returns true if incidence summary contains RichText; false otherwise.
Definition: incidence.cpp:438
KCal::Recurrence::recurrenceType
ushort recurrenceType() const
Returns the event's recurrence status.
Definition: recurrence.cpp:238
KCal::Todo::completedStr
QString completedStr(bool shortfmt=false) const
Returns string contaiting date and time when the todo was completed formatted according to the user's...
Definition: todo.cpp:441
KCal::Incidence::revision
int revision() const
Returns the number of revisions this incidence has seen.
Definition: incidence.cpp:344
KCal::Attendee::Delegated
Event or to-do delegated.
Definition: attendee.h:76
KCal::IncidenceBase::comments
QStringList comments() const
Returns all incidence comments as a list of strings.
Definition: incidencebase.cpp:357
KCal::Incidence::categories
QStringList categories() const
Returns the incidence categories as a list of strings.
Definition: incidence.cpp:473
KCal::IncidenceFormatter::durationString
KCAL_DEPRECATED_EXPORT QString durationString(Incidence *incidence)
Returns a duration string computed for the specified Incidence.
Definition: incidenceformatter.cpp:3834
KCal::Incidence::location
QString location() const
Returns the incidence location.
Definition: incidence.cpp:962
KCal::IncidenceFormatter::extensiveDisplayStr
KCAL_DEPRECATED_EXPORT QString extensiveDisplayStr(Calendar *calendar, IncidenceBase *incidence, const QDate &date=QDate(), KDateTime::Spec spec=KDateTime::Spec())
Create a RichText QString representation of an Incidence in a nice format suitable for using in a vie...
Definition: incidenceformatter.cpp:991
KCal::Alarm::time
KDateTime time() const
Returns the alarm trigger date/time.
Definition: alarm.cpp:430
KCal::IncidenceBase::Visitor::visit
virtual bool visit(Event *event)
Reimplement this function in your concrete subclass of IncidenceBase::Visitor to perform actions on a...
Definition: incidencebase.cpp:513
KCal::Attendee::roleName
static QString roleName(Role role)
Returns the specified Role role as a text string.
Definition: attendee.cpp:205
KCal::FreeBusy
Provides information about the free/busy time of a calendar.
Definition: freebusy.h:50
KCal::Alarm
Represents an alarm notification.
Definition: alarm.h:66
KCal::Attendee::Tentative
Event or to-do tentatively accepted.
Definition: attendee.h:75
KCal::Recurrence::monthDays
QList< int > monthDays() const
Returns list of day numbers of a month.
Definition: recurrence.cpp:649
KCal::FreeBusy::dtEnd
virtual KDateTime dtEnd() const
Returns the end datetime for the free/busy.
Definition: freebusy.cpp:239
icalformat.h
This file is part of the API for handling calendar data and defines the ICalFormat class...
KCal::Recurrence
This class represents a recurrence rule for a calendar incidence.
Definition: recurrence.h:91
KCal::Event::dtEnd
virtual KDateTime dtEnd() const
Returns the event end date and time.
Definition: event.cpp:132
KCal::Incidence::summary
QString summary() const
Returns the incidence summary.
Definition: incidence.cpp:424
KCal::IncidenceBase::attendeeByMail
Attendee * attendeeByMail(const QString &email) const
Returns the attendee with the specified email address.
Definition: incidencebase.cpp:397
KCal::CalFormat::exception
ErrorFormat * exception()
Returns an exception, if there is any, containing information about the last error that occurred...
Definition: calformat.cpp:83
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:58 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KCal Library

Skip menu "KCal Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal