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