incidenceformatter.cpp
00001 /* 00002 This file is part of libkcal. 00003 00004 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> 00005 Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com> 00006 Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net> 00007 00008 This library is free software; you can redistribute it and/or 00009 modify it under the terms of the GNU Library General Public 00010 License as published by the Free Software Foundation; either 00011 version 2 of the License, or (at your option) any later version. 00012 00013 This library is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 Library General Public License for more details. 00017 00018 You should have received a copy of the GNU Library General Public License 00019 along with this library; see the file COPYING.LIB. If not, write to 00020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include "incidenceformatter.h" 00025 00026 #include <libkcal/attachment.h> 00027 #include <libkcal/event.h> 00028 #include <libkcal/todo.h> 00029 #include <libkcal/journal.h> 00030 #include <libkcal/calendar.h> 00031 #include <libkcal/calendarlocal.h> 00032 #include <libkcal/icalformat.h> 00033 #include <libkcal/freebusy.h> 00034 #include <libkcal/calendarresources.h> 00035 00036 #include <libemailfunctions/email.h> 00037 00038 #include <ktnef/ktnefparser.h> 00039 #include <ktnef/ktnefmessage.h> 00040 #include <ktnef/ktnefdefs.h> 00041 #include <kabc/phonenumber.h> 00042 #include <kabc/vcardconverter.h> 00043 #include <kabc/stdaddressbook.h> 00044 00045 #include <kapplication.h> 00046 #include <kemailsettings.h> 00047 00048 #include <klocale.h> 00049 #include <kglobal.h> 00050 #include <kiconloader.h> 00051 #include <kcalendarsystem.h> 00052 #include <kmimetype.h> 00053 00054 #include <tqbuffer.h> 00055 #include <tqstylesheet.h> 00056 #include <tqdatetime.h> 00057 #include <tqregexp.h> 00058 00059 #include <time.h> 00060 00061 using namespace KCal; 00062 00063 /******************* 00064 * General helpers 00065 *******************/ 00066 00067 static TQString htmlAddLink( const TQString &ref, const TQString &text, 00068 bool newline = true ) 00069 { 00070 TQString tmpStr( "<a href=\"" + ref + "\">" + text + "</a>" ); 00071 if ( newline ) tmpStr += "\n"; 00072 return tmpStr; 00073 } 00074 00075 static TQString htmlAddTag( const TQString & tag, const TQString & text ) 00076 { 00077 int numLineBreaks = text.contains( "\n" ); 00078 TQString str = "<" + tag + ">"; 00079 TQString tmpText = text; 00080 TQString tmpStr = str; 00081 if( numLineBreaks >= 0 ) { 00082 if ( numLineBreaks > 0) { 00083 int pos = 0; 00084 TQString tmp; 00085 for( int i = 0; i <= numLineBreaks; i++ ) { 00086 pos = tmpText.find( "\n" ); 00087 tmp = tmpText.left( pos ); 00088 tmpText = tmpText.right( tmpText.length() - pos - 1 ); 00089 tmpStr += tmp + "<br>"; 00090 } 00091 } else { 00092 tmpStr += tmpText; 00093 } 00094 } 00095 tmpStr += "</" + tag + ">"; 00096 return tmpStr; 00097 } 00098 00099 static bool iamAttendee( Attendee *attendee ) 00100 { 00101 // Check if I'm this attendee 00102 00103 bool iam = false; 00104 KEMailSettings settings; 00105 TQStringList profiles = settings.profiles(); 00106 for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { 00107 settings.setProfile( *it ); 00108 if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) { 00109 iam = true; 00110 break; 00111 } 00112 } 00113 return iam; 00114 } 00115 00116 static bool iamOrganizer( Incidence *incidence ) 00117 { 00118 // Check if I'm the organizer for this incidence 00119 00120 if ( !incidence ) { 00121 return false; 00122 } 00123 00124 bool iam = false; 00125 KEMailSettings settings; 00126 TQStringList profiles = settings.profiles(); 00127 for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { 00128 settings.setProfile( *it ); 00129 if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) { 00130 iam = true; 00131 break; 00132 } 00133 } 00134 return iam; 00135 } 00136 00137 static bool senderIsOrganizer( Incidence *incidence, const TQString &sender ) 00138 { 00139 // Check if the specified sender is the organizer 00140 00141 if ( !incidence || sender.isEmpty() ) { 00142 return true; 00143 } 00144 bool isorg = true; 00145 TQString senderName, senderEmail; 00146 if ( KPIM::getNameAndMail( sender, senderName, senderEmail ) ) { 00147 // for this heuristic, we say the sender is the organizer if either the name or the email match. 00148 if ( incidence->organizer().email() != senderEmail && 00149 incidence->organizer().name() != senderName ) { 00150 isorg = false; 00151 } 00152 } 00153 return isorg; 00154 } 00155 00156 static TQString firstAttendeeName( Incidence *incidence, const TQString &defName ) 00157 { 00158 TQString name; 00159 if ( !incidence ) { 00160 return name; 00161 } 00162 00163 Attendee::List attendees = incidence->attendees(); 00164 if( attendees.count() > 0 ) { 00165 Attendee *attendee = *attendees.begin(); 00166 name = attendee->name(); 00167 if ( name.isEmpty() ) { 00168 name = attendee->email(); 00169 } 00170 if ( name.isEmpty() ) { 00171 name = defName; 00172 } 00173 } 00174 return name; 00175 } 00176 00177 /******************************************************************* 00178 * Helper functions for the extensive display (display viewer) 00179 *******************************************************************/ 00180 00181 static TQString displayViewLinkPerson( const TQString& email, TQString name, TQString uid ) 00182 { 00183 // Make the search, if there is an email address to search on, 00184 // and either name or uid is missing 00185 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) { 00186 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); 00187 KABC::Addressee::List addressList = add_book->findByEmail( email ); 00188 if ( !addressList.isEmpty() ) { 00189 KABC::Addressee o = addressList.first(); 00190 if ( !o.isEmpty() && addressList.size() < 2 ) { 00191 if ( name.isEmpty() ) { 00192 // No name set, so use the one from the addressbook 00193 name = o.formattedName(); 00194 } 00195 uid = o.uid(); 00196 } else { 00197 // Email not found in the addressbook. Don't make a link 00198 uid = TQString(); 00199 } 00200 } 00201 } 00202 00203 // Show the attendee 00204 TQString tmpString; 00205 if ( !uid.isEmpty() ) { 00206 // There is a UID, so make a link to the addressbook 00207 if ( name.isEmpty() ) { 00208 // Use the email address for text 00209 tmpString += htmlAddLink( "uid:" + uid, email ); 00210 } else { 00211 tmpString += htmlAddLink( "uid:" + uid, name ); 00212 } 00213 } else { 00214 // No UID, just show some text 00215 tmpString += ( name.isEmpty() ? email : name ); 00216 } 00217 00218 // Make the mailto link 00219 if ( !email.isEmpty() ) { 00220 KURL mailto; 00221 mailto.setProtocol( "mailto" ); 00222 mailto.setPath( email ); 00223 const TQString iconPath = 00224 KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small ); 00225 tmpString += " " + 00226 htmlAddLink( mailto.url(), 00227 "<img valign=\"top\" src=\"" + iconPath + "\">" ); 00228 } 00229 00230 return tmpString; 00231 } 00232 00233 static TQString displayViewFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role ) 00234 { 00235 TQString tmpStr; 00236 Attendee::List::ConstIterator it; 00237 Attendee::List attendees = incidence->attendees(); 00238 00239 for ( it = attendees.begin(); it != attendees.end(); ++it ) { 00240 Attendee *a = *it; 00241 if ( a->role() != role ) { 00242 // skip this role 00243 continue; 00244 } 00245 if ( a->email() == incidence->organizer().email() ) { 00246 // skip attendee that is also the organizer 00247 continue; 00248 } 00249 tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid() ); 00250 if ( !a->delegator().isEmpty() ) { 00251 tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); 00252 } 00253 if ( !a->delegate().isEmpty() ) { 00254 tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); 00255 } 00256 tmpStr += "<br>"; 00257 } 00258 if ( tmpStr.endsWith( "<br>" ) ) { 00259 tmpStr.truncate( tmpStr.length() - 4 ); 00260 } 00261 return tmpStr; 00262 } 00263 00264 static TQString displayViewFormatAttendees( Incidence *incidence ) 00265 { 00266 TQString tmpStr, str; 00267 00268 // Add organizer link 00269 int attendeeCount = incidence->attendees().count(); 00270 if ( attendeeCount > 1 || 00271 ( attendeeCount == 1 && 00272 incidence->organizer().email() != incidence->attendees().first()->email() ) ) { 00273 tmpStr += "<tr>"; 00274 tmpStr += "<td><b>" + i18n( "Organizer:" ) + "</b></td>"; 00275 tmpStr += "<td>" + 00276 displayViewLinkPerson( incidence->organizer().email(), 00277 incidence->organizer().name(), 00278 TQString() ) + 00279 "</td>"; 00280 tmpStr += "</tr>"; 00281 } 00282 00283 // Add "chair" 00284 str = displayViewFormatAttendeeRoleList( incidence, Attendee::Chair ); 00285 if ( !str.isEmpty() ) { 00286 tmpStr += "<tr>"; 00287 tmpStr += "<td><b>" + i18n( "Chair:" ) + "</b></td>"; 00288 tmpStr += "<td>" + str + "</td>"; 00289 tmpStr += "</tr>"; 00290 } 00291 00292 // Add required participants 00293 str = displayViewFormatAttendeeRoleList( incidence, Attendee::ReqParticipant ); 00294 if ( !str.isEmpty() ) { 00295 tmpStr += "<tr>"; 00296 tmpStr += "<td><b>" + i18n( "Required Participants:" ) + "</b></td>"; 00297 tmpStr += "<td>" + str + "</td>"; 00298 tmpStr += "</tr>"; 00299 } 00300 00301 // Add optional participants 00302 str = displayViewFormatAttendeeRoleList( incidence, Attendee::OptParticipant ); 00303 if ( !str.isEmpty() ) { 00304 tmpStr += "<tr>"; 00305 tmpStr += "<td><b>" + i18n( "Optional Participants:" ) + "</b></td>"; 00306 tmpStr += "<td>" + str + "</td>"; 00307 tmpStr += "</tr>"; 00308 } 00309 00310 // Add observers 00311 str = displayViewFormatAttendeeRoleList( incidence, Attendee::NonParticipant ); 00312 if ( !str.isEmpty() ) { 00313 tmpStr += "<tr>"; 00314 tmpStr += "<td><b>" + i18n( "Observers:" ) + "</b></td>"; 00315 tmpStr += "<td>" + str + "</td>"; 00316 tmpStr += "</tr>"; 00317 } 00318 00319 return tmpStr; 00320 } 00321 00322 static TQString displayViewFormatAttachments( Incidence *incidence ) 00323 { 00324 TQString tmpStr; 00325 Attachment::List as = incidence->attachments(); 00326 Attachment::List::ConstIterator it; 00327 uint count = 0; 00328 for( it = as.begin(); it != as.end(); ++it ) { 00329 count++; 00330 if ( (*it)->isUri() ) { 00331 TQString name; 00332 if ( (*it)->uri().startsWith( "kmail:" ) ) { 00333 name = i18n( "Show mail" ); 00334 } else { 00335 if ( (*it)->label().isEmpty() ) { 00336 name = (*it)->uri(); 00337 } else { 00338 name = (*it)->label(); 00339 } 00340 } 00341 tmpStr += htmlAddLink( (*it)->uri(), name ); 00342 } else { 00343 tmpStr += htmlAddLink( "ATTACH:" + incidence->uid() + ':' + (*it)->label(), 00344 (*it)->label(), false ); 00345 } 00346 if ( count < as.count() ) { 00347 tmpStr += "<br>"; 00348 } 00349 } 00350 return tmpStr; 00351 } 00352 00353 static TQString displayViewFormatCategories( Incidence *incidence ) 00354 { 00355 // We do not use Incidence::categoriesStr() since it does not have whitespace 00356 return incidence->categories().join( ", " ); 00357 } 00358 00359 static TQString displayViewFormatCreationDate( Incidence *incidence ) 00360 { 00361 return i18n( "Creation date: %1" ). 00362 arg( IncidenceFormatter::dateTimeToString( incidence->created(), false, true ) ); 00363 } 00364 00365 static TQString displayViewFormatBirthday( Event *event ) 00366 { 00367 if ( !event ) { 00368 return TQString(); 00369 } 00370 if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) { 00371 return TQString(); 00372 } 00373 00374 TQString uid = event->customProperty("KABC","UID-1"); 00375 TQString name = event->customProperty("KABC","NAME-1"); 00376 TQString email= event->customProperty("KABC","EMAIL-1"); 00377 00378 TQString tmpStr = displayViewLinkPerson( email, name, uid ); 00379 00380 if ( event->customProperty( "KABC", "ANNIVERSARY") == "YES" ) { 00381 uid = event->customProperty("KABC","UID-2"); 00382 name = event->customProperty("KABC","NAME-2"); 00383 email= event->customProperty("KABC","EMAIL-2"); 00384 tmpStr += "<br>"; 00385 tmpStr += displayViewLinkPerson( email, name, uid ); 00386 } 00387 00388 return tmpStr; 00389 } 00390 00391 static TQString displayViewFormatHeader( Incidence *incidence ) 00392 { 00393 TQString tmpStr = "<table><tr>"; 00394 00395 // show icons 00396 { 00397 tmpStr += "<td>"; 00398 00399 if ( incidence->type() == "Event" ) { 00400 TQString iconPath; 00401 if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) { 00402 if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) { 00403 iconPath = 00404 KGlobal::iconLoader()->iconPath( "calendaranniversary", KIcon::Small ); 00405 } else { 00406 iconPath = KGlobal::iconLoader()->iconPath( "calendarbirthday", KIcon::Small ); 00407 } 00408 } else { 00409 iconPath = KGlobal::iconLoader()->iconPath( "appointment", KIcon::Small ); 00410 } 00411 tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">"; 00412 } 00413 if ( incidence->type() == "Todo" ) { 00414 tmpStr += "<img valign=\"top\" src=\"" + 00415 KGlobal::iconLoader()->iconPath( "todo", KIcon::Small ) + 00416 "\">"; 00417 } 00418 if ( incidence->type() == "Journal" ) { 00419 tmpStr += "<img valign=\"top\" src=\"" + 00420 KGlobal::iconLoader()->iconPath( "journal", KIcon::Small ) + 00421 "\">"; 00422 } 00423 if ( incidence->isAlarmEnabled() ) { 00424 tmpStr += "<img valign=\"top\" src=\"" + 00425 KGlobal::iconLoader()->iconPath( "bell", KIcon::Small ) + 00426 "\">"; 00427 } 00428 if ( incidence->doesRecur() ) { 00429 tmpStr += "<img valign=\"top\" src=\"" + 00430 KGlobal::iconLoader()->iconPath( "recur", KIcon::Small ) + 00431 "\">"; 00432 } 00433 if ( incidence->isReadOnly() ) { 00434 tmpStr += "<img valign=\"top\" src=\"" + 00435 KGlobal::iconLoader()->iconPath( "readonlyevent", KIcon::Small ) + 00436 "\">"; 00437 } 00438 00439 tmpStr += "</td>"; 00440 } 00441 00442 tmpStr += "<td>"; 00443 tmpStr += "<b><u>" + incidence->summary() + "</u></b>"; 00444 tmpStr += "</td>"; 00445 00446 tmpStr += "</tr></table>"; 00447 00448 return tmpStr; 00449 } 00450 00451 static TQString displayViewFormatEvent( Calendar *calendar, Event *event, 00452 const TQDate &date ) 00453 { 00454 if ( !event ) { 00455 return TQString(); 00456 } 00457 00458 TQString tmpStr = displayViewFormatHeader( event ); 00459 00460 tmpStr += "<table>"; 00461 tmpStr += "<col width=\"25%\"/>"; 00462 tmpStr += "<col width=\"75%\"/>"; 00463 00464 if ( calendar ) { 00465 TQString calStr = IncidenceFormatter::resourceString( calendar, event ); 00466 if ( !calStr.isEmpty() ) { 00467 tmpStr += "<tr>"; 00468 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>"; 00469 tmpStr += "<td>" + calStr + "</td>"; 00470 tmpStr += "</tr>"; 00471 } 00472 } 00473 00474 if ( !event->location().isEmpty() ) { 00475 tmpStr += "<tr>"; 00476 tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>"; 00477 tmpStr += "<td>" + event->location() + "</td>"; 00478 tmpStr += "</tr>"; 00479 } 00480 00481 TQDateTime startDt = event->dtStart(); 00482 TQDateTime endDt = event->dtEnd(); 00483 if ( event->doesRecur() ) { 00484 if ( date.isValid() ) { 00485 TQDateTime dt( date, TQTime( 0, 0, 0 ) ); 00486 int diffDays = startDt.daysTo( dt ); 00487 dt = dt.addSecs( -1 ); 00488 startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() ); 00489 if ( event->hasEndDate() ) { 00490 endDt = endDt.addDays( diffDays ); 00491 if ( startDt > endDt ) { 00492 startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() ); 00493 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) ); 00494 } 00495 } 00496 } 00497 } 00498 00499 tmpStr += "<tr>"; 00500 if ( event->doesFloat() ) { 00501 if ( event->isMultiDay() ) { 00502 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>"; 00503 tmpStr += "<td>" + 00504 i18n("<beginDate> - <endDate>","%1 - %2"). 00505 arg( IncidenceFormatter::dateToString( startDt, false ) ). 00506 arg( IncidenceFormatter::dateToString( endDt, false ) ) + 00507 "</td>"; 00508 } else { 00509 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>"; 00510 tmpStr += "<td>" + 00511 i18n("date as string","%1"). 00512 arg( IncidenceFormatter::dateToString( startDt, false ) ) + 00513 "</td>"; 00514 } 00515 } else { 00516 if ( event->isMultiDay() ) { 00517 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>"; 00518 tmpStr += "<td>" + 00519 i18n("<beginDate> - <endDate>","%1 - %2"). 00520 arg( IncidenceFormatter::dateToString( startDt, false ) ). 00521 arg( IncidenceFormatter::dateToString( endDt, false ) ) + 00522 "</td>"; 00523 } else { 00524 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>"; 00525 tmpStr += "<td>" + 00526 i18n("date as string","%1"). 00527 arg( IncidenceFormatter::dateToString( startDt, false ) ) + 00528 "</td>"; 00529 00530 tmpStr += "</tr><tr>"; 00531 tmpStr += "<td><b>" + i18n( "Time:" ) + "</b></td>"; 00532 if ( event->hasEndDate() && startDt != endDt ) { 00533 tmpStr += "<td>" + 00534 i18n("<beginTime> - <endTime>","%1 - %2"). 00535 arg( IncidenceFormatter::timeToString( startDt, true ) ). 00536 arg( IncidenceFormatter::timeToString( endDt, true ) ) + 00537 "</td>"; 00538 } else { 00539 tmpStr += "<td>" + 00540 IncidenceFormatter::timeToString( startDt, true ) + 00541 "</td>"; 00542 } 00543 } 00544 } 00545 tmpStr += "</tr>"; 00546 00547 TQString durStr = IncidenceFormatter::durationString( event ); 00548 if ( !durStr.isEmpty() ) { 00549 tmpStr += "<tr>"; 00550 tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>"; 00551 tmpStr += "<td>" + durStr + "</td>"; 00552 tmpStr += "</tr>"; 00553 } 00554 00555 if ( event->doesRecur() ) { 00556 tmpStr += "<tr>"; 00557 tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>"; 00558 tmpStr += "<td>" + 00559 IncidenceFormatter::recurrenceString( event ) + 00560 "</td>"; 00561 tmpStr += "</tr>"; 00562 } 00563 00564 if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) { 00565 tmpStr += "<tr>"; 00566 if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) { 00567 tmpStr += "<td><b>" + i18n( "Anniversary:" ) + "</b></td>"; 00568 } else { 00569 tmpStr += "<td><b>" + i18n( "Birthday:" ) + "</b></td>"; 00570 } 00571 tmpStr += "<td>" + displayViewFormatBirthday( event ) + "</td>"; 00572 tmpStr += "</tr>"; 00573 tmpStr += "</table>"; 00574 return tmpStr; 00575 } 00576 00577 if ( !event->description().isEmpty() ) { 00578 tmpStr += "<tr>"; 00579 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>"; 00580 tmpStr += "<td>" + event->description() + "</td>"; 00581 tmpStr += "</tr>"; 00582 } 00583 00584 // TODO: print comments? 00585 00586 int reminderCount = event->alarms().count(); 00587 if ( reminderCount > 0 && event->isAlarmEnabled() ) { 00588 tmpStr += "<tr>"; 00589 tmpStr += "<td><b>" + 00590 i18n( "Reminder:", "%n Reminders:", reminderCount ) + 00591 "</b></td>"; 00592 tmpStr += "<td>" + IncidenceFormatter::reminderStringList( event ).join( "<br>" ) + "</td>"; 00593 tmpStr += "</tr>"; 00594 } 00595 00596 tmpStr += displayViewFormatAttendees( event ); 00597 00598 int categoryCount = event->categories().count(); 00599 if ( categoryCount > 0 ) { 00600 tmpStr += "<tr>"; 00601 tmpStr += "<td><b>" + 00602 i18n( "Category:", "%n Categories:", categoryCount ) + 00603 "</b></td>"; 00604 tmpStr += "<td>" + displayViewFormatCategories( event ) + "</td>"; 00605 tmpStr += "</tr>"; 00606 } 00607 00608 int attachmentCount = event->attachments().count(); 00609 if ( attachmentCount > 0 ) { 00610 tmpStr += "<tr>"; 00611 tmpStr += "<td><b>" + 00612 i18n( "Attachment:", "%n Attachments:", attachmentCount ) + 00613 "</b></td>"; 00614 tmpStr += "<td>" + displayViewFormatAttachments( event ) + "</td>"; 00615 tmpStr += "</tr>"; 00616 } 00617 tmpStr += "</table>"; 00618 00619 tmpStr += "<em>" + displayViewFormatCreationDate( event ) + "</em>"; 00620 00621 return tmpStr; 00622 } 00623 00624 static TQString displayViewFormatTodo( Calendar *calendar, Todo *todo, 00625 const TQDate &date ) 00626 { 00627 if ( !todo ) { 00628 return TQString(); 00629 } 00630 00631 TQString tmpStr = displayViewFormatHeader( todo ); 00632 00633 tmpStr += "<table>"; 00634 tmpStr += "<col width=\"25%\"/>"; 00635 tmpStr += "<col width=\"75%\"/>"; 00636 00637 if ( calendar ) { 00638 TQString calStr = IncidenceFormatter::resourceString( calendar, todo ); 00639 if ( !calStr.isEmpty() ) { 00640 tmpStr += "<tr>"; 00641 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>"; 00642 tmpStr += "<td>" + calStr + "</td>"; 00643 tmpStr += "</tr>"; 00644 } 00645 } 00646 00647 if ( !todo->location().isEmpty() ) { 00648 tmpStr += "<tr>"; 00649 tmpStr += "<td><b>" + i18n( "Location:" ) + "</b></td>"; 00650 tmpStr += "<td>" + todo->location() + "</td>"; 00651 tmpStr += "</tr>"; 00652 } 00653 00654 if ( todo->hasStartDate() && todo->dtStart().isValid() ) { 00655 TQDateTime startDt = todo->dtStart(); 00656 if ( todo->doesRecur() ) { 00657 if ( date.isValid() ) { 00658 startDt.setDate( date ); 00659 } 00660 } 00661 tmpStr += "<tr>"; 00662 tmpStr += "<td><b>" + i18n( "Start:" ) + "</b></td>"; 00663 tmpStr += "<td>" + 00664 IncidenceFormatter::dateTimeToString( startDt, 00665 todo->doesFloat(), false ) + 00666 "</td>"; 00667 tmpStr += "</tr>"; 00668 } 00669 00670 if ( todo->hasDueDate() && todo->dtDue().isValid() ) { 00671 TQDateTime dueDt = todo->dtDue(); 00672 if ( todo->doesRecur() ) { 00673 if ( date.isValid() ) { 00674 TQDateTime dt( date, TQTime( 0, 0, 0 ) ); 00675 dt = dt.addSecs( -1 ); 00676 dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() ); 00677 } 00678 } 00679 tmpStr += "<tr>"; 00680 tmpStr += "<td><b>" + i18n( "Due:" ) + "</b></td>"; 00681 tmpStr += "<td>" + 00682 IncidenceFormatter::dateTimeToString( dueDt, 00683 todo->doesFloat(), false ) + 00684 "</td>"; 00685 tmpStr += "</tr>"; 00686 } 00687 00688 TQString durStr = IncidenceFormatter::durationString( todo ); 00689 if ( !durStr.isEmpty() ) { 00690 tmpStr += "<tr>"; 00691 tmpStr += "<td><b>" + i18n( "Duration:" ) + "</b></td>"; 00692 tmpStr += "<td>" + durStr + "</td>"; 00693 tmpStr += "</tr>"; 00694 } 00695 00696 if ( todo->doesRecur() ) { 00697 tmpStr += "<tr>"; 00698 tmpStr += "<td><b>" + i18n( "Recurrence:" ) + "</b></td>"; 00699 tmpStr += "<td>" + 00700 IncidenceFormatter::recurrenceString( todo ) + 00701 "</td>"; 00702 tmpStr += "</tr>"; 00703 } 00704 00705 if ( !todo->description().isEmpty() ) { 00706 tmpStr += "<tr>"; 00707 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>"; 00708 tmpStr += "<td>" + todo->description() + "</td>"; 00709 tmpStr += "</tr>"; 00710 } 00711 00712 // TODO: print comments? 00713 00714 int reminderCount = todo->alarms().count(); 00715 if ( reminderCount > 0 && todo->isAlarmEnabled() ) { 00716 tmpStr += "<tr>"; 00717 tmpStr += "<td><b>" + 00718 i18n( "Reminder:", "%n Reminders:", reminderCount ) + 00719 "</b></td>"; 00720 tmpStr += "<td>" + IncidenceFormatter::reminderStringList( todo ).join( "<br>" ) + "</td>"; 00721 tmpStr += "</tr>"; 00722 } 00723 00724 tmpStr += displayViewFormatAttendees( todo ); 00725 00726 int categoryCount = todo->categories().count(); 00727 if ( categoryCount > 0 ) { 00728 tmpStr += "<tr>"; 00729 tmpStr += "<td><b>" + 00730 i18n( "Category:", "%n Categories:", categoryCount ) + 00731 "</b></td>"; 00732 tmpStr += "<td>" + displayViewFormatCategories( todo ) + "</td>"; 00733 tmpStr += "</tr>"; 00734 } 00735 00736 if ( todo->priority() > 0 ) { 00737 tmpStr += "<tr>"; 00738 tmpStr += "<td><b>" + i18n( "Priority:" ) + "</b></td>"; 00739 tmpStr += "<td>"; 00740 tmpStr += TQString::number( todo->priority() ); 00741 tmpStr += "</td>"; 00742 tmpStr += "</tr>"; 00743 } 00744 00745 tmpStr += "<tr>"; 00746 if ( todo->isCompleted() ) { 00747 tmpStr += "<td><b>" + i18n( "Completed:" ) + "</b></td>"; 00748 tmpStr += "<td>"; 00749 tmpStr += todo->completedStr(); 00750 } else { 00751 tmpStr += "<td><b>" + i18n( "Percent Done:" ) + "</b></td>"; 00752 tmpStr += "<td>"; 00753 tmpStr += i18n( "%1%" ).arg( todo->percentComplete() ); 00754 } 00755 tmpStr += "</td>"; 00756 tmpStr += "</tr>"; 00757 00758 int attachmentCount = todo->attachments().count(); 00759 if ( attachmentCount > 0 ) { 00760 tmpStr += "<tr>"; 00761 tmpStr += "<td><b>" + 00762 i18n( "Attachment:", "Attachments:", attachmentCount ) + 00763 "</b></td>"; 00764 tmpStr += "<td>" + displayViewFormatAttachments( todo ) + "</td>"; 00765 tmpStr += "</tr>"; 00766 } 00767 00768 tmpStr += "</table>"; 00769 00770 tmpStr += "<em>" + displayViewFormatCreationDate( todo ) + "</em>"; 00771 00772 return tmpStr; 00773 } 00774 00775 static TQString displayViewFormatJournal( Calendar *calendar, Journal *journal ) 00776 { 00777 if ( !journal ) { 00778 return TQString(); 00779 } 00780 00781 TQString tmpStr = displayViewFormatHeader( journal ); 00782 00783 tmpStr += "<table>"; 00784 tmpStr += "<col width=\"25%\"/>"; 00785 tmpStr += "<col width=\"75%\"/>"; 00786 00787 if ( calendar ) { 00788 TQString calStr = IncidenceFormatter::resourceString( calendar, journal ); 00789 if ( !calStr.isEmpty() ) { 00790 tmpStr += "<tr>"; 00791 tmpStr += "<td><b>" + i18n( "Calendar:" ) + "</b></td>"; 00792 tmpStr += "<td>" + calStr + "</td>"; 00793 tmpStr += "</tr>"; 00794 } 00795 } 00796 00797 tmpStr += "<tr>"; 00798 tmpStr += "<td><b>" + i18n( "Date:" ) + "</b></td>"; 00799 tmpStr += "<td>" + 00800 IncidenceFormatter::dateToString( journal->dtStart(), false ) + 00801 "</td>"; 00802 tmpStr += "</tr>"; 00803 00804 if ( !journal->description().isEmpty() ) { 00805 tmpStr += "<tr>"; 00806 tmpStr += "<td><b>" + i18n( "Description:" ) + "</b></td>"; 00807 tmpStr += "<td>" + journal->description() + "</td>"; 00808 tmpStr += "</tr>"; 00809 } 00810 00811 int categoryCount = journal->categories().count(); 00812 if ( categoryCount > 0 ) { 00813 tmpStr += "<tr>"; 00814 tmpStr += "<td><b>" + 00815 i18n( "Category:", "%n Categories:", categoryCount ) + 00816 "</b></td>"; 00817 tmpStr += "<td>" + displayViewFormatCategories( journal ) + "</td>"; 00818 tmpStr += "</tr>"; 00819 } 00820 tmpStr += "</table>"; 00821 00822 tmpStr += "<em>" + displayViewFormatCreationDate( journal ) + "</em>"; 00823 00824 return tmpStr; 00825 } 00826 00827 static TQString displayViewFormatFreeBusy( Calendar * /*calendar*/, FreeBusy *fb ) 00828 { 00829 if ( !fb ) { 00830 return TQString(); 00831 } 00832 00833 TQString tmpStr = htmlAddTag( "h2", 00834 htmlAddTag( "b", 00835 i18n("Free/Busy information for %1"). 00836 arg( fb->organizer().fullName() ) ) ); 00837 00838 tmpStr += htmlAddTag( "h4", i18n("Busy times in date range %1 - %2:"). 00839 arg( IncidenceFormatter::dateToString( fb->dtStart(), true ) ). 00840 arg( IncidenceFormatter::dateToString( fb->dtEnd(), true ) ) ); 00841 00842 TQValueList<Period> periods = fb->busyPeriods(); 00843 00844 TQString text = htmlAddTag( "em", htmlAddTag( "b", i18n("Busy:") ) ); 00845 TQValueList<Period>::iterator it; 00846 for ( it = periods.begin(); it != periods.end(); ++it ) { 00847 Period per = *it; 00848 if ( per.hasDuration() ) { 00849 int dur = per.duration().asSeconds(); 00850 TQString cont; 00851 if ( dur >= 3600 ) { 00852 cont += i18n("1 hour ", "%n hours ", dur / 3600 ); 00853 dur %= 3600; 00854 } 00855 if ( dur >= 60 ) { 00856 cont += i18n("1 minute ", "%n minutes ", dur / 60); 00857 dur %= 60; 00858 } 00859 if ( dur > 0 ) { 00860 cont += i18n("1 second", "%n seconds", dur); 00861 } 00862 text += i18n("startDate for duration", "%1 for %2"). 00863 arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ). 00864 arg( cont ); 00865 text += "<br>"; 00866 } else { 00867 if ( per.start().date() == per.end().date() ) { 00868 text += i18n("date, fromTime - toTime ", "%1, %2 - %3"). 00869 arg( IncidenceFormatter::dateToString( per.start().date(), true ) ). 00870 arg( IncidenceFormatter::timeToString( per.start(), true ) ). 00871 arg( IncidenceFormatter::timeToString( per.end(), true ) ); 00872 } else { 00873 text += i18n("fromDateTime - toDateTime", "%1 - %2"). 00874 arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ). 00875 arg( IncidenceFormatter::dateTimeToString( per.end(), false, true ) ); 00876 } 00877 text += "<br>"; 00878 } 00879 } 00880 tmpStr += htmlAddTag( "p", text ); 00881 return tmpStr; 00882 } 00883 00884 class IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor 00885 { 00886 public: 00887 EventViewerVisitor() 00888 : mCalendar( 0 ), mResult( "" ) {} 00889 00890 bool act( Calendar *calendar, IncidenceBase *incidence, const TQDate &date ) 00891 { 00892 mCalendar = calendar; 00893 mDate = date; 00894 mResult = ""; 00895 return incidence->accept( *this ); 00896 } 00897 TQString result() const { return mResult; } 00898 00899 protected: 00900 bool visit( Event *event ) 00901 { 00902 mResult = displayViewFormatEvent( mCalendar, event, mDate ); 00903 return !mResult.isEmpty(); 00904 } 00905 bool visit( Todo *todo ) 00906 { 00907 mResult = displayViewFormatTodo( mCalendar, todo, mDate ); 00908 return !mResult.isEmpty(); 00909 } 00910 bool visit( Journal *journal ) 00911 { 00912 mResult = displayViewFormatJournal( mCalendar, journal ); 00913 return !mResult.isEmpty(); 00914 } 00915 bool visit( FreeBusy *fb ) 00916 { 00917 mResult = displayViewFormatFreeBusy( mCalendar, fb ); 00918 return !mResult.isEmpty(); 00919 } 00920 00921 protected: 00922 Calendar *mCalendar; 00923 TQDate mDate; 00924 TQString mResult; 00925 }; 00926 00927 TQString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence ) 00928 { 00929 return extensiveDisplayStr( 0, incidence, TQDate() ); 00930 } 00931 00932 TQString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar, 00933 IncidenceBase *incidence, 00934 const TQDate &date ) 00935 { 00936 if ( !incidence ) { 00937 return TQString(); 00938 } 00939 00940 EventViewerVisitor v; 00941 if ( v.act( calendar, incidence, date ) ) { 00942 return v.result(); 00943 } else { 00944 return TQString(); 00945 } 00946 } 00947 00948 /*********************************************************************** 00949 * Helper functions for the body part formatter of kmail (Invitations) 00950 ***********************************************************************/ 00951 00952 static TQString string2HTML( const TQString& str ) 00953 { 00954 return TQStyleSheet::convertFromPlainText(str, TQStyleSheetItem::WhiteSpaceNormal); 00955 } 00956 00957 static TQString cleanHtml( const TQString &html ) 00958 { 00959 TQRegExp rx( "<body[^>]*>(.*)</body>" ); 00960 rx.setCaseSensitive( false ); 00961 rx.search( html ); 00962 TQString body = rx.cap( 1 ); 00963 00964 return TQStyleSheet::escape( body.remove( TQRegExp( "<[^>]*>" ) ).stripWhiteSpace() ); 00965 } 00966 00967 static TQString eventStartTimeStr( Event *event ) 00968 { 00969 TQString tmp; 00970 if ( !event->doesFloat() ) { 00971 tmp = i18n( "%1: Start Date, %2: Start Time", "%1 %2" ). 00972 arg( IncidenceFormatter::dateToString( event->dtStart(), true ), 00973 IncidenceFormatter::timeToString( event->dtStart(), true ) ); 00974 } else { 00975 tmp = i18n( "%1: Start Date", "%1 (all day)" ). 00976 arg( IncidenceFormatter::dateToString( event->dtStart(), true ) ); 00977 } 00978 return tmp; 00979 } 00980 00981 static TQString eventEndTimeStr( Event *event ) 00982 { 00983 TQString tmp; 00984 if ( event->hasEndDate() && event->dtEnd().isValid() ) { 00985 if ( !event->doesFloat() ) { 00986 tmp = i18n( "%1: End Date, %2: End Time", "%1 %2" ). 00987 arg( IncidenceFormatter::dateToString( event->dtEnd(), true ), 00988 IncidenceFormatter::timeToString( event->dtEnd(), true ) ); 00989 } else { 00990 tmp = i18n( "%1: End Date", "%1 (all day)" ). 00991 arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) ); 00992 } 00993 } 00994 return tmp; 00995 } 00996 00997 static TQString invitationRow( const TQString &cell1, const TQString &cell2 ) 00998 { 00999 return "<tr><td>" + cell1 + "</td><td>" + cell2 + "</td></tr>\n"; 01000 } 01001 01002 static Attendee *findDelegatedFromMyAttendee( Incidence *incidence ) 01003 { 01004 // Return the first attendee that was delegated-from me 01005 01006 Attendee *attendee = 0; 01007 if ( !incidence ) { 01008 return attendee; 01009 } 01010 01011 KEMailSettings settings; 01012 TQStringList profiles = settings.profiles(); 01013 for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { 01014 settings.setProfile( *it ); 01015 01016 TQString delegatorName, delegatorEmail; 01017 Attendee::List attendees = incidence->attendees(); 01018 Attendee::List::ConstIterator it2; 01019 for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) { 01020 Attendee *a = *it2; 01021 KPIM::getNameAndMail( a->delegator(), delegatorName, delegatorEmail ); 01022 if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) { 01023 attendee = a; 01024 break; 01025 } 01026 } 01027 } 01028 return attendee; 01029 } 01030 01031 static Attendee *findMyAttendee( Incidence *incidence ) 01032 { 01033 // Return the attendee for the incidence that is probably me 01034 01035 Attendee *attendee = 0; 01036 if ( !incidence ) { 01037 return attendee; 01038 } 01039 01040 KEMailSettings settings; 01041 TQStringList profiles = settings.profiles(); 01042 for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { 01043 settings.setProfile( *it ); 01044 01045 Attendee::List attendees = incidence->attendees(); 01046 Attendee::List::ConstIterator it2; 01047 for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) { 01048 Attendee *a = *it2; 01049 if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) { 01050 attendee = a; 01051 break; 01052 } 01053 } 01054 } 01055 return attendee; 01056 } 01057 01058 static Attendee *findAttendee( Incidence *incidence, const TQString &email ) 01059 { 01060 // Search for an attendee by email address 01061 01062 Attendee *attendee = 0; 01063 if ( !incidence ) { 01064 return attendee; 01065 } 01066 01067 Attendee::List attendees = incidence->attendees(); 01068 Attendee::List::ConstIterator it; 01069 for ( it = attendees.begin(); it != attendees.end(); ++it ) { 01070 Attendee *a = *it; 01071 if ( email == a->email() ) { 01072 attendee = a; 01073 break; 01074 } 01075 } 01076 return attendee; 01077 } 01078 01079 static bool rsvpRequested( Incidence *incidence ) 01080 { 01081 if ( !incidence ) { 01082 return false; 01083 } 01084 01085 //use a heuristic to determine if a response is requested. 01086 01087 bool rsvp = true; // better send superfluously than not at all 01088 Attendee::List attendees = incidence->attendees(); 01089 Attendee::List::ConstIterator it; 01090 for ( it = attendees.begin(); it != attendees.end(); ++it ) { 01091 if ( it == attendees.begin() ) { 01092 rsvp = (*it)->RSVP(); // use what the first one has 01093 } else { 01094 if ( (*it)->RSVP() != rsvp ) { 01095 rsvp = true; // they differ, default 01096 break; 01097 } 01098 } 01099 } 01100 return rsvp; 01101 } 01102 01103 static TQString rsvpRequestedStr( bool rsvpRequested, const TQString &role ) 01104 { 01105 if ( rsvpRequested ) { 01106 if ( role.isEmpty() ) { 01107 return i18n( "Your response is requested" ); 01108 } else { 01109 return i18n( "Your response as <b>%1</b> is requested" ).arg( role ); 01110 } 01111 } else { 01112 if ( role.isEmpty() ) { 01113 return i18n( "No response is necessary" ); 01114 } else { 01115 return i18n( "No response as <b>%1</b> is necessary" ).arg( role ); 01116 } 01117 } 01118 } 01119 01120 static TQString myStatusStr( Incidence *incidence ) 01121 { 01122 TQString ret; 01123 Attendee *a = findMyAttendee( incidence ); 01124 if ( a && 01125 a->status() != Attendee::NeedsAction && a->status() != Attendee::Delegated ) { 01126 ret = i18n( "(<b>Note</b>: the Organizer preset your response to <b>%1</b>)" ). 01127 arg( Attendee::statusName( a->status() ) ); 01128 } 01129 return ret; 01130 } 01131 01132 static TQString invitationPerson( const TQString& email, TQString name, TQString uid ) 01133 { 01134 // Make the search, if there is an email address to search on, 01135 // and either name or uid is missing 01136 if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) { 01137 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); 01138 KABC::Addressee::List addressList = add_book->findByEmail( email ); 01139 if ( !addressList.isEmpty() ) { 01140 KABC::Addressee o = addressList.first(); 01141 if ( !o.isEmpty() && addressList.size() < 2 ) { 01142 if ( name.isEmpty() ) { 01143 // No name set, so use the one from the addressbook 01144 name = o.formattedName(); 01145 } 01146 uid = o.uid(); 01147 } else { 01148 // Email not found in the addressbook. Don't make a link 01149 uid = TQString(); 01150 } 01151 } 01152 } 01153 01154 // Show the attendee 01155 TQString tmpString; 01156 if ( !uid.isEmpty() ) { 01157 // There is a UID, so make a link to the addressbook 01158 if ( name.isEmpty() ) { 01159 // Use the email address for text 01160 tmpString += htmlAddLink( "uid:" + uid, email ); 01161 } else { 01162 tmpString += htmlAddLink( "uid:" + uid, name ); 01163 } 01164 } else { 01165 // No UID, just show some text 01166 tmpString += ( name.isEmpty() ? email : name ); 01167 } 01168 tmpString += '\n'; 01169 01170 // Make the mailto link 01171 if ( !email.isEmpty() ) { 01172 KCal::Person person( name, email ); 01173 KURL mailto; 01174 mailto.setProtocol( "mailto" ); 01175 mailto.setPath( person.fullName() ); 01176 const TQString iconPath = 01177 KGlobal::iconLoader()->iconPath( "mail_new", KIcon::Small ); 01178 tmpString += " " + 01179 htmlAddLink( mailto.url(), "<img src=\"" + iconPath + "\">" ) 01180 ; 01181 } 01182 tmpString += "\n"; 01183 01184 return tmpString; 01185 } 01186 01187 static TQString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode ) 01188 { 01189 // if description and comment -> use both 01190 // if description, but no comment -> use the desc as the comment (and no desc) 01191 // if comment, but no description -> use the comment and no description 01192 01193 TQString html; 01194 TQString descr; 01195 TQStringList comments; 01196 01197 if ( incidence->comments().isEmpty() ) { 01198 if ( !incidence->description().isEmpty() ) { 01199 // use description as comments 01200 if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) { 01201 comments << string2HTML( incidence->description() ); 01202 } else { 01203 comments << incidence->description(); 01204 if ( noHtmlMode ) { 01205 comments[0] = cleanHtml( comments[0] ); 01206 } 01207 comments[0] = htmlAddTag( "p", comments[0] ); 01208 } 01209 } 01210 //else desc and comments are empty 01211 } else { 01212 // non-empty comments 01213 TQStringList cl = incidence->comments(); 01214 uint i = 0; 01215 for( TQStringList::Iterator it=cl.begin(); it!=cl.end(); ++it ) { 01216 if ( !TQStyleSheet::mightBeRichText( *it ) ) { 01217 comments.append( string2HTML( *it ) ); 01218 } else { 01219 if ( noHtmlMode ) { 01220 comments.append( cleanHtml( "<body>" + (*it) + "</body>" ) ); 01221 } else { 01222 comments.append( *it ); 01223 } 01224 } 01225 i++; 01226 } 01227 if ( !incidence->description().isEmpty() ) { 01228 // use description too 01229 if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) { 01230 descr = string2HTML( incidence->description() ); 01231 } else { 01232 descr = incidence->description(); 01233 if ( noHtmlMode ) { 01234 descr = cleanHtml( descr ); 01235 } 01236 descr = htmlAddTag( "p", descr ); 01237 } 01238 } 01239 } 01240 01241 if( !descr.isEmpty() ) { 01242 html += "<p>"; 01243 html += "<table border=\"0\" style=\"margin-top:4px;\">"; 01244 html += "<tr><td><center>" + 01245 htmlAddTag( "u", i18n( "Description:" ) ) + 01246 "</center></td></tr>"; 01247 html += "<tr><td>" + descr + "</td></tr>"; 01248 html += "</table>"; 01249 } 01250 01251 if ( !comments.isEmpty() ) { 01252 html += "<p>"; 01253 html += "<table border=\"0\" style=\"margin-top:4px;\">"; 01254 html += "<tr><td><center>" + 01255 htmlAddTag( "u", i18n( "Comments:" ) ) + 01256 "</center></td></tr>"; 01257 html += "<tr><td>"; 01258 if ( comments.count() > 1 ) { 01259 html += "<ul>"; 01260 for ( uint i=0; i < comments.count(); ++i ) { 01261 html += "<li>" + comments[i] + "</li>"; 01262 } 01263 html += "</ul>"; 01264 } else { 01265 html += comments[0]; 01266 } 01267 html += "</td></tr>"; 01268 html += "</table>"; 01269 } 01270 return html; 01271 } 01272 01273 static TQString invitationDetailsEvent( Event* event, bool noHtmlMode ) 01274 { 01275 // Invitation details are formatted into an HTML table 01276 if ( !event ) { 01277 return TQString(); 01278 } 01279 01280 TQString sSummary = i18n( "Summary unspecified" ); 01281 if ( !event->summary().isEmpty() ) { 01282 if ( !TQStyleSheet::mightBeRichText( event->summary() ) ) { 01283 sSummary = TQStyleSheet::escape( event->summary() ); 01284 } else { 01285 sSummary = event->summary(); 01286 if ( noHtmlMode ) { 01287 sSummary = cleanHtml( sSummary ); 01288 } 01289 } 01290 } 01291 01292 TQString sLocation = i18n( "Location unspecified" ); 01293 if ( !event->location().isEmpty() ) { 01294 if ( !TQStyleSheet::mightBeRichText( event->location() ) ) { 01295 sLocation = TQStyleSheet::escape( event->location() ); 01296 } else { 01297 sLocation = event->location(); 01298 if ( noHtmlMode ) { 01299 sLocation = cleanHtml( sLocation ); 01300 } 01301 } 01302 } 01303 01304 TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" ); 01305 TQString html = TQString("<div dir=\"%1\">\n").arg(dir); 01306 01307 html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n"; 01308 01309 // Invitation summary & location rows 01310 html += invitationRow( i18n( "What:" ), sSummary ); 01311 html += invitationRow( i18n( "Where:" ), sLocation ); 01312 01313 if (event->doesRecur() == true) { 01314 html += invitationRow( i18n( "First Start Time:" ), eventStartTimeStr( event ) ); 01315 html += invitationRow( i18n( "First End Time:" ), eventEndTimeStr( event ) ); 01316 } 01317 // else { 01318 // If a 1 day event 01319 if ( event->dtStart().date() == event->dtEnd().date() ) { 01320 html += invitationRow( i18n( "Date:" ), 01321 IncidenceFormatter::dateToString( event->dtStart(), false ) ); 01322 if ( !event->doesFloat() ) { 01323 html += invitationRow( i18n( "Time:" ), 01324 IncidenceFormatter::timeToString( event->dtStart(), true ) + 01325 " - " + 01326 IncidenceFormatter::timeToString( event->dtEnd(), true ) ); 01327 } 01328 } else { 01329 html += invitationRow( i18n( "Starting date of an event", "From:" ), 01330 IncidenceFormatter::dateToString( event->dtStart(), false ) ); 01331 if ( !event->doesFloat() ) { 01332 html += invitationRow( i18n( "Starting time of an event", "At:" ), 01333 IncidenceFormatter::timeToString( event->dtStart(), true ) ); 01334 } 01335 if ( event->hasEndDate() ) { 01336 html += invitationRow( i18n( "Ending date of an event", "To:" ), 01337 IncidenceFormatter::dateToString( event->dtEnd(), false ) ); 01338 if ( !event->doesFloat() ) { 01339 html += invitationRow( i18n( "Starting time of an event", "At:" ), 01340 IncidenceFormatter::timeToString( event->dtEnd(), true ) ); 01341 } 01342 } else { 01343 html += invitationRow( i18n( "Ending date of an event", "To:" ), 01344 i18n( "no end date specified" ) ); 01345 } 01346 } 01347 // } 01348 01349 // Invitation Duration Row 01350 TQString durStr = IncidenceFormatter::durationString( event ); 01351 if ( !durStr.isEmpty() ) { 01352 html += invitationRow( i18n( "Duration:" ), durStr ); 01353 } 01354 01355 // Recurrence Information Rows 01356 if ( event->doesRecur() ) { 01357 Recurrence *recur = event->recurrence(); 01358 html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) ); 01359 01360 DateList exceptions = recur->exDates(); 01361 if (exceptions.isEmpty() == false) { 01362 bool isFirstExRow; 01363 isFirstExRow = true; 01364 DateList::ConstIterator ex_iter; 01365 for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) { 01366 if (isFirstExRow == true) { 01367 isFirstExRow = false; 01368 html += invitationRow( i18n("Cancelled on:"), KGlobal::locale()->formatDate(* ex_iter ) ); 01369 } 01370 else { 01371 html += invitationRow(" ", KGlobal::locale()->formatDate(* ex_iter ) ); 01372 } 01373 } 01374 } 01375 } 01376 01377 html += "</table>\n"; 01378 html += invitationsDetailsIncidence( event, noHtmlMode ); 01379 html += "</div>\n"; 01380 01381 return html; 01382 } 01383 01384 static TQString invitationDetailsTodo( Todo *todo, bool noHtmlMode ) 01385 { 01386 // Task details are formatted into an HTML table 01387 if ( !todo ) { 01388 return TQString(); 01389 } 01390 01391 TQString sSummary = i18n( "Summary unspecified" ); 01392 if ( !todo->summary().isEmpty() ) { 01393 if ( !TQStyleSheet::mightBeRichText( todo->summary() ) ) { 01394 sSummary = TQStyleSheet::escape( todo->summary() ); 01395 } else { 01396 sSummary = todo->summary(); 01397 if ( noHtmlMode ) { 01398 sSummary = cleanHtml( sSummary ); 01399 } 01400 } 01401 } 01402 01403 TQString sLocation = i18n( "Location unspecified" ); 01404 if ( !todo->location().isEmpty() ) { 01405 if ( !TQStyleSheet::mightBeRichText( todo->location() ) ) { 01406 sLocation = TQStyleSheet::escape( todo->location() ); 01407 } else { 01408 sLocation = todo->location(); 01409 if ( noHtmlMode ) { 01410 sLocation = cleanHtml( sLocation ); 01411 } 01412 } 01413 } 01414 01415 TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" ); 01416 TQString html = TQString("<div dir=\"%1\">\n").arg(dir); 01417 html += "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n"; 01418 01419 // Invitation summary & location rows 01420 html += invitationRow( i18n( "What:" ), sSummary ); 01421 html += invitationRow( i18n( "Where:" ), sLocation ); 01422 01423 if ( todo->hasStartDate() && todo->dtStart().isValid() ) { 01424 html += invitationRow( i18n( "Start Date:" ), 01425 IncidenceFormatter::dateToString( todo->dtStart(), false ) ); 01426 if ( !todo->doesFloat() ) { 01427 html += invitationRow( i18n( "Start Time:" ), 01428 IncidenceFormatter::timeToString( todo->dtStart(), false ) ); 01429 } 01430 } 01431 if ( todo->hasDueDate() && todo->dtDue().isValid() ) { 01432 html += invitationRow( i18n( "Due Date:" ), 01433 IncidenceFormatter::dateToString( todo->dtDue(), false ) ); 01434 if ( !todo->doesFloat() ) { 01435 html += invitationRow( i18n( "Due Time:" ), 01436 IncidenceFormatter::timeToString( todo->dtDue(), false ) ); 01437 } 01438 01439 } else { 01440 html += invitationRow( i18n( "Due Date:" ), i18n( "Due Date: None", "None" ) ); 01441 } 01442 01443 html += "</table></div>\n"; 01444 html += invitationsDetailsIncidence( todo, noHtmlMode ); 01445 01446 return html; 01447 } 01448 01449 static TQString invitationDetailsJournal( Journal *journal, bool noHtmlMode ) 01450 { 01451 if ( !journal ) { 01452 return TQString(); 01453 } 01454 01455 TQString sSummary = i18n( "Summary unspecified" ); 01456 TQString sDescr = i18n( "Description unspecified" ); 01457 if ( ! journal->summary().isEmpty() ) { 01458 sSummary = journal->summary(); 01459 if ( noHtmlMode ) { 01460 sSummary = cleanHtml( sSummary ); 01461 } 01462 } 01463 if ( ! journal->description().isEmpty() ) { 01464 sDescr = journal->description(); 01465 if ( noHtmlMode ) { 01466 sDescr = cleanHtml( sDescr ); 01467 } 01468 } 01469 TQString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" ); 01470 html += invitationRow( i18n( "Summary:" ), sSummary ); 01471 html += invitationRow( i18n( "Date:" ), 01472 IncidenceFormatter::dateToString( journal->dtStart(), false ) ); 01473 html += invitationRow( i18n( "Description:" ), sDescr ); 01474 html += "</table>\n"; 01475 html += invitationsDetailsIncidence( journal, noHtmlMode ); 01476 01477 return html; 01478 } 01479 01480 static TQString invitationDetailsFreeBusy( FreeBusy *fb, bool /*noHtmlMode*/ ) 01481 { 01482 if ( !fb ) 01483 return TQString(); 01484 TQString html( "<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\">\n" ); 01485 01486 html += invitationRow( i18n("Person:"), fb->organizer().fullName() ); 01487 html += invitationRow( i18n("Start date:"), 01488 IncidenceFormatter::dateToString( fb->dtStart(), true ) ); 01489 html += invitationRow( i18n("End date:"), 01490 KGlobal::locale()->formatDate( fb->dtEnd().date(), true ) ); 01491 html += "<tr><td colspan=2><hr></td></tr>\n"; 01492 html += "<tr><td colspan=2>Busy periods given in this free/busy object:</td></tr>\n"; 01493 01494 TQValueList<Period> periods = fb->busyPeriods(); 01495 01496 TQValueList<Period>::iterator it; 01497 for ( it = periods.begin(); it != periods.end(); ++it ) { 01498 Period per = *it; 01499 if ( per.hasDuration() ) { 01500 int dur = per.duration().asSeconds(); 01501 TQString cont; 01502 if ( dur >= 3600 ) { 01503 cont += i18n("1 hour ", "%n hours ", dur / 3600); 01504 dur %= 3600; 01505 } 01506 if ( dur >= 60 ) { 01507 cont += i18n("1 minute", "%n minutes ", dur / 60); 01508 dur %= 60; 01509 } 01510 if ( dur > 0 ) { 01511 cont += i18n("1 second", "%n seconds", dur); 01512 } 01513 html += invitationRow( TQString(), i18n("startDate for duration", "%1 for %2") 01514 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) ) 01515 .arg(cont) ); 01516 } else { 01517 TQString cont; 01518 if ( per.start().date() == per.end().date() ) { 01519 cont = i18n("date, fromTime - toTime ", "%1, %2 - %3") 01520 .arg( KGlobal::locale()->formatDate( per.start().date() ) ) 01521 .arg( KGlobal::locale()->formatTime( per.start().time() ) ) 01522 .arg( KGlobal::locale()->formatTime( per.end().time() ) ); 01523 } else { 01524 cont = i18n("fromDateTime - toDateTime", "%1 - %2") 01525 .arg( KGlobal::locale()->formatDateTime( per.start(), false ) ) 01526 .arg( KGlobal::locale()->formatDateTime( per.end(), false ) ); 01527 } 01528 01529 html += invitationRow( TQString(), cont ); 01530 } 01531 } 01532 01533 html += "</table>\n"; 01534 return html; 01535 } 01536 01537 static bool replyMeansCounter( Incidence */*incidence*/ ) 01538 { 01539 return false; 01554 } 01555 01556 static TQString invitationHeaderEvent( Event *event, Incidence *existingIncidence, 01557 ScheduleMessage *msg, const TQString &sender ) 01558 { 01559 if ( !msg || !event ) 01560 return TQString(); 01561 01562 switch ( msg->method() ) { 01563 case Scheduler::Publish: 01564 return i18n( "This invitation has been published" ); 01565 case Scheduler::Request: 01566 if ( existingIncidence && event->revision() > 0 ) { 01567 return i18n( "This invitation has been updated by the organizer %1" ). 01568 arg( event->organizer().fullName() ); 01569 } 01570 if ( iamOrganizer( event ) ) { 01571 return i18n( "I created this invitation" ); 01572 } else { 01573 TQString orgStr; 01574 if ( !event->organizer().fullName().isEmpty() ) { 01575 orgStr = event->organizer().fullName(); 01576 } else if ( !event->organizer().email().isEmpty() ) { 01577 orgStr = event->organizer().email(); 01578 } 01579 if ( senderIsOrganizer( event, sender ) ) { 01580 if ( !orgStr.isEmpty() ) { 01581 return i18n( "You received an invitation from %1" ).arg( orgStr ); 01582 } else { 01583 return i18n( "You received an invitation" ); 01584 } 01585 } else { 01586 if ( !orgStr.isEmpty() ) { 01587 return i18n( "You received an invitation from %1 as a representative of %2" ). 01588 arg( sender, orgStr ); 01589 } else { 01590 return i18n( "You received an invitation from %1 as the organizer's representative" ). 01591 arg( sender ); 01592 } 01593 } 01594 } 01595 case Scheduler::Refresh: 01596 return i18n( "This invitation was refreshed" ); 01597 case Scheduler::Cancel: 01598 return i18n( "This invitation has been canceled" ); 01599 case Scheduler::Add: 01600 return i18n( "Addition to the invitation" ); 01601 case Scheduler::Reply: 01602 { 01603 if ( replyMeansCounter( event ) ) { 01604 return i18n( "%1 makes this counter proposal" ). 01605 arg( firstAttendeeName( event, i18n( "Sender" ) ) ); 01606 } 01607 01608 Attendee::List attendees = event->attendees(); 01609 if( attendees.count() == 0 ) { 01610 kdDebug(5850) << "No attendees in the iCal reply!" << endl; 01611 return TQString(); 01612 } 01613 if( attendees.count() != 1 ) { 01614 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " 01615 << "but is " << attendees.count() << endl; 01616 } 01617 TQString attendeeName = firstAttendeeName( event, i18n( "Sender" ) ); 01618 01619 TQString delegatorName, dummy; 01620 Attendee* attendee = *attendees.begin(); 01621 KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy ); 01622 if ( delegatorName.isEmpty() ) { 01623 delegatorName = attendee->delegator(); 01624 } 01625 01626 switch( attendee->status() ) { 01627 case Attendee::NeedsAction: 01628 return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName ); 01629 case Attendee::Accepted: 01630 if ( event->revision() > 0 ) { 01631 if ( !sender.isEmpty() ) { 01632 return i18n( "This invitation has been updated by attendee %1" ).arg( sender ); 01633 } else { 01634 return i18n( "This invitation has been updated by an attendee" ); 01635 } 01636 } else { 01637 if ( delegatorName.isEmpty() ) { 01638 return i18n( "%1 accepts this invitation" ).arg( attendeeName ); 01639 } else { 01640 return i18n( "%1 accepts this invitation on behalf of %2" ). 01641 arg( attendeeName ).arg( delegatorName ); 01642 } 01643 } 01644 case Attendee::Tentative: 01645 if ( delegatorName.isEmpty() ) { 01646 return i18n( "%1 tentatively accepts this invitation" ). 01647 arg( attendeeName ); 01648 } else { 01649 return i18n( "%1 tentatively accepts this invitation on behalf of %2" ). 01650 arg( attendeeName ).arg( delegatorName ); 01651 } 01652 case Attendee::Declined: 01653 if ( delegatorName.isEmpty() ) { 01654 return i18n( "%1 declines this invitation" ).arg( attendeeName ); 01655 } else { 01656 return i18n( "%1 declines this invitation on behalf of %2" ). 01657 arg( attendeeName ).arg( delegatorName ); 01658 } 01659 case Attendee::Delegated: { 01660 TQString delegate, dummy; 01661 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy ); 01662 if ( delegate.isEmpty() ) { 01663 delegate = attendee->delegate(); 01664 } 01665 if ( !delegate.isEmpty() ) { 01666 return i18n( "%1 has delegated this invitation to %2" ). 01667 arg( attendeeName ) .arg( delegate ); 01668 } else { 01669 return i18n( "%1 has delegated this invitation" ).arg( attendeeName ); 01670 } 01671 } 01672 case Attendee::Completed: 01673 return i18n( "This invitation is now completed" ); 01674 case Attendee::InProcess: 01675 return i18n( "%1 is still processing the invitation" ). 01676 arg( attendeeName ); 01677 default: 01678 return i18n( "Unknown response to this invitation" ); 01679 } 01680 break; 01681 } 01682 01683 case Scheduler::Counter: 01684 return i18n( "%1 makes this counter proposal" ). 01685 arg( firstAttendeeName( event, i18n( "Sender" ) ) ); 01686 01687 case Scheduler::Declinecounter: 01688 return i18n( "%1 declines the counter proposal" ). 01689 arg( firstAttendeeName( event, i18n( "Sender" ) ) ); 01690 01691 case Scheduler::NoMethod: 01692 return i18n("Error: iMIP message with unknown method: '%1'"). 01693 arg( msg->method() ); 01694 } 01695 return TQString(); 01696 } 01697 01698 static TQString invitationHeaderTodo( Todo *todo, Incidence *existingIncidence, 01699 ScheduleMessage *msg, const TQString &sender ) 01700 { 01701 if ( !msg || !todo ) { 01702 return TQString(); 01703 } 01704 01705 switch ( msg->method() ) { 01706 case Scheduler::Publish: 01707 return i18n("This task has been published"); 01708 case Scheduler::Request: 01709 if ( existingIncidence && todo->revision() > 0 ) { 01710 return i18n( "This task has been updated by the organizer %1" ). 01711 arg( todo->organizer().fullName() ); 01712 } else { 01713 if ( iamOrganizer( todo ) ) { 01714 return i18n( "I created this task" ); 01715 } else { 01716 TQString orgStr; 01717 if ( !todo->organizer().fullName().isEmpty() ) { 01718 orgStr = todo->organizer().fullName(); 01719 } else if ( !todo->organizer().email().isEmpty() ) { 01720 orgStr = todo->organizer().email(); 01721 } 01722 if ( senderIsOrganizer( todo, sender ) ) { 01723 if ( !orgStr.isEmpty() ) { 01724 return i18n( "You have been assigned this task by %1" ).arg( orgStr ); 01725 } else { 01726 return i18n( "You have been assigned this task" ); 01727 } 01728 } else { 01729 if ( !orgStr.isEmpty() ) { 01730 return i18n( "You have been assigned this task by %1 as a representative of %2" ). 01731 arg( sender, orgStr ); 01732 } else { 01733 return i18n( "You have been assigned this task by %1 as the organizer's representative" ). 01734 arg( sender ); 01735 } 01736 } 01737 } 01738 } 01739 case Scheduler::Refresh: 01740 return i18n( "This task was refreshed" ); 01741 case Scheduler::Cancel: 01742 return i18n( "This task was canceled" ); 01743 case Scheduler::Add: 01744 return i18n( "Addition to the task" ); 01745 case Scheduler::Reply: 01746 { 01747 if ( replyMeansCounter( todo ) ) { 01748 return i18n( "%1 makes this counter proposal" ). 01749 arg( firstAttendeeName( todo, i18n( "Sender" ) ) ); 01750 } 01751 01752 Attendee::List attendees = todo->attendees(); 01753 if( attendees.count() == 0 ) { 01754 kdDebug(5850) << "No attendees in the iCal reply!" << endl; 01755 return TQString(); 01756 } 01757 if( attendees.count() != 1 ) { 01758 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " 01759 << "but is " << attendees.count() << endl; 01760 } 01761 TQString attendeeName = firstAttendeeName( todo, i18n( "Sender" ) ); 01762 01763 TQString delegatorName, dummy; 01764 Attendee* attendee = *attendees.begin(); 01765 KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy ); 01766 if ( delegatorName.isEmpty() ) { 01767 delegatorName = attendee->delegator(); 01768 } 01769 01770 switch( attendee->status() ) { 01771 case Attendee::NeedsAction: 01772 return i18n( "%1 indicates this task assignment still needs some action" ).arg( attendeeName ); 01773 case Attendee::Accepted: 01774 if ( todo->revision() > 0 ) { 01775 if ( !sender.isEmpty() ) { 01776 if ( todo->isCompleted() ) { 01777 return i18n( "This task has been completed by assignee %1" ).arg( sender ); 01778 } else { 01779 return i18n( "This task has been updated by assignee %1" ).arg( sender ); 01780 } 01781 } else { 01782 if ( todo->isCompleted() ) { 01783 return i18n( "This task has been completed by an assignee" ); 01784 } else { 01785 return i18n( "This task has been updated by an assignee" ); 01786 } 01787 } 01788 } else { 01789 if ( delegatorName.isEmpty() ) { 01790 return i18n( "%1 accepts this task" ).arg( attendeeName ); 01791 } else { 01792 return i18n( "%1 accepts this task on behalf of %2" ). 01793 arg( attendeeName ).arg( delegatorName ); 01794 } 01795 } 01796 case Attendee::Tentative: 01797 if ( delegatorName.isEmpty() ) { 01798 return i18n( "%1 tentatively accepts this task" ). 01799 arg( attendeeName ); 01800 } else { 01801 return i18n( "%1 tentatively accepts this task on behalf of %2" ). 01802 arg( attendeeName ).arg( delegatorName ); 01803 } 01804 case Attendee::Declined: 01805 if ( delegatorName.isEmpty() ) { 01806 return i18n( "%1 declines this task" ).arg( attendeeName ); 01807 } else { 01808 return i18n( "%1 declines this task on behalf of %2" ). 01809 arg( attendeeName ).arg( delegatorName ); 01810 } 01811 case Attendee::Delegated: { 01812 TQString delegate, dummy; 01813 KPIM::getNameAndMail( attendee->delegate(), delegate, dummy ); 01814 if ( delegate.isEmpty() ) { 01815 delegate = attendee->delegate(); 01816 } 01817 if ( !delegate.isEmpty() ) { 01818 return i18n( "%1 has delegated this request for the task to %2" ). 01819 arg( attendeeName ).arg( delegate ); 01820 } else { 01821 return i18n( "%1 has delegated this request for the task" ). 01822 arg( attendeeName ); 01823 } 01824 } 01825 case Attendee::Completed: 01826 return i18n( "The request for this task is now completed" ); 01827 case Attendee::InProcess: 01828 return i18n( "%1 is still processing the task" ). 01829 arg( attendeeName ); 01830 default: 01831 return i18n( "Unknown response to this task" ); 01832 } 01833 break; 01834 } 01835 01836 case Scheduler::Counter: 01837 return i18n( "%1 makes this counter proposal" ). 01838 arg( firstAttendeeName( todo, i18n( "Sender" ) ) ); 01839 01840 case Scheduler::Declinecounter: 01841 return i18n( "%1 declines the counter proposal" ). 01842 arg( firstAttendeeName( todo, i18n( "Sender" ) ) ); 01843 01844 case Scheduler::NoMethod: 01845 return i18n( "Error: iMIP message with unknown method: '%1'" ). 01846 arg( msg->method() ); 01847 } 01848 return TQString(); 01849 } 01850 01851 static TQString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg ) 01852 { 01853 if ( !msg || !journal ) { 01854 return TQString(); 01855 } 01856 01857 switch ( msg->method() ) { 01858 case Scheduler::Publish: 01859 return i18n("This journal has been published"); 01860 case Scheduler::Request: 01861 return i18n( "You have been assigned this journal" ); 01862 case Scheduler::Refresh: 01863 return i18n( "This journal was refreshed" ); 01864 case Scheduler::Cancel: 01865 return i18n( "This journal was canceled" ); 01866 case Scheduler::Add: 01867 return i18n( "Addition to the journal" ); 01868 case Scheduler::Reply: 01869 { 01870 if ( replyMeansCounter( journal ) ) { 01871 return i18n( "Sender makes this counter proposal" ); 01872 } 01873 01874 Attendee::List attendees = journal->attendees(); 01875 if( attendees.count() == 0 ) { 01876 kdDebug(5850) << "No attendees in the iCal reply!" << endl; 01877 return TQString(); 01878 } 01879 if( attendees.count() != 1 ) { 01880 kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " 01881 << "but is " << attendees.count() << endl; 01882 } 01883 Attendee* attendee = *attendees.begin(); 01884 01885 switch( attendee->status() ) { 01886 case Attendee::NeedsAction: 01887 return i18n( "Sender indicates this journal assignment still needs some action" ); 01888 case Attendee::Accepted: 01889 return i18n( "Sender accepts this journal" ); 01890 case Attendee::Tentative: 01891 return i18n( "Sender tentatively accepts this journal" ); 01892 case Attendee::Declined: 01893 return i18n( "Sender declines this journal" ); 01894 case Attendee::Delegated: 01895 return i18n( "Sender has delegated this request for the journal" ); 01896 case Attendee::Completed: 01897 return i18n( "The request for this journal is now completed" ); 01898 case Attendee::InProcess: 01899 return i18n( "Sender is still processing the invitation" ); 01900 default: 01901 return i18n( "Unknown response to this journal" ); 01902 } 01903 break; 01904 } 01905 case Scheduler::Counter: 01906 return i18n( "Sender makes this counter proposal" ); 01907 case Scheduler::Declinecounter: 01908 return i18n( "Sender declines the counter proposal" ); 01909 case Scheduler::NoMethod: 01910 return i18n("Error: iMIP message with unknown method: '%1'"). 01911 arg( msg->method() ); 01912 } 01913 return TQString(); 01914 } 01915 01916 static TQString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg ) 01917 { 01918 if ( !msg || !fb ) { 01919 return TQString(); 01920 } 01921 01922 switch ( msg->method() ) { 01923 case Scheduler::Publish: 01924 return i18n("This free/busy list has been published"); 01925 case Scheduler::Request: 01926 return i18n( "The free/busy list has been requested" ); 01927 case Scheduler::Refresh: 01928 return i18n( "This free/busy list was refreshed" ); 01929 case Scheduler::Cancel: 01930 return i18n( "This free/busy list was canceled" ); 01931 case Scheduler::Add: 01932 return i18n( "Addition to the free/busy list" ); 01933 case Scheduler::NoMethod: 01934 default: 01935 return i18n("Error: Free/Busy iMIP message with unknown method: '%1'"). 01936 arg( msg->method() ); 01937 } 01938 } 01939 01940 static TQString invitationAttendees( Incidence *incidence ) 01941 { 01942 TQString tmpStr; 01943 if ( !incidence ) { 01944 return tmpStr; 01945 } 01946 01947 if ( incidence->type() == "Todo" ) { 01948 tmpStr += htmlAddTag( "u", i18n( "Assignees" ) ); 01949 } else { 01950 tmpStr += htmlAddTag( "u", i18n( "Attendees" ) ); 01951 } 01952 tmpStr += "<br/>"; 01953 01954 int count=0; 01955 Attendee::List attendees = incidence->attendees(); 01956 if ( !attendees.isEmpty() ) { 01957 01958 Attendee::List::ConstIterator it; 01959 for( it = attendees.begin(); it != attendees.end(); ++it ) { 01960 Attendee *a = *it; 01961 if ( !iamAttendee( a ) ) { 01962 count++; 01963 if ( count == 1 ) { 01964 tmpStr += "<table border=\"1\" cellpadding=\"1\" cellspacing=\"0\" columns=\"2\">"; 01965 } 01966 tmpStr += "<tr>"; 01967 tmpStr += "<td>"; 01968 tmpStr += invitationPerson( a->email(), a->name(), TQString() ); 01969 if ( !a->delegator().isEmpty() ) { 01970 tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); 01971 } 01972 if ( !a->delegate().isEmpty() ) { 01973 tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); 01974 } 01975 tmpStr += "</td>"; 01976 tmpStr += "<td>" + a->statusStr() + "</td>"; 01977 tmpStr += "</tr>"; 01978 } 01979 } 01980 } 01981 if ( count ) { 01982 tmpStr += "</table>"; 01983 } else { 01984 tmpStr += "<i>" + i18n( "No attendee", "None" ) + "</i>"; 01985 } 01986 01987 return tmpStr; 01988 } 01989 01990 static TQString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence ) 01991 { 01992 TQString tmpStr; 01993 if ( !incidence ) { 01994 return tmpStr; 01995 } 01996 01997 Attachment::List attachments = incidence->attachments(); 01998 if ( !attachments.isEmpty() ) { 01999 tmpStr += i18n( "Attached Documents:" ) + "<ol>"; 02000 02001 Attachment::List::ConstIterator it; 02002 for( it = attachments.begin(); it != attachments.end(); ++it ) { 02003 Attachment *a = *it; 02004 tmpStr += "<li>"; 02005 // Attachment icon 02006 KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() ); 02007 const TQString iconStr = mimeType ? mimeType->icon( a->uri(), false ) : TQString( "application-octet-stream" ); 02008 const TQString iconPath = KGlobal::iconLoader()->iconPath( iconStr, KIcon::Small ); 02009 if ( !iconPath.isEmpty() ) { 02010 tmpStr += "<img valign=\"top\" src=\"" + iconPath + "\">"; 02011 } 02012 tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() ); 02013 tmpStr += "</li>"; 02014 } 02015 tmpStr += "</ol>"; 02016 } 02017 02018 return tmpStr; 02019 } 02020 02021 class IncidenceFormatter::ScheduleMessageVisitor 02022 : public IncidenceBase::Visitor 02023 { 02024 public: 02025 ScheduleMessageVisitor() : mExistingIncidence( 0 ), mMessage( 0 ) { mResult = ""; } 02026 bool act( IncidenceBase *incidence, Incidence *existingIncidence, ScheduleMessage *msg, 02027 const TQString &sender ) 02028 { 02029 mExistingIncidence = existingIncidence; 02030 mMessage = msg; 02031 mSender = sender; 02032 return incidence->accept( *this ); 02033 } 02034 TQString result() const { return mResult; } 02035 02036 protected: 02037 TQString mResult; 02038 Incidence *mExistingIncidence; 02039 ScheduleMessage *mMessage; 02040 TQString mSender; 02041 }; 02042 02043 class IncidenceFormatter::InvitationHeaderVisitor 02044 : public IncidenceFormatter::ScheduleMessageVisitor 02045 { 02046 protected: 02047 bool visit( Event *event ) 02048 { 02049 mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender ); 02050 return !mResult.isEmpty(); 02051 } 02052 bool visit( Todo *todo ) 02053 { 02054 mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender ); 02055 return !mResult.isEmpty(); 02056 } 02057 bool visit( Journal *journal ) 02058 { 02059 mResult = invitationHeaderJournal( journal, mMessage ); 02060 return !mResult.isEmpty(); 02061 } 02062 bool visit( FreeBusy *fb ) 02063 { 02064 mResult = invitationHeaderFreeBusy( fb, mMessage ); 02065 return !mResult.isEmpty(); 02066 } 02067 }; 02068 02069 class IncidenceFormatter::InvitationBodyVisitor 02070 : public IncidenceFormatter::ScheduleMessageVisitor 02071 { 02072 public: 02073 InvitationBodyVisitor( bool noHtmlMode ) 02074 : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ) {} 02075 02076 protected: 02077 bool visit( Event *event ) 02078 { 02079 mResult = invitationDetailsEvent( event, mNoHtmlMode ); 02080 return !mResult.isEmpty(); 02081 } 02082 bool visit( Todo *todo ) 02083 { 02084 mResult = invitationDetailsTodo( todo, mNoHtmlMode ); 02085 return !mResult.isEmpty(); 02086 } 02087 bool visit( Journal *journal ) 02088 { 02089 mResult = invitationDetailsJournal( journal, mNoHtmlMode ); 02090 return !mResult.isEmpty(); 02091 } 02092 bool visit( FreeBusy *fb ) 02093 { 02094 mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode ); 02095 return !mResult.isEmpty(); 02096 } 02097 02098 private: 02099 bool mNoHtmlMode; 02100 }; 02101 02102 class IncidenceFormatter::IncidenceCompareVisitor 02103 : public IncidenceBase::Visitor 02104 { 02105 public: 02106 IncidenceCompareVisitor() : mExistingIncidence(0) {} 02107 bool act( IncidenceBase *incidence, Incidence *existingIncidence, int method ) 02108 { 02109 Incidence *inc = dynamic_cast<Incidence*>( incidence ); 02110 if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() ) 02111 return false; 02112 mExistingIncidence = existingIncidence; 02113 mMethod = method; 02114 return incidence->accept( *this ); 02115 } 02116 02117 TQString result() const 02118 { 02119 if ( mChanges.isEmpty() ) { 02120 return TQString(); 02121 } 02122 TQString html = "<div align=\"left\"><ul><li>"; 02123 html += mChanges.join( "</li><li>" ); 02124 html += "</li><ul></div>"; 02125 return html; 02126 } 02127 02128 protected: 02129 bool visit( Event *event ) 02130 { 02131 compareEvents( event, dynamic_cast<Event*>( mExistingIncidence ) ); 02132 compareIncidences( event, mExistingIncidence, mMethod ); 02133 return !mChanges.isEmpty(); 02134 } 02135 bool visit( Todo *todo ) 02136 { 02137 compareTodos( todo, dynamic_cast<Todo*>( mExistingIncidence ) ); 02138 compareIncidences( todo, mExistingIncidence, mMethod ); 02139 return !mChanges.isEmpty(); 02140 } 02141 bool visit( Journal *journal ) 02142 { 02143 compareIncidences( journal, mExistingIncidence, mMethod ); 02144 return !mChanges.isEmpty(); 02145 } 02146 bool visit( FreeBusy *fb ) 02147 { 02148 Q_UNUSED( fb ); 02149 return !mChanges.isEmpty(); 02150 } 02151 02152 private: 02153 void compareEvents( Event *newEvent, Event *oldEvent ) 02154 { 02155 if ( !oldEvent || !newEvent ) 02156 return; 02157 if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() ) 02158 mChanges += i18n( "The invitation starting time has been changed from %1 to %2" ) 02159 .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) ); 02160 if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() ) 02161 mChanges += i18n( "The invitation ending time has been changed from %1 to %2" ) 02162 .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) ); 02163 } 02164 02165 void compareTodos( Todo *newTodo, Todo *oldTodo ) 02166 { 02167 if ( !oldTodo || !newTodo ) { 02168 return; 02169 } 02170 02171 if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) { 02172 mChanges += i18n( "The task has been completed" ); 02173 } 02174 if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) { 02175 mChanges += i18n( "The task is no longer completed" ); 02176 } 02177 if ( oldTodo->percentComplete() != newTodo->percentComplete() ) { 02178 const TQString oldPer = i18n( "%1%" ).arg( oldTodo->percentComplete() ); 02179 const TQString newPer = i18n( "%1%" ).arg( newTodo->percentComplete() ); 02180 mChanges += i18n( "The task completed percentage has changed from %1 to %2" ). 02181 arg( oldPer, newPer ); 02182 } 02183 02184 if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) { 02185 mChanges += i18n( "A task starting time has been added" ); 02186 } 02187 if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) { 02188 mChanges += i18n( "The task starting time has been removed" ); 02189 } 02190 if ( oldTodo->hasStartDate() && newTodo->hasStartDate() && 02191 oldTodo->dtStart() != newTodo->dtStart() ) { 02192 mChanges += i18n( "The task starting time has been changed from %1 to %2" ). 02193 arg( dateTimeToString( oldTodo->dtStart(), oldTodo->doesFloat(), false ), 02194 dateTimeToString( newTodo->dtStart(), newTodo->doesFloat(), false ) ); 02195 } 02196 02197 if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) { 02198 mChanges += i18n( "A task due time has been added" ); 02199 } 02200 if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) { 02201 mChanges += i18n( "The task due time has been removed" ); 02202 } 02203 if ( oldTodo->hasDueDate() && newTodo->hasDueDate() && 02204 oldTodo->dtDue() != newTodo->dtDue() ) { 02205 mChanges += i18n( "The task due time has been changed from %1 to %2" ). 02206 arg( dateTimeToString( oldTodo->dtDue(), oldTodo->doesFloat(), false ), 02207 dateTimeToString( newTodo->dtDue(), newTodo->doesFloat(), false ) ); 02208 } 02209 } 02210 02211 void compareIncidences( Incidence *newInc, Incidence *oldInc, int method ) 02212 { 02213 if ( !oldInc || !newInc ) 02214 return; 02215 if ( oldInc->summary() != newInc->summary() ) 02216 mChanges += i18n( "The summary has been changed to: \"%1\"" ).arg( newInc->summary() ); 02217 if ( oldInc->location() != newInc->location() ) 02218 mChanges += i18n( "The location has been changed to: \"%1\"" ).arg( newInc->location() ); 02219 if ( oldInc->description() != newInc->description() ) 02220 mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() ); 02221 Attendee::List oldAttendees = oldInc->attendees(); 02222 Attendee::List newAttendees = newInc->attendees(); 02223 for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); 02224 it != newAttendees.constEnd(); ++it ) { 02225 Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() ); 02226 if ( !oldAtt ) { 02227 mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() ); 02228 } else { 02229 if ( oldAtt->status() != (*it)->status() ) 02230 mChanges += i18n( "The status of attendee %1 has been changed to: %2" ). 02231 arg( (*it)->fullName() ).arg( (*it)->statusStr() ); 02232 } 02233 } 02234 if ( method == Scheduler::Request ) { 02235 for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); 02236 it != oldAttendees.constEnd(); ++it ) { 02237 if ( (*it)->email() != oldInc->organizer().email() ) { 02238 Attendee *newAtt = newInc->attendeeByMail( (*it)->email() ); 02239 if ( !newAtt ) { 02240 mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() ); 02241 } 02242 } 02243 } 02244 } 02245 } 02246 02247 private: 02248 Incidence *mExistingIncidence; 02249 int mMethod; 02250 TQStringList mChanges; 02251 }; 02252 02253 02254 TQString InvitationFormatterHelper::makeLink( const TQString &id, const TQString &text ) 02255 { 02256 if ( !id.startsWith( "ATTACH:" ) ) { 02257 TQString res = TQString( "<a href=\"%1\"><b>%2</b></a>" ). 02258 arg( generateLinkURL( id ), text ); 02259 return res; 02260 } else { 02261 // draw the attachment links in non-bold face 02262 TQString res = TQString( "<a href=\"%1\">%2</a>" ). 02263 arg( generateLinkURL( id ), text ); 02264 return res; 02265 } 02266 } 02267 02268 // Check if the given incidence is likely one that we own instead one from 02269 // a shared calendar (Kolab-specific) 02270 static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence ) 02271 { 02272 CalendarResources *cal = dynamic_cast<CalendarResources*>( calendar ); 02273 if ( !cal || !incidence ) { 02274 return true; 02275 } 02276 ResourceCalendar *res = cal->resource( incidence ); 02277 if ( !res ) { 02278 return true; 02279 } 02280 const TQString subRes = res->subresourceIdentifier( incidence ); 02281 if ( !subRes.contains( "/.INBOX.directory/" ) ) { 02282 return false; 02283 } 02284 return true; 02285 } 02286 02287 // The spacer for the invitation buttons 02288 static TQString spacer = "<td> </td>"; 02289 // The open & close table cell tags for the invitation buttons 02290 static TQString tdOpen = "<td>"; 02291 static TQString tdClose = "</td>" + spacer; 02292 02293 static TQString responseButtons( Incidence *inc, bool rsvpReq, bool rsvpRec, 02294 InvitationFormatterHelper *helper ) 02295 { 02296 TQString html; 02297 if ( !helper ) { 02298 return html; 02299 } 02300 02301 if ( !rsvpReq && ( inc && inc->revision() == 0 ) ) { 02302 // Record only 02303 html += tdOpen; 02304 html += helper->makeLink( "record", i18n( "[Record]" ) ); 02305 html += tdClose; 02306 02307 // Move to trash 02308 html += tdOpen; 02309 html += helper->makeLink( "delete", i18n( "[Move to Trash]" ) ); 02310 html += tdClose; 02311 02312 } else { 02313 02314 // Accept 02315 html += tdOpen; 02316 html += helper->makeLink( "accept", i18n( "[Accept]" ) ); 02317 html += tdClose; 02318 02319 // Tentative 02320 html += tdOpen; 02321 html += helper->makeLink( "accept_conditionally", 02322 i18n( "Accept conditionally", "[Accept cond.]" ) ); 02323 html += tdClose; 02324 02325 // Counter proposal 02326 html += tdOpen; 02327 html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) ); 02328 html += tdClose; 02329 02330 // Decline 02331 html += tdOpen; 02332 html += helper->makeLink( "decline", i18n( "[Decline]" ) ); 02333 html += tdClose; 02334 } 02335 02336 if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) { 02337 // Delegate 02338 html += tdOpen; 02339 html += helper->makeLink( "delegate", i18n( "[Delegate]" ) ); 02340 html += tdClose; 02341 02342 // Forward 02343 html += tdOpen; 02344 html += helper->makeLink( "forward", i18n( "[Forward]" ) ); 02345 html += tdClose; 02346 02347 // Check calendar 02348 if ( inc && inc->type() == "Event" ) { 02349 html += tdOpen; 02350 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) ); 02351 html += tdClose; 02352 } 02353 } 02354 return html; 02355 } 02356 02357 static TQString counterButtons( Incidence *incidence, 02358 InvitationFormatterHelper *helper ) 02359 { 02360 TQString html; 02361 if ( !helper ) { 02362 return html; 02363 } 02364 02365 // Accept proposal 02366 html += tdOpen; 02367 html += helper->makeLink( "accept_counter", i18n("[Accept]") ); 02368 html += tdClose; 02369 02370 // Decline proposal 02371 html += tdOpen; 02372 html += helper->makeLink( "decline_counter", i18n("[Decline]") ); 02373 html += tdClose; 02374 02375 // Check calendar 02376 if ( incidence && incidence->type() == "Event" ) { 02377 html += tdOpen; 02378 html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) ); 02379 html += tdClose; 02380 } 02381 return html; 02382 } 02383 02384 TQString IncidenceFormatter::formatICalInvitationHelper( TQString invitation, 02385 Calendar *mCalendar, 02386 InvitationFormatterHelper *helper, 02387 bool noHtmlMode, 02388 const TQString &sender ) 02389 { 02390 if ( invitation.isEmpty() ) { 02391 return TQString(); 02392 } 02393 02394 ICalFormat format; 02395 // parseScheduleMessage takes the tz from the calendar, no need to set it manually here for the format! 02396 ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation ); 02397 02398 if( !msg ) { 02399 kdDebug( 5850 ) << "Failed to parse the scheduling message" << endl; 02400 Q_ASSERT( format.exception() ); 02401 kdDebug( 5850 ) << format.exception()->message() << endl; 02402 return TQString(); 02403 } 02404 02405 IncidenceBase *incBase = msg->event(); 02406 02407 // Determine if this incidence is in my calendar (and owned by me) 02408 Incidence *existingIncidence = 0; 02409 if ( incBase && helper->calendar() ) { 02410 existingIncidence = helper->calendar()->incidence( incBase->uid() ); 02411 if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) { 02412 existingIncidence = 0; 02413 } 02414 if ( !existingIncidence ) { 02415 const Incidence::List list = helper->calendar()->incidences(); 02416 for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) { 02417 if ( (*it)->schedulingID() == incBase->uid() && 02418 incidenceOwnedByMe( helper->calendar(), *it ) ) { 02419 existingIncidence = *it; 02420 break; 02421 } 02422 } 02423 } 02424 } 02425 02426 // First make the text of the message 02427 TQString html; 02428 02429 TQString tableStyle = TQString::fromLatin1( 02430 "style=\"border: solid 1px; margin: 0em;\"" ); 02431 TQString tableHead = TQString::fromLatin1( 02432 "<div align=\"center\">" 02433 "<table width=\"80%\" cellpadding=\"1\" cellspacing=\"0\" %1>" 02434 "<tr><td>").arg(tableStyle); 02435 02436 html += tableHead; 02437 InvitationHeaderVisitor headerVisitor; 02438 // The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled 02439 if ( !headerVisitor.act( incBase, existingIncidence, msg, sender ) ) 02440 return TQString(); 02441 html += "<b>" + headerVisitor.result() + "</b>"; 02442 02443 InvitationBodyVisitor bodyVisitor( noHtmlMode ); 02444 if ( !bodyVisitor.act( incBase, existingIncidence, msg, sender ) ) 02445 return TQString(); 02446 html += bodyVisitor.result(); 02447 02448 if ( msg->method() == Scheduler::Request ) { 02449 IncidenceCompareVisitor compareVisitor; 02450 if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) { 02451 html += "<p align=\"left\">"; 02452 html += i18n( "The following changes have been made by the organizer:" ); 02453 html += "</p>"; 02454 html += compareVisitor.result(); 02455 } 02456 } 02457 if ( msg->method() == Scheduler::Reply ) { 02458 IncidenceCompareVisitor compareVisitor; 02459 if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) { 02460 html += "<p align=\"left\">"; 02461 if ( !sender.isEmpty() ) { 02462 html += i18n( "The following changes have been made by %1:" ).arg( sender ); 02463 } else { 02464 html += i18n( "The following changes have been made by an attendee:" ); 02465 } 02466 html += "</p>"; 02467 html += compareVisitor.result(); 02468 } 02469 } 02470 02471 Incidence *inc = dynamic_cast<Incidence*>( incBase ); 02472 02473 // determine if I am the organizer for this invitation 02474 bool myInc = iamOrganizer( inc ); 02475 02476 // determine if the invitation response has already been recorded 02477 bool rsvpRec = false; 02478 Attendee *ea = 0; 02479 if ( !myInc ) { 02480 Incidence *rsvpIncidence = existingIncidence; 02481 if ( !rsvpIncidence && inc && inc->revision() > 0 ) { 02482 rsvpIncidence = inc; 02483 } 02484 if ( rsvpIncidence ) { 02485 ea = findMyAttendee( rsvpIncidence ); 02486 } 02487 if ( ea && 02488 ( ea->status() == Attendee::Accepted || 02489 ea->status() == Attendee::Declined || 02490 ea->status() == Attendee::Tentative ) ) { 02491 rsvpRec = true; 02492 } 02493 } 02494 02495 // determine invitation role 02496 TQString role; 02497 bool isDelegated = false; 02498 Attendee *a = findMyAttendee( inc ); 02499 if ( !a && inc ) { 02500 if ( !inc->attendees().isEmpty() ) { 02501 a = inc->attendees().first(); 02502 } 02503 } 02504 if ( a ) { 02505 isDelegated = ( a->status() == Attendee::Delegated ); 02506 role = Attendee::roleName( a->role() ); 02507 } 02508 02509 // determine if RSVP needed, not-needed, or response already recorded 02510 bool rsvpReq = rsvpRequested( inc ); 02511 if ( !myInc && a ) { 02512 html += "<br/>"; 02513 html += "<i><u>"; 02514 if ( rsvpRec && inc ) { 02515 if ( inc->revision() == 0 ) { 02516 html += i18n( "Your <b>%1</b> response has already been recorded" ). 02517 arg( ea->statusStr() ); 02518 } else { 02519 html += i18n( "Your status for this invitation is <b>%1</b>" ). 02520 arg( ea->statusStr() ); 02521 } 02522 rsvpReq = false; 02523 } else if ( msg->method() == Scheduler::Cancel ) { 02524 html += i18n( "This invitation was declined" ); 02525 } else if ( msg->method() == Scheduler::Add ) { 02526 html += i18n( "This invitation was accepted" ); 02527 } else { 02528 if ( !isDelegated ) { 02529 html += rsvpRequestedStr( rsvpReq, role ); 02530 } else { 02531 html += i18n( "Awaiting delegation response" ); 02532 } 02533 } 02534 html += "</u></i>"; 02535 } 02536 02537 // Print if the organizer gave you a preset status 02538 if ( !myInc ) { 02539 if ( inc && inc->revision() == 0 ) { 02540 TQString statStr = myStatusStr( inc ); 02541 if ( !statStr.isEmpty() ) { 02542 html += "<br/>"; 02543 html += "<i>"; 02544 html += statStr; 02545 html += "</i>"; 02546 } 02547 } 02548 } 02549 02550 // Add groupware links 02551 02552 html += "<br><table border=\"0\" cellspacing=\"0\"><tr><td> </td></tr>"; 02553 02554 switch ( msg->method() ) { 02555 case Scheduler::Publish: 02556 case Scheduler::Request: 02557 case Scheduler::Refresh: 02558 case Scheduler::Add: 02559 { 02560 if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) { 02561 html += "<tr>"; 02562 if ( inc->type() == "Todo" ) { 02563 html += "<td colspan=\"9\">"; 02564 html += helper->makeLink( "reply", i18n( "[Record invitation in my task list]" ) ); 02565 } else { 02566 html += "<td colspan=\"13\">"; 02567 html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) ); 02568 } 02569 html += "</td></tr>"; 02570 } 02571 02572 if ( !myInc && a ) { 02573 html += "<tr>" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + "</tr>"; 02574 } 02575 break; 02576 } 02577 02578 case Scheduler::Cancel: 02579 // Remove invitation 02580 if ( inc ) { 02581 html += "<tr>"; 02582 if ( inc->type() == "Todo" ) { 02583 html += "<td colspan=\"9\">"; 02584 html += helper->makeLink( "cancel", i18n( "[Remove invitation from my task list]" ) ); 02585 } else { 02586 html += "<td colspan=\"13\">"; 02587 html += helper->makeLink( "cancel", i18n( "[Remove invitation from my calendar]" ) ); 02588 } 02589 html += "</td></tr>"; 02590 } 02591 break; 02592 02593 case Scheduler::Reply: 02594 { 02595 // Record invitation response 02596 Attendee *a = 0; 02597 Attendee *ea = 0; 02598 if ( inc ) { 02599 // First, determine if this reply is really a counter in disguise. 02600 if ( replyMeansCounter( inc ) ) { 02601 html += "<tr>" + counterButtons( inc, helper ) + "</tr>"; 02602 break; 02603 } 02604 02605 // Next, maybe this is a declined reply that was delegated from me? 02606 // find first attendee who is delegated-from me 02607 // look a their PARTSTAT response, if the response is declined, 02608 // then we need to start over which means putting all the action 02609 // buttons and NOT putting on the [Record response..] button 02610 a = findDelegatedFromMyAttendee( inc ); 02611 if ( a ) { 02612 if ( a->status() != Attendee::Accepted || 02613 a->status() != Attendee::Tentative ) { 02614 html += "<tr>" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + "</tr>"; 02615 break; 02616 } 02617 } 02618 02619 // Finally, simply allow a Record of the reply 02620 if ( !inc->attendees().isEmpty() ) { 02621 a = inc->attendees().first(); 02622 } 02623 if ( a ) { 02624 ea = findAttendee( existingIncidence, a->email() ); 02625 } 02626 } 02627 if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) { 02628 if ( inc && inc->revision() > 0 ) { 02629 html += "<br><u><i>"; 02630 html += i18n( "The response has been recorded [%1]" ).arg( ea->statusStr() ); 02631 html += "</i></u>"; 02632 } 02633 } else { 02634 if ( inc ) { 02635 html += "<tr><td>"; 02636 if ( inc->type() == "Todo" ) { 02637 html += helper->makeLink( "reply", i18n( "[Record response in my task list]" ) ); 02638 } else { 02639 html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) ); 02640 } 02641 html += "</td></tr>"; 02642 } 02643 } 02644 break; 02645 } 02646 02647 case Scheduler::Counter: 02648 // Counter proposal 02649 html += "<tr>" + counterButtons( inc, helper ) + "</tr>"; 02650 break; 02651 02652 case Scheduler::Declinecounter: 02653 case Scheduler::NoMethod: 02654 break; 02655 } 02656 02657 // close the groupware table 02658 html += "</td></tr></table>"; 02659 02660 // Add the attendee list if I am the organizer 02661 if ( myInc && helper->calendar() ) { 02662 html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) ); 02663 } 02664 02665 // close the top-level table 02666 html += "</td></tr></table><br></div>"; 02667 02668 // Add the attachment list 02669 html += invitationAttachments( helper, inc ); 02670 02671 return html; 02672 } 02673 02674 TQString IncidenceFormatter::formatICalInvitation( TQString invitation, 02675 Calendar *mCalendar, 02676 InvitationFormatterHelper *helper ) 02677 { 02678 return formatICalInvitationHelper( invitation, mCalendar, helper, false, TQString() ); 02679 } 02680 02681 TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation, 02682 Calendar *mCalendar, 02683 InvitationFormatterHelper *helper ) 02684 { 02685 return formatICalInvitationHelper( invitation, mCalendar, helper, true, TQString() ); 02686 } 02687 02688 TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation, 02689 Calendar *mCalendar, 02690 InvitationFormatterHelper *helper, 02691 const TQString &sender ) 02692 { 02693 return formatICalInvitationHelper( invitation, mCalendar, helper, true, sender ); 02694 } 02695 02696 02697 /******************************************************************* 02698 * Helper functions for the msTNEF -> VPart converter 02699 *******************************************************************/ 02700 02701 02702 //----------------------------------------------------------------------------- 02703 02704 static TQString stringProp( KTNEFMessage* tnefMsg, const TQ_UINT32& key, 02705 const TQString& fallback = TQString()) 02706 { 02707 return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16, 02708 fallback ); 02709 } 02710 02711 static TQString sNamedProp( KTNEFMessage* tnefMsg, const TQString& name, 02712 const TQString& fallback = TQString() ) 02713 { 02714 return tnefMsg->findNamedProp( name, fallback ); 02715 } 02716 02717 struct save_tz { char* old_tz; char* tz_env_str; }; 02718 02719 /* temporarily go to a different timezone */ 02720 static struct save_tz set_tz( const char* _tc ) 02721 { 02722 const char *tc = _tc?_tc:"UTC"; 02723 02724 struct save_tz rv; 02725 02726 rv.old_tz = 0; 02727 rv.tz_env_str = 0; 02728 02729 //kdDebug(5006) << "set_tz(), timezone before = " << timezone << endl; 02730 02731 char* tz_env = 0; 02732 if( getenv( "TZ" ) ) { 02733 tz_env = strdup( getenv( "TZ" ) ); 02734 rv.old_tz = tz_env; 02735 } 02736 char* tmp_env = (char*)malloc( strlen( tc ) + 4 ); 02737 strcpy( tmp_env, "TZ=" ); 02738 strcpy( tmp_env+3, tc ); 02739 putenv( tmp_env ); 02740 02741 rv.tz_env_str = tmp_env; 02742 02743 /* tmp_env is not free'ed -- it is part of the environment */ 02744 02745 tzset(); 02746 //kdDebug(5006) << "set_tz(), timezone after = " << timezone << endl; 02747 02748 return rv; 02749 } 02750 02751 /* restore previous timezone */ 02752 static void unset_tz( struct save_tz old_tz ) 02753 { 02754 if( old_tz.old_tz ) { 02755 char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 ); 02756 strcpy( tmp_env, "TZ=" ); 02757 strcpy( tmp_env+3, old_tz.old_tz ); 02758 putenv( tmp_env ); 02759 /* tmp_env is not free'ed -- it is part of the environment */ 02760 free( old_tz.old_tz ); 02761 } else { 02762 /* clear TZ from env */ 02763 putenv( strdup("TZ") ); 02764 } 02765 tzset(); 02766 02767 /* is this OK? */ 02768 if( old_tz.tz_env_str ) free( old_tz.tz_env_str ); 02769 } 02770 02771 static TQDateTime utc2Local( const TQDateTime& utcdt ) 02772 { 02773 struct tm tmL; 02774 02775 save_tz tmp_tz = set_tz("UTC"); 02776 time_t utc = utcdt.toTime_t(); 02777 unset_tz( tmp_tz ); 02778 02779 localtime_r( &utc, &tmL ); 02780 return TQDateTime( TQDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ), 02781 TQTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) ); 02782 } 02783 02784 02785 static TQDateTime pureISOToLocalTQDateTime( const TQString& dtStr, 02786 bool bDateOnly = false ) 02787 { 02788 TQDate tmpDate; 02789 TQTime tmpTime; 02790 int year, month, day, hour, minute, second; 02791 02792 if( bDateOnly ) { 02793 year = dtStr.left( 4 ).toInt(); 02794 month = dtStr.mid( 4, 2 ).toInt(); 02795 day = dtStr.mid( 6, 2 ).toInt(); 02796 hour = 0; 02797 minute = 0; 02798 second = 0; 02799 } else { 02800 year = dtStr.left( 4 ).toInt(); 02801 month = dtStr.mid( 4, 2 ).toInt(); 02802 day = dtStr.mid( 6, 2 ).toInt(); 02803 hour = dtStr.mid( 9, 2 ).toInt(); 02804 minute = dtStr.mid( 11, 2 ).toInt(); 02805 second = dtStr.mid( 13, 2 ).toInt(); 02806 } 02807 tmpDate.setYMD( year, month, day ); 02808 tmpTime.setHMS( hour, minute, second ); 02809 02810 if( tmpDate.isValid() && tmpTime.isValid() ) { 02811 TQDateTime dT = TQDateTime( tmpDate, tmpTime ); 02812 02813 if( !bDateOnly ) { 02814 // correct for GMT ( == Zulu time == UTC ) 02815 if (dtStr.at(dtStr.length()-1) == 'Z') { 02816 //dT = dT.addSecs( 60 * KRFCDate::localUTCOffset() ); 02817 //localUTCOffset( dT ) ); 02818 dT = utc2Local( dT ); 02819 } 02820 } 02821 return dT; 02822 } else 02823 return TQDateTime(); 02824 } 02825 02826 02827 02828 TQString IncidenceFormatter::msTNEFToVPart( const TQByteArray& tnef ) 02829 { 02830 bool bOk = false; 02831 02832 KTNEFParser parser; 02833 TQBuffer buf( tnef ); 02834 CalendarLocal cal ( TQString::fromLatin1( "UTC" ) ); 02835 KABC::Addressee addressee; 02836 KABC::VCardConverter cardConv; 02837 ICalFormat calFormat; 02838 Event* event = new Event(); 02839 02840 if( parser.openDevice( &TQT_TQIODEVICE_OBJECT(buf) ) ) { 02841 KTNEFMessage* tnefMsg = parser.message(); 02842 //TQMap<int,KTNEFProperty*> props = parser.message()->properties(); 02843 02844 // Everything depends from property PR_MESSAGE_CLASS 02845 // (this is added by KTNEFParser): 02846 TQString msgClass = tnefMsg->findProp( 0x001A, TQString(), true ) 02847 .upper(); 02848 if( !msgClass.isEmpty() ) { 02849 // Match the old class names that might be used by Outlook for 02850 // compatibility with Microsoft Mail for Windows for Workgroups 3.1. 02851 bool bCompatClassAppointment = false; 02852 bool bCompatMethodRequest = false; 02853 bool bCompatMethodCancled = false; 02854 bool bCompatMethodAccepted = false; 02855 bool bCompatMethodAcceptedCond = false; 02856 bool bCompatMethodDeclined = false; 02857 if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) { 02858 bCompatClassAppointment = true; 02859 if( msgClass.endsWith( ".MTGREQ" ) ) 02860 bCompatMethodRequest = true; 02861 if( msgClass.endsWith( ".MTGCNCL" ) ) 02862 bCompatMethodCancled = true; 02863 if( msgClass.endsWith( ".MTGRESPP" ) ) 02864 bCompatMethodAccepted = true; 02865 if( msgClass.endsWith( ".MTGRESPA" ) ) 02866 bCompatMethodAcceptedCond = true; 02867 if( msgClass.endsWith( ".MTGRESPN" ) ) 02868 bCompatMethodDeclined = true; 02869 } 02870 bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" ); 02871 02872 if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) { 02873 // Compose a vCal 02874 bool bIsReply = false; 02875 TQString prodID = "-//Microsoft Corporation//Outlook "; 02876 prodID += tnefMsg->findNamedProp( "0x8554", "9.0" ); 02877 prodID += "MIMEDIR/EN\n"; 02878 prodID += "VERSION:2.0\n"; 02879 calFormat.setApplication( "Outlook", prodID ); 02880 02881 Scheduler::Method method; 02882 if( bCompatMethodRequest ) 02883 method = Scheduler::Request; 02884 else if( bCompatMethodCancled ) 02885 method = Scheduler::Cancel; 02886 else if( bCompatMethodAccepted || bCompatMethodAcceptedCond || 02887 bCompatMethodDeclined ) { 02888 method = Scheduler::Reply; 02889 bIsReply = true; 02890 } else { 02891 // pending(khz): verify whether "0x0c17" is the right tag ??? 02892 // 02893 // at the moment we think there are REQUESTS and UPDATES 02894 // 02895 // but WHAT ABOUT REPLIES ??? 02896 // 02897 // 02898 02899 if( tnefMsg->findProp(0x0c17) == "1" ) 02900 bIsReply = true; 02901 method = Scheduler::Request; 02902 } 02903 02905 ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown ); 02906 02907 TQString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) ); 02908 02909 if( !sSenderSearchKeyEmail.isEmpty() ) { 02910 int colon = sSenderSearchKeyEmail.find( ':' ); 02911 // May be e.g. "SMTP:KHZ@KDE.ORG" 02912 if( sSenderSearchKeyEmail.find( ':' ) == -1 ) 02913 sSenderSearchKeyEmail.remove( 0, colon+1 ); 02914 } 02915 02916 TQString s( tnefMsg->findProp( 0x0e04 ) ); 02917 TQStringList attendees = TQStringList::split( ';', s ); 02918 if( attendees.count() ) { 02919 for( TQStringList::Iterator it = attendees.begin(); 02920 it != attendees.end(); ++it ) { 02921 // Skip all entries that have no '@' since these are 02922 // no mail addresses 02923 if( (*it).find('@') == -1 ) { 02924 s = (*it).stripWhiteSpace(); 02925 02926 Attendee *attendee = new Attendee( s, s, true ); 02927 if( bIsReply ) { 02928 if( bCompatMethodAccepted ) 02929 attendee->setStatus( Attendee::Accepted ); 02930 if( bCompatMethodDeclined ) 02931 attendee->setStatus( Attendee::Declined ); 02932 if( bCompatMethodAcceptedCond ) 02933 attendee->setStatus(Attendee::Tentative); 02934 } else { 02935 attendee->setStatus( Attendee::NeedsAction ); 02936 attendee->setRole( Attendee::ReqParticipant ); 02937 } 02938 event->addAttendee(attendee); 02939 } 02940 } 02941 } else { 02942 // Oops, no attendees? 02943 // This must be old style, let us use the PR_SENDER_SEARCH_KEY. 02944 s = sSenderSearchKeyEmail; 02945 if( !s.isEmpty() ) { 02946 Attendee *attendee = new Attendee( TQString(), TQString(), 02947 true ); 02948 if( bIsReply ) { 02949 if( bCompatMethodAccepted ) 02950 attendee->setStatus( Attendee::Accepted ); 02951 if( bCompatMethodAcceptedCond ) 02952 attendee->setStatus( Attendee::Declined ); 02953 if( bCompatMethodDeclined ) 02954 attendee->setStatus( Attendee::Tentative ); 02955 } else { 02956 attendee->setStatus(Attendee::NeedsAction); 02957 attendee->setRole(Attendee::ReqParticipant); 02958 } 02959 event->addAttendee(attendee); 02960 } 02961 } 02962 s = tnefMsg->findProp( 0x0c1f ); // look for organizer property 02963 if( s.isEmpty() && !bIsReply ) 02964 s = sSenderSearchKeyEmail; 02965 // TODO: Use the common name? 02966 if( !s.isEmpty() ) 02967 event->setOrganizer( s ); 02968 02969 s = tnefMsg->findProp( 0x8516 ).replace( TQChar( '-' ), TQString() ) 02970 .replace( TQChar( ':' ), TQString() ); 02971 event->setDtStart( TQDateTime::fromString( s ) ); // ## Format?? 02972 02973 s = tnefMsg->findProp( 0x8517 ).replace( TQChar( '-' ), TQString() ) 02974 .replace( TQChar( ':' ), TQString() ); 02975 event->setDtEnd( TQDateTime::fromString( s ) ); 02976 02977 s = tnefMsg->findProp( 0x8208 ); 02978 event->setLocation( s ); 02979 02980 // is it OK to set this to OPAQUE always ?? 02981 //vPart += "TRANSP:OPAQUE\n"; ###FIXME, portme! 02982 //vPart += "SEQUENCE:0\n"; 02983 02984 // is "0x0023" OK - or should we look for "0x0003" ?? 02985 s = tnefMsg->findProp( 0x0023 ); 02986 event->setUid( s ); 02987 02988 // PENDING(khz): is this value in local timezone? Must it be 02989 // adjusted? Most likely this is a bug in the server or in 02990 // Outlook - we ignore it for now. 02991 s = tnefMsg->findProp( 0x8202 ).replace( TQChar( '-' ), TQString() ) 02992 .replace( TQChar( ':' ), TQString() ); 02993 // ### libkcal always uses currentDateTime() 02994 // event->setDtStamp(TQDateTime::fromString(s)); 02995 02996 s = tnefMsg->findNamedProp( "Keywords" ); 02997 event->setCategories( s ); 02998 02999 s = tnefMsg->findProp( 0x1000 ); 03000 event->setDescription( s ); 03001 03002 s = tnefMsg->findProp( 0x0070 ); 03003 event->setSummary( s ); 03004 03005 s = tnefMsg->findProp( 0x0026 ); 03006 event->setPriority( s.toInt() ); 03007 03008 // is reminder flag set ? 03009 if(!tnefMsg->findProp(0x8503).isEmpty()) { 03010 Alarm *alarm = new Alarm(event); 03011 TQDateTime highNoonTime = 03012 pureISOToLocalTQDateTime( tnefMsg->findProp( 0x8502 ) 03013 .replace( TQChar( '-' ), "" ) 03014 .replace( TQChar( ':' ), "" ) ); 03015 TQDateTime wakeMeUpTime = 03016 pureISOToLocalTQDateTime( tnefMsg->findProp( 0x8560, "" ) 03017 .replace( TQChar( '-' ), "" ) 03018 .replace( TQChar( ':' ), "" ) ); 03019 alarm->setTime(wakeMeUpTime); 03020 03021 if( highNoonTime.isValid() && wakeMeUpTime.isValid() ) 03022 alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) ); 03023 else 03024 // default: wake them up 15 minutes before the appointment 03025 alarm->setStartOffset( Duration( 15*60 ) ); 03026 alarm->setDisplayAlarm( i18n( "Reminder" ) ); 03027 03028 // Sorry: the different action types are not known (yet) 03029 // so we always set 'DISPLAY' (no sounds, no images...) 03030 event->addAlarm( alarm ); 03031 } 03032 cal.addEvent( event ); 03033 bOk = true; 03034 // we finished composing a vCal 03035 } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) { 03036 addressee.setUid( stringProp( tnefMsg, attMSGID ) ); 03037 addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) ); 03038 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true ); 03039 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false ); 03040 addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false ); 03041 addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) ); 03042 addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) ); 03043 addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) ); 03044 addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) ); 03045 addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) ); 03046 addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) ); 03047 addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) ); 03048 03049 TQString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY ) 03050 .replace( TQChar( '-' ), TQString() ) 03051 .replace( TQChar( ':' ), TQString() ); 03052 if( !s.isEmpty() ) 03053 addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s ); 03054 03055 addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE ) ) ); 03056 03057 // collect parts of Name entry 03058 addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) ); 03059 addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) ); 03060 addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) ); 03061 addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) ); 03062 addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) ); 03063 03064 addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) ); 03065 addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) ); 03066 addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) ); 03067 /* 03068 the MAPI property ID of this (multiline) )field is unknown: 03069 vPart += stringProp(tnefMsg, "\n","NOTE", ... , "" ); 03070 */ 03071 03072 KABC::Address adr; 03073 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) ); 03074 adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) ); 03075 adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) ); 03076 adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) ); 03077 adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) ); 03078 adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) ); 03079 adr.setType(KABC::Address::Home); 03080 addressee.insertAddress(adr); 03081 03082 adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) ); 03083 adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) ); 03084 adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) ); 03085 adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) ); 03086 adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) ); 03087 adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) ); 03088 adr.setType( KABC::Address::Work ); 03089 addressee.insertAddress( adr ); 03090 03091 adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) ); 03092 adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) ); 03093 adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) ); 03094 adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) ); 03095 adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) ); 03096 adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) ); 03097 adr.setType( KABC::Address::Dom ); 03098 addressee.insertAddress(adr); 03099 03100 // problem: the 'other' address was stored by KOrganizer in 03101 // a line looking like the following one: 03102 // 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 03103 03104 TQString nr; 03105 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER ); 03106 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Home ) ); 03107 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER ); 03108 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Work ) ); 03109 nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER ); 03110 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Cell ) ); 03111 nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER ); 03112 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Home ) ); 03113 nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER ); 03114 addressee.insertPhoneNumber( KABC::PhoneNumber( nr, KABC::PhoneNumber::Fax | KABC::PhoneNumber::Work ) ); 03115 03116 s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY ) 03117 .replace( TQChar( '-' ), TQString() ) 03118 .replace( TQChar( ':' ), TQString() ); 03119 if( !s.isEmpty() ) 03120 addressee.setBirthday( TQDateTime::fromString( s ) ); 03121 03122 bOk = ( !addressee.isEmpty() ); 03123 } else if( "IPM.NOTE" == msgClass ) { 03124 03125 } // else if ... and so on ... 03126 } 03127 } 03128 03129 // Compose return string 03130 TQString iCal = calFormat.toString( &cal ); 03131 if( !iCal.isEmpty() ) 03132 // This was an iCal 03133 return iCal; 03134 03135 // Not an iCal - try a vCard 03136 KABC::VCardConverter converter; 03137 return converter.createVCard( addressee ); 03138 } 03139 03140 03141 TQString IncidenceFormatter::formatTNEFInvitation( const TQByteArray& tnef, 03142 Calendar *mCalendar, InvitationFormatterHelper *helper ) 03143 { 03144 TQString vPart = IncidenceFormatter::msTNEFToVPart( tnef ); 03145 TQString iCal = IncidenceFormatter::formatICalInvitation( vPart, mCalendar, helper ); 03146 if( !iCal.isEmpty() ) 03147 return iCal; 03148 return vPart; 03149 } 03150 03151 03152 03153 03154 /******************************************************************* 03155 * Helper functions for the Incidence tooltips 03156 *******************************************************************/ 03157 03158 class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor 03159 { 03160 public: 03161 ToolTipVisitor() 03162 : mCalendar( 0 ), mRichText( true ), mResult( "" ) {} 03163 03164 bool act( Calendar *calendar, IncidenceBase *incidence, 03165 const TQDate &date=TQDate(), bool richText=true ) 03166 { 03167 mCalendar = calendar; 03168 mDate = date; 03169 mRichText = richText; 03170 mResult = ""; 03171 return incidence ? incidence->accept( *this ) : false; 03172 } 03173 TQString result() const { return mResult; } 03174 03175 protected: 03176 bool visit( Event *event ); 03177 bool visit( Todo *todo ); 03178 bool visit( Journal *journal ); 03179 bool visit( FreeBusy *fb ); 03180 03181 TQString dateRangeText( Event *event, const TQDate &date ); 03182 TQString dateRangeText( Todo *todo, const TQDate &date ); 03183 TQString dateRangeText( Journal *journal ); 03184 TQString dateRangeText( FreeBusy *fb ); 03185 03186 TQString generateToolTip( Incidence* incidence, TQString dtRangeText ); 03187 03188 protected: 03189 Calendar *mCalendar; 03190 TQDate mDate; 03191 bool mRichText; 03192 TQString mResult; 03193 }; 03194 03195 TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const TQDate &date ) 03196 { 03197 TQString ret; 03198 TQString tmp; 03199 03200 TQDateTime startDt = event->dtStart(); 03201 TQDateTime endDt = event->dtEnd(); 03202 if ( event->doesRecur() ) { 03203 if ( date.isValid() ) { 03204 TQDateTime dt( date, TQTime( 0, 0, 0 ) ); 03205 int diffDays = startDt.daysTo( dt ); 03206 dt = dt.addSecs( -1 ); 03207 startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() ); 03208 if ( event->hasEndDate() ) { 03209 endDt = endDt.addDays( diffDays ); 03210 if ( startDt > endDt ) { 03211 startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() ); 03212 endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) ); 03213 } 03214 } 03215 } 03216 } 03217 if ( event->isMultiDay() ) { 03218 03219 tmp = "<br>" + i18n("Event start", "<i>From:</i> %1"); 03220 if (event->doesFloat()) 03221 ret += tmp.arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") ); 03222 else 03223 ret += tmp.arg( IncidenceFormatter::dateToString( startDt ).replace(" ", " ") ); 03224 03225 tmp = "<br>" + i18n("Event end","<i>To:</i> %1"); 03226 if (event->doesFloat()) 03227 ret += tmp.arg( IncidenceFormatter::dateToString( endDt, false ).replace(" ", " ") ); 03228 else 03229 ret += tmp.arg( IncidenceFormatter::dateToString( endDt ).replace(" ", " ") ); 03230 03231 } else { 03232 03233 ret += "<br>"+i18n("<i>Date:</i> %1"). 03234 arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") ); 03235 if ( !event->doesFloat() ) { 03236 const TQString dtStartTime = 03237 IncidenceFormatter::timeToString( startDt, true ).replace( " ", " " ); 03238 const TQString dtEndTime = 03239 IncidenceFormatter::timeToString( endDt, true ).replace( " ", " " ); 03240 if ( dtStartTime == dtEndTime ) { // to prevent 'Time: 17:00 - 17:00' 03241 tmp = "<br>" + i18n("time for event, to prevent ugly line breaks", 03242 "<i>Time:</i> %1"). 03243 arg( dtStartTime ); 03244 } else { 03245 tmp = "<br>" + i18n("time range for event, to prevent ugly line breaks", 03246 "<i>Time:</i> %1 - %2"). 03247 arg( dtStartTime, dtEndTime ); 03248 } 03249 ret += tmp; 03250 } 03251 03252 } 03253 return ret; 03254 } 03255 03256 TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const TQDate &date ) 03257 { 03258 TQString ret; 03259 bool floats( todo->doesFloat() ); 03260 03261 if ( todo->hasStartDate() && todo->dtStart().isValid() ) { 03262 TQDateTime startDt = todo->dtStart(); 03263 if ( todo->doesRecur() ) { 03264 if ( date.isValid() ) { 03265 startDt.setDate( date ); 03266 } 03267 } 03268 ret += "<br>" + 03269 i18n("<i>Start:</i> %1"). 03270 arg( IncidenceFormatter::dateTimeToString( startDt, floats, false ). 03271 replace( " ", " " ) ); 03272 } 03273 03274 if ( todo->hasDueDate() && todo->dtDue().isValid() ) { 03275 TQDateTime dueDt = todo->dtDue(); 03276 if ( todo->doesRecur() ) { 03277 if ( date.isValid() ) { 03278 TQDateTime dt( date, TQTime( 0, 0, 0 ) ); 03279 dt = dt.addSecs( -1 ); 03280 dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() ); 03281 } 03282 } 03283 ret += "<br>" + 03284 i18n("<i>Due:</i> %1"). 03285 arg( IncidenceFormatter::dateTimeToString( dueDt, floats, false ). 03286 replace( " ", " " ) ); 03287 } 03288 03289 // Print priority and completed info here, for lack of a better place 03290 03291 if ( todo->priority() > 0 ) { 03292 ret += "<br>"; 03293 ret += "<i>" + i18n( "Priority:" ) + "</i>" + " "; 03294 ret += TQString::number( todo->priority() ); 03295 } 03296 03297 ret += "<br>"; 03298 if ( todo->isCompleted() ) { 03299 ret += "<i>" + i18n( "Completed:" ) + "</i>" + " "; 03300 ret += todo->completedStr().replace( " ", " " ); 03301 } else { 03302 ret += "<i>" + i18n( "Percent Done:" ) + "</i>" + " "; 03303 ret += i18n( "%1%" ).arg( todo->percentComplete() ); 03304 } 03305 03306 return ret; 03307 } 03308 03309 TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal ) 03310 { 03311 TQString ret; 03312 if (journal->dtStart().isValid() ) { 03313 ret += "<br>" + 03314 i18n("<i>Date:</i> %1"). 03315 arg( IncidenceFormatter::dateToString( journal->dtStart(), false ) ); 03316 } 03317 return ret; 03318 } 03319 03320 TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb ) 03321 { 03322 TQString tmp( "<br>" + i18n("<i>Period start:</i> %1") ); 03323 TQString ret = tmp.arg( KGlobal::locale()->formatDateTime( fb->dtStart() ) ); 03324 tmp = "<br>" + i18n("<i>Period start:</i> %1"); 03325 ret += tmp.arg( KGlobal::locale()->formatDateTime( fb->dtEnd() ) ); 03326 return ret; 03327 } 03328 03329 03330 03331 bool IncidenceFormatter::ToolTipVisitor::visit( Event *event ) 03332 { 03333 mResult = generateToolTip( event, dateRangeText( event, mDate ) ); 03334 return !mResult.isEmpty(); 03335 } 03336 03337 bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo ) 03338 { 03339 mResult = generateToolTip( todo, dateRangeText( todo, mDate ) ); 03340 return !mResult.isEmpty(); 03341 } 03342 03343 bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal ) 03344 { 03345 mResult = generateToolTip( journal, dateRangeText( journal ) ); 03346 return !mResult.isEmpty(); 03347 } 03348 03349 bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb ) 03350 { 03351 mResult = "<qt><b>" + i18n("Free/Busy information for %1") 03352 .arg(fb->organizer().fullName()) + "</b>"; 03353 mResult += dateRangeText( fb ); 03354 mResult += "</qt>"; 03355 return !mResult.isEmpty(); 03356 } 03357 03358 static TQString tooltipPerson( const TQString& email, TQString name ) 03359 { 03360 // Make the search, if there is an email address to search on, 03361 // and name is missing 03362 if ( name.isEmpty() && !email.isEmpty() ) { 03363 KABC::AddressBook *add_book = KABC::StdAddressBook::self( true ); 03364 KABC::Addressee::List addressList = add_book->findByEmail( email ); 03365 if ( !addressList.isEmpty() ) { 03366 KABC::Addressee o = addressList.first(); 03367 if ( !o.isEmpty() && addressList.size() < 2 ) { 03368 // use the name from the addressbook 03369 name = o.formattedName(); 03370 } 03371 } 03372 } 03373 03374 // Show the attendee 03375 TQString tmpString = ( name.isEmpty() ? email : name ); 03376 03377 return tmpString; 03378 } 03379 03380 static TQString etc = i18n( "elipsis", "..." ); 03381 static TQString tooltipFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role ) 03382 { 03383 int maxNumAtts = 8; // maximum number of people to print per attendee role 03384 TQString sep = i18n( "separator for lists of people names", ", " ); 03385 int sepLen = sep.length(); 03386 03387 int i = 0; 03388 TQString tmpStr; 03389 Attendee::List::ConstIterator it; 03390 Attendee::List attendees = incidence->attendees(); 03391 03392 for( it = attendees.begin(); it != attendees.end(); ++it ) { 03393 Attendee *a = *it; 03394 if ( a->role() != role ) { 03395 // skip not this role 03396 continue; 03397 } 03398 if ( a->email() == incidence->organizer().email() ) { 03399 // skip attendee that is also the organizer 03400 continue; 03401 } 03402 if ( i == maxNumAtts ) { 03403 tmpStr += etc; 03404 break; 03405 } 03406 tmpStr += tooltipPerson( a->email(), a->name() ); 03407 if ( !a->delegator().isEmpty() ) { 03408 tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); 03409 } 03410 if ( !a->delegate().isEmpty() ) { 03411 tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); 03412 } 03413 tmpStr += sep; 03414 i++; 03415 } 03416 if ( tmpStr.endsWith( sep ) ) { 03417 tmpStr.truncate( tmpStr.length() - sepLen ); 03418 } 03419 return tmpStr; 03420 } 03421 03422 static TQString tooltipFormatAttendees( Incidence *incidence ) 03423 { 03424 TQString tmpStr, str; 03425 03426 // Add organizer link 03427 int attendeeCount = incidence->attendees().count(); 03428 if ( attendeeCount > 1 || 03429 ( attendeeCount == 1 && 03430 incidence->organizer().email() != incidence->attendees().first()->email() ) ) { 03431 tmpStr += "<i>" + i18n( "Organizer:" ) + "</i>" + " "; 03432 tmpStr += tooltipPerson( incidence->organizer().email(), 03433 incidence->organizer().name() ); 03434 } 03435 03436 // Add "chair" 03437 str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair ); 03438 if ( !str.isEmpty() ) { 03439 tmpStr += "<br><i>" + i18n( "Chair:" ) + "</i>" + " "; 03440 tmpStr += str; 03441 } 03442 03443 // Add required participants 03444 str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant ); 03445 if ( !str.isEmpty() ) { 03446 tmpStr += "<br><i>" + i18n( "Required Participants:" ) + "</i>" + " "; 03447 tmpStr += str; 03448 } 03449 03450 // Add optional participants 03451 str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant ); 03452 if ( !str.isEmpty() ) { 03453 tmpStr += "<br><i>" + i18n( "Optional Participants:" ) + "</i>" + " "; 03454 tmpStr += str; 03455 } 03456 03457 // Add observers 03458 str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant ); 03459 if ( !str.isEmpty() ) { 03460 tmpStr += "<br><i>" + i18n( "Observers:" ) + "</i>" + " "; 03461 tmpStr += str; 03462 } 03463 03464 return tmpStr; 03465 } 03466 03467 TQString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, TQString dtRangeText ) 03468 { 03469 uint maxDescLen = 120; // maximum description chars to print (before elipsis) 03470 03471 if ( !incidence ) { 03472 return TQString(); 03473 } 03474 03475 TQString tmp = "<qt>"; 03476 03477 // header 03478 tmp += "<b>" + incidence->summary().replace( "\n", "<br>" ) + "</b>"; 03479 //NOTE: using <hr> seems to confuse TQt3 tooltips in some cases so use "-----" 03480 tmp += "<br>----------<br>"; 03481 03482 if ( mCalendar ) { 03483 TQString calStr = IncidenceFormatter::resourceString( mCalendar, incidence ); 03484 if ( !calStr.isEmpty() ) { 03485 tmp += "<i>" + i18n( "Calendar:" ) + "</i>" + " "; 03486 tmp += calStr; 03487 } 03488 } 03489 03490 tmp += dtRangeText; 03491 03492 if ( !incidence->location().isEmpty() ) { 03493 tmp += "<br>"; 03494 tmp += "<i>" + i18n( "Location:" ) + "</i>" + " "; 03495 tmp += incidence->location().replace( "\n", "<br>" ); 03496 } 03497 03498 TQString durStr = IncidenceFormatter::durationString( incidence ); 03499 if ( !durStr.isEmpty() ) { 03500 tmp += "<br>"; 03501 tmp += "<i>" + i18n( "Duration:" ) + "</i>" + " "; 03502 tmp += durStr; 03503 } 03504 03505 if ( incidence->doesRecur() ) { 03506 tmp += "<br>"; 03507 tmp += "<i>" + i18n( "Recurrence:" ) + "</i>" + " "; 03508 tmp += IncidenceFormatter::recurrenceString( incidence ); 03509 } 03510 03511 if ( !incidence->description().isEmpty() ) { 03512 TQString desc( incidence->description() ); 03513 if ( desc.length() > maxDescLen ) { 03514 desc = desc.left( maxDescLen ) + etc; 03515 } 03516 tmp += "<br>----------<br>"; 03517 tmp += "<i>" + i18n( "Description:" ) + "</i>" + "<br>"; 03518 tmp += desc.replace( "\n", "<br>" ); 03519 tmp += "<br>----------"; 03520 } 03521 03522 int reminderCount = incidence->alarms().count(); 03523 if ( reminderCount > 0 && incidence->isAlarmEnabled() ) { 03524 tmp += "<br>"; 03525 tmp += "<i>" + i18n( "Reminder:", "%n Reminders:", reminderCount ) + "</i>" + " "; 03526 tmp += IncidenceFormatter::reminderStringList( incidence ).join( ", " ); 03527 } 03528 03529 tmp += "<br>"; 03530 tmp += tooltipFormatAttendees( incidence ); 03531 03532 int categoryCount = incidence->categories().count(); 03533 if ( categoryCount > 0 ) { 03534 tmp += "<br>"; 03535 tmp += "<i>" + i18n( "Category:", "%n Categories:", categoryCount ) + "</i>" + " "; 03536 tmp += incidence->categories().join( ", " ); 03537 } 03538 03539 tmp += "</qt>"; 03540 return tmp; 03541 } 03542 03543 TQString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText ) 03544 { 03545 return toolTipStr( 0, incidence, TQDate(), richText ); 03546 } 03547 03548 TQString IncidenceFormatter::toolTipStr( Calendar *calendar, 03549 IncidenceBase *incidence, 03550 const TQDate &date, 03551 bool richText ) 03552 { 03553 ToolTipVisitor v; 03554 if ( v.act( calendar, incidence, date, richText ) ) { 03555 return v.result(); 03556 } else { 03557 return TQString(); 03558 } 03559 } 03560 03561 /******************************************************************* 03562 * Helper functions for the Incidence tooltips 03563 *******************************************************************/ 03564 03565 class IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor 03566 { 03567 public: 03568 MailBodyVisitor() : mResult( "" ) {} 03569 03570 bool act( IncidenceBase *incidence ) 03571 { 03572 mResult = ""; 03573 return incidence ? incidence->accept( *this ) : false; 03574 } 03575 TQString result() const { return mResult; } 03576 03577 protected: 03578 bool visit( Event *event ); 03579 bool visit( Todo *todo ); 03580 bool visit( Journal *journal ); 03581 bool visit( FreeBusy * ) { mResult = i18n("This is a Free Busy Object"); return !mResult.isEmpty(); } 03582 protected: 03583 TQString mResult; 03584 }; 03585 03586 03587 static TQString mailBodyIncidence( Incidence *incidence ) 03588 { 03589 TQString body; 03590 if ( !incidence->summary().isEmpty() ) { 03591 body += i18n("Summary: %1\n").arg( incidence->summary() ); 03592 } 03593 if ( !incidence->organizer().isEmpty() ) { 03594 body += i18n("Organizer: %1\n").arg( incidence->organizer().fullName() ); 03595 } 03596 if ( !incidence->location().isEmpty() ) { 03597 body += i18n("Location: %1\n").arg( incidence->location() ); 03598 } 03599 return body; 03600 } 03601 03602 bool IncidenceFormatter::MailBodyVisitor::visit( Event *event ) 03603 { 03604 TQString recurrence[]= {i18n("no recurrence", "None"), 03605 i18n("Minutely"), i18n("Hourly"), i18n("Daily"), 03606 i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"), 03607 i18n("Yearly"), i18n("Yearly"), i18n("Yearly")}; 03608 03609 mResult = mailBodyIncidence( event ); 03610 mResult += i18n("Start Date: %1\n"). 03611 arg( IncidenceFormatter::dateToString( event->dtStart(), true ) ); 03612 if ( !event->doesFloat() ) { 03613 mResult += i18n("Start Time: %1\n"). 03614 arg( IncidenceFormatter::timeToString( event->dtStart(), true ) ); 03615 } 03616 if ( event->dtStart() != event->dtEnd() ) { 03617 mResult += i18n("End Date: %1\n"). 03618 arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) ); 03619 } 03620 if ( !event->doesFloat() ) { 03621 mResult += i18n("End Time: %1\n"). 03622 arg( IncidenceFormatter::timeToString( event->dtEnd(), true ) ); 03623 } 03624 if ( event->doesRecur() ) { 03625 Recurrence *recur = event->recurrence(); 03626 // TODO: Merge these two to one of the form "Recurs every 3 days" 03627 mResult += i18n("Recurs: %1\n") 03628 .arg( recurrence[ recur->recurrenceType() ] ); 03629 mResult += i18n("Frequency: %1\n") 03630 .arg( event->recurrence()->frequency() ); 03631 03632 if ( recur->duration() > 0 ) { 03633 mResult += i18n ("Repeats once", "Repeats %n times", recur->duration()); 03634 mResult += '\n'; 03635 } else { 03636 if ( recur->duration() != -1 ) { 03637 // TODO_Recurrence: What to do with floating 03638 TQString endstr; 03639 if ( event->doesFloat() ) { 03640 endstr = KGlobal::locale()->formatDate( recur->endDate() ); 03641 } else { 03642 endstr = KGlobal::locale()->formatDateTime( recur->endDateTime() ); 03643 } 03644 mResult += i18n("Repeat until: %1\n").arg( endstr ); 03645 } else { 03646 mResult += i18n("Repeats forever\n"); 03647 } 03648 } 03649 03650 DateList exceptions = recur->exDates(); 03651 if (exceptions.isEmpty() == false) { 03652 mResult += i18n("This recurring meeting has been cancelled on the following days:\n"); 03653 DateList::ConstIterator ex_iter; 03654 for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) { 03655 mResult += i18n(" %1\n").arg( KGlobal::locale()->formatDate(* ex_iter ) ); 03656 } 03657 } 03658 } 03659 TQString details = event->description(); 03660 if ( !details.isEmpty() ) { 03661 mResult += i18n("Details:\n%1\n").arg( details ); 03662 } 03663 return !mResult.isEmpty(); 03664 } 03665 03666 bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo ) 03667 { 03668 mResult = mailBodyIncidence( todo ); 03669 03670 if ( todo->hasStartDate() ) { 03671 mResult += i18n("Start Date: %1\n"). 03672 arg( IncidenceFormatter::dateToString( todo->dtStart( false ), true ) ); 03673 if ( !todo->doesFloat() ) { 03674 mResult += i18n("Start Time: %1\n"). 03675 arg( IncidenceFormatter::timeToString( todo->dtStart( false ),true ) ); 03676 } 03677 } 03678 if ( todo->hasDueDate() ) { 03679 mResult += i18n("Due Date: %1\n"). 03680 arg( IncidenceFormatter::dateToString( todo->dtDue(), true ) ); 03681 if ( !todo->doesFloat() ) { 03682 mResult += i18n("Due Time: %1\n"). 03683 arg( IncidenceFormatter::timeToString( todo->dtDue(), true ) ); 03684 } 03685 } 03686 TQString details = todo->description(); 03687 if ( !details.isEmpty() ) { 03688 mResult += i18n("Details:\n%1\n").arg( details ); 03689 } 03690 return !mResult.isEmpty(); 03691 } 03692 03693 bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal ) 03694 { 03695 mResult = mailBodyIncidence( journal ); 03696 mResult += i18n("Date: %1\n"). 03697 arg( IncidenceFormatter::dateToString( journal->dtStart(), true ) ); 03698 if ( !journal->doesFloat() ) { 03699 mResult += i18n("Time: %1\n"). 03700 arg( IncidenceFormatter::timeToString( journal->dtStart(), true ) ); 03701 } 03702 if ( !journal->description().isEmpty() ) 03703 mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() ); 03704 return !mResult.isEmpty(); 03705 } 03706 03707 03708 03709 TQString IncidenceFormatter::mailBodyString( IncidenceBase *incidence ) 03710 { 03711 if ( !incidence ) 03712 return TQString(); 03713 03714 MailBodyVisitor v; 03715 if ( v.act( incidence ) ) { 03716 return v.result(); 03717 } 03718 return TQString(); 03719 } 03720 03721 static TQString recurEnd( Incidence *incidence ) 03722 { 03723 TQString endstr; 03724 if ( incidence->doesFloat() ) { 03725 endstr = KGlobal::locale()->formatDate( incidence->recurrence()->endDate() ); 03726 } else { 03727 endstr = KGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() ); 03728 } 03729 return endstr; 03730 } 03731 03732 /************************************ 03733 * More static formatting functions 03734 ************************************/ 03735 TQString IncidenceFormatter::recurrenceString( Incidence *incidence ) 03736 { 03737 if ( !incidence->doesRecur() ) { 03738 return i18n( "No recurrence" ); 03739 } 03740 TQStringList dayList; 03741 dayList.append( i18n( "31st Last" ) ); 03742 dayList.append( i18n( "30th Last" ) ); 03743 dayList.append( i18n( "29th Last" ) ); 03744 dayList.append( i18n( "28th Last" ) ); 03745 dayList.append( i18n( "27th Last" ) ); 03746 dayList.append( i18n( "26th Last" ) ); 03747 dayList.append( i18n( "25th Last" ) ); 03748 dayList.append( i18n( "24th Last" ) ); 03749 dayList.append( i18n( "23rd Last" ) ); 03750 dayList.append( i18n( "22nd Last" ) ); 03751 dayList.append( i18n( "21st Last" ) ); 03752 dayList.append( i18n( "20th Last" ) ); 03753 dayList.append( i18n( "19th Last" ) ); 03754 dayList.append( i18n( "18th Last" ) ); 03755 dayList.append( i18n( "17th Last" ) ); 03756 dayList.append( i18n( "16th Last" ) ); 03757 dayList.append( i18n( "15th Last" ) ); 03758 dayList.append( i18n( "14th Last" ) ); 03759 dayList.append( i18n( "13th Last" ) ); 03760 dayList.append( i18n( "12th Last" ) ); 03761 dayList.append( i18n( "11th Last" ) ); 03762 dayList.append( i18n( "10th Last" ) ); 03763 dayList.append( i18n( "9th Last" ) ); 03764 dayList.append( i18n( "8th Last" ) ); 03765 dayList.append( i18n( "7th Last" ) ); 03766 dayList.append( i18n( "6th Last" ) ); 03767 dayList.append( i18n( "5th Last" ) ); 03768 dayList.append( i18n( "4th Last" ) ); 03769 dayList.append( i18n( "3rd Last" ) ); 03770 dayList.append( i18n( "2nd Last" ) ); 03771 dayList.append( i18n( "last day of the month", "Last" ) ); 03772 dayList.append( i18n( "unknown day of the month", "unknown" ) ); //#31 - zero offset from UI 03773 dayList.append( i18n( "1st" ) ); 03774 dayList.append( i18n( "2nd" ) ); 03775 dayList.append( i18n( "3rd" ) ); 03776 dayList.append( i18n( "4th" ) ); 03777 dayList.append( i18n( "5th" ) ); 03778 dayList.append( i18n( "6th" ) ); 03779 dayList.append( i18n( "7th" ) ); 03780 dayList.append( i18n( "8th" ) ); 03781 dayList.append( i18n( "9th" ) ); 03782 dayList.append( i18n( "10th" ) ); 03783 dayList.append( i18n( "11th" ) ); 03784 dayList.append( i18n( "12th" ) ); 03785 dayList.append( i18n( "13th" ) ); 03786 dayList.append( i18n( "14th" ) ); 03787 dayList.append( i18n( "15th" ) ); 03788 dayList.append( i18n( "16th" ) ); 03789 dayList.append( i18n( "17th" ) ); 03790 dayList.append( i18n( "18th" ) ); 03791 dayList.append( i18n( "19th" ) ); 03792 dayList.append( i18n( "20th" ) ); 03793 dayList.append( i18n( "21st" ) ); 03794 dayList.append( i18n( "22nd" ) ); 03795 dayList.append( i18n( "23rd" ) ); 03796 dayList.append( i18n( "24th" ) ); 03797 dayList.append( i18n( "25th" ) ); 03798 dayList.append( i18n( "26th" ) ); 03799 dayList.append( i18n( "27th" ) ); 03800 dayList.append( i18n( "28th" ) ); 03801 dayList.append( i18n( "29th" ) ); 03802 dayList.append( i18n( "30th" ) ); 03803 dayList.append( i18n( "31st" ) ); 03804 int weekStart = KGlobal::locale()->weekStartDay(); 03805 TQString dayNames; 03806 TQString recurStr, txt; 03807 const KCalendarSystem *calSys = KGlobal::locale()->calendar(); 03808 Recurrence *recur = incidence->recurrence(); 03809 switch ( recur->recurrenceType() ) { 03810 case Recurrence::rNone: 03811 return i18n( "No recurrence" ); 03812 03813 case Recurrence::rMinutely: 03814 recurStr = i18n( "Recurs every minute", "Recurs every %n minutes", recur->frequency() ); 03815 if ( recur->duration() != -1 ) { 03816 txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) ); 03817 if ( recur->duration() > 0 ) { 03818 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); 03819 } 03820 return txt; 03821 } 03822 return recurStr; 03823 03824 case Recurrence::rHourly: 03825 recurStr = i18n( "Recurs hourly", "Recurs every %n hours", recur->frequency() ); 03826 if ( recur->duration() != -1 ) { 03827 txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) ); 03828 if ( recur->duration() > 0 ) { 03829 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); 03830 } 03831 return txt; 03832 } 03833 return recurStr; 03834 03835 case Recurrence::rDaily: 03836 recurStr = i18n( "Recurs daily", "Recurs every %n days", recur->frequency() ); 03837 if ( recur->duration() != -1 ) { 03838 03839 txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) ); 03840 if ( recur->duration() > 0 ) { 03841 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); 03842 } 03843 return txt; 03844 } 03845 return recurStr; 03846 03847 case Recurrence::rWeekly: 03848 { 03849 recurStr = i18n( "Recurs weekly", "Recurs every %n weeks", recur->frequency() ); 03850 03851 bool addSpace = false; 03852 for ( int i = 0; i < 7; ++i ) { 03853 if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) { 03854 if ( addSpace ) { 03855 dayNames.append( i18n( "separator for list of days", ", " ) ); 03856 } 03857 dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1, true ) ); 03858 addSpace = true; 03859 } 03860 } 03861 if ( dayNames.isEmpty() ) { 03862 dayNames = i18n( "Recurs weekly on no days", "no days" ); 03863 } 03864 if ( recur->duration() != -1 ) { 03865 txt = i18n( "%1 on %2 until %3" ). 03866 arg( recurStr ).arg( dayNames ).arg( recurEnd( incidence ) ); 03867 if ( recur->duration() > 0 ) { 03868 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); 03869 } 03870 return txt; 03871 } 03872 txt = i18n( "%1 on %2" ).arg( recurStr ).arg( dayNames ); 03873 return txt; 03874 } 03875 case Recurrence::rMonthlyPos: 03876 { 03877 recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() ); 03878 03879 if ( !recur->monthPositions().isEmpty() ) { 03880 KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0]; 03881 if ( recur->duration() != -1 ) { 03882 txt = i18n( "%1 on the %2 %3 until %4" ). 03883 arg( recurStr ). 03884 arg( dayList[rule.pos() + 31] ). 03885 arg( calSys->weekDayName( rule.day(), false ) ). 03886 arg( recurEnd( incidence ) ); 03887 if ( recur->duration() > 0 ) { 03888 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); 03889 } 03890 return txt; 03891 } 03892 txt = i18n( "%1 on the %2 %3" ). 03893 arg( recurStr ). 03894 arg( dayList[rule.pos() + 31] ). 03895 arg( calSys->weekDayName( rule.day(), false ) ); 03896 return txt; 03897 } else { 03898 return recurStr; 03899 } 03900 break; 03901 } 03902 case Recurrence::rMonthlyDay: 03903 { 03904 recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() ); 03905 03906 if ( !recur->monthDays().isEmpty() ) { 03907 int days = recur->monthDays()[0]; 03908 if ( recur->duration() != -1 ) { 03909 txt = i18n( "%1 on the %2 day until %3" ). 03910 arg( recurStr ). 03911 arg( dayList[days + 31] ). 03912 arg( recurEnd( incidence ) ); 03913 if ( recur->duration() > 0 ) { 03914 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); 03915 } 03916 return txt; 03917 } 03918 txt = i18n( "%1 on the %2 day" ).arg( recurStr ).arg( dayList[days + 31] ); 03919 return txt; 03920 } else { 03921 return recurStr; 03922 } 03923 break; 03924 } 03925 case Recurrence::rYearlyMonth: 03926 { 03927 recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() ); 03928 03929 if ( recur->duration() != -1 ) { 03930 if ( !recur->yearDates().isEmpty() ) { 03931 txt = i18n( "%1 on %2 %3 until %4" ). 03932 arg( recurStr ). 03933 arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). 03934 arg( dayList[ recur->yearDates()[0] + 31 ] ). 03935 arg( recurEnd( incidence ) ); 03936 if ( recur->duration() > 0 ) { 03937 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); 03938 } 03939 return txt; 03940 } 03941 } 03942 if ( !recur->yearDates().isEmpty() ) { 03943 txt = i18n( "%1 on %2 %3" ). 03944 arg( recurStr ). 03945 arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). 03946 arg( dayList[ recur->yearDates()[0] + 31 ] ); 03947 return txt; 03948 } else { 03949 if ( !recur->yearMonths().isEmpty() ) { 03950 txt = i18n( "Recurs yearly on %1 %2" ). 03951 arg( calSys->monthName( recur->yearMonths()[0], 03952 recur->startDate().year() ) ). 03953 arg( dayList[ recur->startDate().day() + 31 ] ); 03954 } else { 03955 txt = i18n( "Recurs yearly on %1 %2" ). 03956 arg( calSys->monthName( recur->startDate().month(), 03957 recur->startDate().year() ) ). 03958 arg( dayList[ recur->startDate().day() + 31 ] ); 03959 } 03960 return txt; 03961 } 03962 break; 03963 } 03964 case Recurrence::rYearlyDay: 03965 { 03966 recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() ); 03967 if ( !recur->yearDays().isEmpty() ) { 03968 if ( recur->duration() != -1 ) { 03969 txt = i18n( "%1 on day %2 until %3" ). 03970 arg( recurStr ). 03971 arg( recur->yearDays()[0] ). 03972 arg( recurEnd( incidence ) ); 03973 if ( recur->duration() > 0 ) { 03974 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); 03975 } 03976 return txt; 03977 } 03978 txt = i18n( "%1 on day %2" ).arg( recurStr ).arg( recur->yearDays()[0] ); 03979 return txt; 03980 } else { 03981 return recurStr; 03982 } 03983 break; 03984 } 03985 case Recurrence::rYearlyPos: 03986 { 03987 recurStr = i18n( "Every year", "Every %n years", recur->frequency() ); 03988 if ( !recur->yearPositions().isEmpty() && !recur->yearMonths().isEmpty() ) { 03989 KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0]; 03990 if ( recur->duration() != -1 ) { 03991 txt = i18n( "%1 on the %2 %3 of %4 until %5" ). 03992 arg( recurStr ). 03993 arg( dayList[rule.pos() + 31] ). 03994 arg( calSys->weekDayName( rule.day(), false ) ). 03995 arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). 03996 arg( recurEnd( incidence ) ); 03997 if ( recur->duration() > 0 ) { 03998 txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); 03999 } 04000 return txt; 04001 } 04002 txt = i18n( "%1 on the %2 %3 of %4" ). 04003 arg( recurStr ). 04004 arg( dayList[rule.pos() + 31] ). 04005 arg( calSys->weekDayName( rule.day(), false ) ). 04006 arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ); 04007 return txt; 04008 } else { 04009 return recurStr; 04010 } 04011 break; 04012 } 04013 } 04014 04015 return i18n( "Incidence recurs" ); 04016 } 04017 04018 TQString IncidenceFormatter::timeToString( const TQDateTime &date, bool shortfmt ) 04019 { 04020 return KGlobal::locale()->formatTime( date.time(), !shortfmt ); 04021 } 04022 04023 TQString IncidenceFormatter::dateToString( const TQDateTime &date, bool shortfmt ) 04024 { 04025 return 04026 KGlobal::locale()->formatDate( date.date(), shortfmt ); 04027 } 04028 04029 TQString IncidenceFormatter::dateTimeToString( const TQDateTime &date, 04030 bool allDay, bool shortfmt ) 04031 { 04032 if ( allDay ) { 04033 return dateToString( date, shortfmt ); 04034 } 04035 04036 return KGlobal::locale()->formatDateTime( date, shortfmt ); 04037 } 04038 04039 TQString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence ) 04040 { 04041 if ( !calendar || !incidence ) { 04042 return TQString(); 04043 } 04044 04045 CalendarResources *calendarResource = dynamic_cast<CalendarResources*>( calendar ); 04046 if ( !calendarResource ) { 04047 return TQString(); 04048 } 04049 04050 ResourceCalendar *resourceCalendar = calendarResource->resource( incidence ); 04051 if ( resourceCalendar ) { 04052 if ( !resourceCalendar->subresources().isEmpty() ) { 04053 TQString subRes = resourceCalendar->subresourceIdentifier( incidence ); 04054 if ( subRes.isEmpty() ) { 04055 return resourceCalendar->resourceName(); 04056 } else { 04057 return resourceCalendar->labelForSubresource( subRes ); 04058 } 04059 } 04060 return resourceCalendar->resourceName(); 04061 } 04062 04063 return TQString(); 04064 } 04065 04066 static TQString secs2Duration( int secs ) 04067 { 04068 TQString tmp; 04069 int days = secs / 86400; 04070 if ( days > 0 ) { 04071 tmp += i18n( "1 day", "%n days", days ); 04072 tmp += ' '; 04073 secs -= ( days * 86400 ); 04074 } 04075 int hours = secs / 3600; 04076 if ( hours > 0 ) { 04077 tmp += i18n( "1 hour", "%n hours", hours ); 04078 tmp += ' '; 04079 secs -= ( hours * 3600 ); 04080 } 04081 int mins = secs / 60; 04082 if ( mins > 0 ) { 04083 tmp += i18n( "1 minute", "%n minutes", mins ); 04084 } 04085 return tmp; 04086 } 04087 04088 TQString IncidenceFormatter::durationString( Incidence *incidence ) 04089 { 04090 TQString tmp; 04091 if ( incidence->type() == "Event" ) { 04092 Event *event = static_cast<Event *>( incidence ); 04093 if ( event->hasEndDate() ) { 04094 if ( !event->doesFloat() ) { 04095 tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) ); 04096 } else { 04097 tmp = i18n( "1 day", "%n days", 04098 event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 ); 04099 } 04100 } else { 04101 tmp = i18n( "forever" ); 04102 } 04103 } else if ( incidence->type() == "Todo" ) { 04104 Todo *todo = static_cast<Todo *>( incidence ); 04105 if ( todo->hasDueDate() ) { 04106 if ( todo->hasStartDate() ) { 04107 if ( !todo->doesFloat() ) { 04108 tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) ); 04109 } else { 04110 tmp = i18n( "1 day", "%n days", 04111 todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 ); 04112 } 04113 } 04114 } 04115 } 04116 return tmp; 04117 } 04118 04119 TQStringList IncidenceFormatter::reminderStringList( Incidence *incidence, bool shortfmt ) 04120 { 04121 //TODO: implement shortfmt=false 04122 Q_UNUSED( shortfmt ); 04123 04124 TQStringList reminderStringList; 04125 04126 if ( incidence ) { 04127 Alarm::List alarms = incidence->alarms(); 04128 Alarm::List::ConstIterator it; 04129 for ( it = alarms.begin(); it != alarms.end(); ++it ) { 04130 Alarm *alarm = *it; 04131 int offset = 0; 04132 TQString remStr, atStr, offsetStr; 04133 if ( alarm->hasTime() ) { 04134 offset = 0; 04135 if ( alarm->time().isValid() ) { 04136 atStr = KGlobal::locale()->formatDateTime( alarm->time() ); 04137 } 04138 } else if ( alarm->hasStartOffset() ) { 04139 offset = alarm->startOffset().asSeconds(); 04140 if ( offset < 0 ) { 04141 offset = -offset; 04142 offsetStr = i18n( "N days/hours/minutes before the start datetime", 04143 "%1 before the start" ); 04144 } else if ( offset > 0 ) { 04145 offsetStr = i18n( "N days/hours/minutes after the start datetime", 04146 "%1 after the start" ); 04147 } else { //offset is 0 04148 if ( incidence->dtStart().isValid() ) { 04149 atStr = KGlobal::locale()->formatDateTime( incidence->dtStart() ); 04150 } 04151 } 04152 } else if ( alarm->hasEndOffset() ) { 04153 offset = alarm->endOffset().asSeconds(); 04154 if ( offset < 0 ) { 04155 offset = -offset; 04156 if ( incidence->type() == "Todo" ) { 04157 offsetStr = i18n( "N days/hours/minutes before the due datetime", 04158 "%1 before the to-do is due" ); 04159 } else { 04160 offsetStr = i18n( "N days/hours/minutes before the end datetime", 04161 "%1 before the end" ); 04162 } 04163 } else if ( offset > 0 ) { 04164 if ( incidence->type() == "Todo" ) { 04165 offsetStr = i18n( "N days/hours/minutes after the due datetime", 04166 "%1 after the to-do is due" ); 04167 } else { 04168 offsetStr = i18n( "N days/hours/minutes after the end datetime", 04169 "%1 after the end" ); 04170 } 04171 } else { //offset is 0 04172 if ( incidence->type() == "Todo" ) { 04173 Todo *t = static_cast<Todo *>( incidence ); 04174 if ( t->dtDue().isValid() ) { 04175 atStr = KGlobal::locale()->formatDateTime( t->dtDue() ); 04176 } 04177 } else { 04178 Event *e = static_cast<Event *>( incidence ); 04179 if ( e->dtEnd().isValid() ) { 04180 atStr = KGlobal::locale()->formatDateTime( e->dtEnd() ); 04181 } 04182 } 04183 } 04184 } 04185 if ( offset == 0 ) { 04186 if ( !atStr.isEmpty() ) { 04187 remStr = i18n( "reminder occurs at datetime", "at %1" ).arg( atStr ); 04188 } 04189 } else { 04190 remStr = offsetStr.arg( secs2Duration( offset ) ); 04191 } 04192 04193 if ( alarm->repeatCount() > 0 ) { 04194 TQString countStr = i18n( "repeats once", "repeats %n times", alarm->repeatCount() ); 04195 TQString intervalStr = i18n( "interval is N days/hours/minutes", "interval is %1" ). 04196 arg( secs2Duration( alarm->snoozeTime().asSeconds() ) ); 04197 TQString repeatStr = i18n( "(repeat string, interval string)", "(%1, %2)" ). 04198 arg( countStr, intervalStr ); 04199 remStr = remStr + ' ' + repeatStr; 04200 04201 } 04202 reminderStringList << remStr; 04203 } 04204 } 04205 04206 return reminderStringList; 04207 }