libkcal

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