/* This file is part of libkcal. Copyright (c) 2001 Cornelius Schumacher Copyright (c) 2004 Reinhold Kainhofer Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "incidenceformatter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KCal; /******************* * General helpers *******************/ static TQString htmlAddLink( const TQString &ref, const TQString &text, bool newline = true ) { TQString tmpStr( "" + text + "" ); if ( newline ) tmpStr += "\n"; return tmpStr; } static TQString htmlAddTag( const TQString & tag, const TQString & text ) { int numLineBreaks = text.contains( "\n" ); TQString str = "<" + tag + ">"; TQString tmpText = text; TQString tmpStr = str; if( numLineBreaks >= 0 ) { if ( numLineBreaks > 0) { int pos = 0; TQString tmp; for( int i = 0; i <= numLineBreaks; i++ ) { pos = tmpText.find( "\n" ); tmp = tmpText.left( pos ); tmpText = tmpText.right( tmpText.length() - pos - 1 ); tmpStr += tmp + "
"; } } else { tmpStr += tmpText; } } tmpStr += ""; return tmpStr; } static bool iamAttendee( Attendee *attendee ) { // Check if I'm this attendee bool iam = false; KEMailSettings settings; TQStringList profiles = settings.profiles(); for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { settings.setProfile( *it ); if ( settings.getSetting( KEMailSettings::EmailAddress ) == attendee->email() ) { iam = true; break; } } return iam; } static bool iamOrganizer( Incidence *incidence ) { // Check if I'm the organizer for this incidence if ( !incidence ) { return false; } bool iam = false; KEMailSettings settings; TQStringList profiles = settings.profiles(); for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { settings.setProfile( *it ); if ( settings.getSetting( KEMailSettings::EmailAddress ) == incidence->organizer().email() ) { iam = true; break; } } return iam; } static bool senderIsOrganizer( Incidence *incidence, const TQString &sender ) { // Check if the specified sender is the organizer if ( !incidence || sender.isEmpty() ) { return true; } bool isorg = true; TQString senderName, senderEmail; if ( KPIM::getNameAndMail( sender, senderName, senderEmail ) ) { // for this heuristic, we say the sender is the organizer if either the name or the email match. if ( incidence->organizer().email() != senderEmail && incidence->organizer().name() != senderName ) { isorg = false; } } return isorg; } static TQString firstAttendeeName( Incidence *incidence, const TQString &defName ) { TQString name; if ( !incidence ) { return name; } Attendee::List attendees = incidence->attendees(); if( attendees.count() > 0 ) { Attendee *attendee = *attendees.begin(); name = attendee->name(); if ( name.isEmpty() ) { name = attendee->email(); } if ( name.isEmpty() ) { name = defName; } } return name; } /******************************************************************* * Helper functions for the extensive display (display viewer) *******************************************************************/ static TQString displayViewLinkPerson( const TQString& email, TQString name, TQString uid ) { // Make the search, if there is an email address to search on, // and either name or uid is missing if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) { TDEABC::AddressBook *add_book = TDEABC::StdAddressBook::self( true ); TDEABC::Addressee::List addressList = add_book->findByEmail( email ); if ( !addressList.isEmpty() ) { TDEABC::Addressee o = addressList.first(); if ( !o.isEmpty() && addressList.size() < 2 ) { if ( name.isEmpty() ) { // No name set, so use the one from the addressbook name = o.formattedName(); } uid = o.uid(); } else { // Email not found in the addressbook. Don't make a link uid = TQString(); } } } // Show the attendee TQString tmpString; if ( !uid.isEmpty() ) { // There is a UID, so make a link to the addressbook if ( name.isEmpty() ) { // Use the email address for text tmpString += htmlAddLink( "uid:" + uid, email ); } else { tmpString += htmlAddLink( "uid:" + uid, name ); } } else { // No UID, just show some text tmpString += ( name.isEmpty() ? email : name ); } // Make the mailto link if ( !email.isEmpty() ) { KURL mailto; mailto.setProtocol( "mailto" ); mailto.setPath( email ); const TQString iconPath = TDEGlobal::iconLoader()->iconPath( "mail-message-new", TDEIcon::Small ); tmpString += " " + htmlAddLink( mailto.url(), "" ); } return tmpString; } static TQString displayViewFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role ) { TQString tmpStr; Attendee::List::ConstIterator it; Attendee::List attendees = incidence->attendees(); for ( it = attendees.begin(); it != attendees.end(); ++it ) { Attendee *a = *it; if ( a->role() != role ) { // skip this role continue; } if ( a->email() == incidence->organizer().email() ) { // skip attendee that is also the organizer continue; } tmpStr += displayViewLinkPerson( a->email(), a->name(), a->uid() ); if ( !a->delegator().isEmpty() ) { tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); } if ( !a->delegate().isEmpty() ) { tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); } tmpStr += "
"; } if ( tmpStr.endsWith( "
" ) ) { tmpStr.truncate( tmpStr.length() - 4 ); } return tmpStr; } static TQString displayViewFormatAttendees( Incidence *incidence ) { TQString tmpStr, str; // Add organizer link int attendeeCount = incidence->attendees().count(); if ( attendeeCount > 1 || ( attendeeCount == 1 && incidence->organizer().email() != incidence->attendees().first()->email() ) ) { tmpStr += ""; tmpStr += "" + i18n( "Organizer:" ) + ""; tmpStr += "" + displayViewLinkPerson( incidence->organizer().email(), incidence->organizer().name(), TQString() ) + ""; tmpStr += ""; } // Add "chair" str = displayViewFormatAttendeeRoleList( incidence, Attendee::Chair ); if ( !str.isEmpty() ) { tmpStr += ""; tmpStr += "" + i18n( "Chair:" ) + ""; tmpStr += "" + str + ""; tmpStr += ""; } // Add required participants str = displayViewFormatAttendeeRoleList( incidence, Attendee::ReqParticipant ); if ( !str.isEmpty() ) { tmpStr += ""; tmpStr += "" + i18n( "Required Participants:" ) + ""; tmpStr += "" + str + ""; tmpStr += ""; } // Add optional participants str = displayViewFormatAttendeeRoleList( incidence, Attendee::OptParticipant ); if ( !str.isEmpty() ) { tmpStr += ""; tmpStr += "" + i18n( "Optional Participants:" ) + ""; tmpStr += "" + str + ""; tmpStr += ""; } // Add observers str = displayViewFormatAttendeeRoleList( incidence, Attendee::NonParticipant ); if ( !str.isEmpty() ) { tmpStr += ""; tmpStr += "" + i18n( "Observers:" ) + ""; tmpStr += "" + str + ""; tmpStr += ""; } return tmpStr; } static TQString displayViewFormatAttachments( Incidence *incidence ) { TQString tmpStr; Attachment::List as = incidence->attachments(); Attachment::List::ConstIterator it; uint count = 0; for( it = as.begin(); it != as.end(); ++it ) { count++; if ( (*it)->isUri() ) { TQString name; if ( (*it)->uri().startsWith( "kmail:" ) ) { name = i18n( "Show mail" ); } else { if ( (*it)->label().isEmpty() ) { name = (*it)->uri(); } else { name = (*it)->label(); } } tmpStr += htmlAddLink( (*it)->uri(), name ); } else { tmpStr += htmlAddLink( "ATTACH:" + incidence->uid() + ':' + (*it)->label(), (*it)->label(), false ); } if ( count < as.count() ) { tmpStr += "
"; } } return tmpStr; } static TQString displayViewFormatCategories( Incidence *incidence ) { // We do not use Incidence::categoriesStr() since it does not have whitespace return incidence->categories().join( ", " ); } static TQString displayViewFormatCreationDate( Incidence *incidence ) { return i18n( "Creation date: %1" ). arg( IncidenceFormatter::dateTimeToString( incidence->created(), false, true ) ); } static TQString displayViewFormatBirthday( Event *event ) { if ( !event ) { return TQString(); } if ( event->customProperty("KABC","BIRTHDAY") != "YES" ) { return TQString(); } TQString uid = event->customProperty("KABC","UID-1"); TQString name = event->customProperty("KABC","NAME-1"); TQString email= event->customProperty("KABC","EMAIL-1"); TQString tmpStr = displayViewLinkPerson( email, name, uid ); if ( event->customProperty( "KABC", "ANNIVERSARY") == "YES" ) { uid = event->customProperty("KABC","UID-2"); name = event->customProperty("KABC","NAME-2"); email= event->customProperty("KABC","EMAIL-2"); tmpStr += "
"; tmpStr += displayViewLinkPerson( email, name, uid ); } return tmpStr; } static TQString displayViewFormatHeader( Incidence *incidence ) { TQString tmpStr = ""; // show icons { tmpStr += ""; } tmpStr += ""; tmpStr += "
"; if ( incidence->type() == "Event" ) { TQString iconPath; if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) { if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) { iconPath = TDEGlobal::iconLoader()->iconPath( "calendaranniversary", TDEIcon::Small ); } else { iconPath = TDEGlobal::iconLoader()->iconPath( "calendarbirthday", TDEIcon::Small ); } } else { iconPath = TDEGlobal::iconLoader()->iconPath( "appointment", TDEIcon::Small ); } tmpStr += ""; } if ( incidence->type() == "Todo" ) { tmpStr += "iconPath( "todo", TDEIcon::Small ) + "\">"; } if ( incidence->type() == "Journal" ) { tmpStr += "iconPath( "journal", TDEIcon::Small ) + "\">"; } if ( incidence->isAlarmEnabled() ) { tmpStr += "iconPath( "bell", TDEIcon::Small ) + "\">"; } if ( incidence->doesRecur() ) { tmpStr += "iconPath( "recur", TDEIcon::Small ) + "\">"; } if ( incidence->isReadOnly() ) { tmpStr += "iconPath( "readonlyevent", TDEIcon::Small ) + "\">"; } tmpStr += ""; tmpStr += "" + incidence->summary() + ""; tmpStr += "
"; return tmpStr; } static TQString displayViewFormatEvent( Calendar *calendar, Event *event, const TQDate &date ) { if ( !event ) { return TQString(); } TQString tmpStr = displayViewFormatHeader( event ); tmpStr += ""; tmpStr += ""; tmpStr += ""; if ( calendar ) { TQString calStr = IncidenceFormatter::resourceString( calendar, event ); if ( !calStr.isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } } if ( !event->location().isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } TQDateTime startDt = event->dtStart(); TQDateTime endDt = event->dtEnd(); if ( event->doesRecur() ) { if ( date.isValid() ) { TQDateTime dt( date, TQTime( 0, 0, 0 ) ); int diffDays = startDt.daysTo( dt ); dt = dt.addSecs( -1 ); startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() ); if ( event->hasEndDate() ) { endDt = endDt.addDays( diffDays ); if ( startDt > endDt ) { startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() ); endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) ); } } } } tmpStr += ""; if ( event->doesFloat() ) { if ( event->isMultiDay() ) { tmpStr += ""; tmpStr += ""; } else { tmpStr += ""; tmpStr += ""; } } else { if ( event->isMultiDay() ) { tmpStr += ""; tmpStr += ""; } else { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; if ( event->hasEndDate() && startDt != endDt ) { tmpStr += ""; } else { tmpStr += ""; } } } tmpStr += ""; TQString durStr = IncidenceFormatter::durationString( event ); if ( !durStr.isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } if ( event->doesRecur() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } if ( event->customProperty("KABC","BIRTHDAY")== "YES" ) { tmpStr += ""; if ( event->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) { tmpStr += ""; } else { tmpStr += ""; } tmpStr += ""; tmpStr += ""; tmpStr += "
" + i18n( "Calendar:" ) + "" + calStr + "
" + i18n( "Location:" ) + "" + event->location() + "
" + i18n( "Date:" ) + "" + i18n(" - ","%1 - %2"). arg( IncidenceFormatter::dateToString( startDt, false ) ). arg( IncidenceFormatter::dateToString( endDt, false ) ) + "" + i18n( "Date:" ) + "" + i18n("date as string","%1"). arg( IncidenceFormatter::dateToString( startDt, false ) ) + "" + i18n( "Date:" ) + "" + i18n(" - ","%1 - %2"). arg( IncidenceFormatter::dateToString( startDt, false ) ). arg( IncidenceFormatter::dateToString( endDt, false ) ) + "" + i18n( "Date:" ) + "" + i18n("date as string","%1"). arg( IncidenceFormatter::dateToString( startDt, false ) ) + "
" + i18n( "Time:" ) + "" + i18n(" - ","%1 - %2"). arg( IncidenceFormatter::timeToString( startDt, true ) ). arg( IncidenceFormatter::timeToString( endDt, true ) ) + "" + IncidenceFormatter::timeToString( startDt, true ) + "
" + i18n( "Duration:" ) + "" + durStr + "
" + i18n( "Recurrence:" ) + "" + IncidenceFormatter::recurrenceString( event ) + "
" + i18n( "Anniversary:" ) + "" + i18n( "Birthday:" ) + "" + displayViewFormatBirthday( event ) + "
"; return tmpStr; } if ( !event->description().isEmpty() ) { TQString description = event->description(); // Regular expression to match URLs TQRegExp urlRegex("https?://[^\\s]+"); int pos = 0; while ((pos = urlRegex.search(description, pos)) != -1) { TQString url = urlRegex.cap(0); TQString link = "" + url + ""; description.replace(pos, url.length(), link); pos += link.length(); } tmpStr += ""; tmpStr += "" + i18n( "Description:" ) + ""; tmpStr += "" + description + ""; tmpStr += ""; } // TODO: print comments? int reminderCount = event->alarms().count(); if ( reminderCount > 0 && event->isAlarmEnabled() ) { tmpStr += ""; tmpStr += "" + i18n( "Reminder:", "%n Reminders:", reminderCount ) + ""; tmpStr += "" + IncidenceFormatter::reminderStringList( event ).join( "
" ) + ""; tmpStr += ""; } tmpStr += displayViewFormatAttendees( event ); int categoryCount = event->categories().count(); if ( categoryCount > 0 ) { tmpStr += ""; tmpStr += "" + i18n( "Category:", "%n Categories:", categoryCount ) + ""; tmpStr += "" + displayViewFormatCategories( event ) + ""; tmpStr += ""; } int attachmentCount = event->attachments().count(); if ( attachmentCount > 0 ) { tmpStr += ""; tmpStr += "" + i18n( "Attachment:", "%n Attachments:", attachmentCount ) + ""; tmpStr += "" + displayViewFormatAttachments( event ) + ""; tmpStr += ""; } tmpStr += ""; tmpStr += "" + displayViewFormatCreationDate( event ) + ""; return tmpStr; } static TQString displayViewFormatTodo( Calendar *calendar, Todo *todo, const TQDate &date ) { if ( !todo ) { return TQString(); } TQString tmpStr = displayViewFormatHeader( todo ); tmpStr += ""; tmpStr += ""; tmpStr += ""; if ( calendar ) { TQString calStr = IncidenceFormatter::resourceString( calendar, todo ); if ( !calStr.isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } } if ( !todo->location().isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } if ( todo->hasStartDate() && todo->dtStart().isValid() ) { TQDateTime startDt = todo->dtStart(); if ( todo->doesRecur() ) { if ( date.isValid() ) { startDt.setDate( date ); } } tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } if ( todo->hasDueDate() && todo->dtDue().isValid() ) { TQDateTime dueDt = todo->dtDue(); if ( todo->doesRecur() ) { if ( date.isValid() ) { TQDateTime dt( date, TQTime( 0, 0, 0 ) ); dt = dt.addSecs( -1 ); dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() ); } } tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } TQString durStr = IncidenceFormatter::durationString( todo ); if ( !durStr.isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } if ( todo->doesRecur() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } if ( !todo->description().isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } // TODO: print comments? int reminderCount = todo->alarms().count(); if ( reminderCount > 0 && todo->isAlarmEnabled() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } tmpStr += displayViewFormatAttendees( todo ); int categoryCount = todo->categories().count(); if ( categoryCount > 0 ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } if ( todo->priority() > 0 ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } tmpStr += ""; if ( todo->isCompleted() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; int attachmentCount = todo->attachments().count(); if ( attachmentCount > 0 ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } tmpStr += "
" + i18n( "Calendar:" ) + "" + calStr + "
" + i18n( "Location:" ) + "" + todo->location() + "
" + i18n( "Start:" ) + "" + IncidenceFormatter::dateTimeToString( startDt, todo->doesFloat(), false ) + "
" + i18n( "Due:" ) + "" + IncidenceFormatter::dateTimeToString( dueDt, todo->doesFloat(), false ) + "
" + i18n( "Duration:" ) + "" + durStr + "
" + i18n( "Recurrence:" ) + "" + IncidenceFormatter::recurrenceString( todo ) + "
" + i18n( "Description:" ) + "" + todo->description() + "
" + i18n( "Reminder:", "%n Reminders:", reminderCount ) + "" + IncidenceFormatter::reminderStringList( todo ).join( "
" ) + "
" + i18n( "Category:", "%n Categories:", categoryCount ) + "" + displayViewFormatCategories( todo ) + "
" + i18n( "Priority:" ) + ""; tmpStr += TQString::number( todo->priority() ); tmpStr += "
" + i18n( "Completed:" ) + ""; tmpStr += todo->completedStr(); } else { tmpStr += "" + i18n( "Percent Done:" ) + ""; tmpStr += i18n( "%1%" ).arg( todo->percentComplete() ); } tmpStr += "
" + i18n( "Attachment:", "Attachments:", attachmentCount ) + "" + displayViewFormatAttachments( todo ) + "
"; tmpStr += "" + displayViewFormatCreationDate( todo ) + ""; return tmpStr; } static TQString displayViewFormatJournal( Calendar *calendar, Journal *journal ) { if ( !journal ) { return TQString(); } TQString tmpStr = displayViewFormatHeader( journal ); tmpStr += ""; tmpStr += ""; tmpStr += ""; if ( calendar ) { TQString calStr = IncidenceFormatter::resourceString( calendar, journal ); if ( !calStr.isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } } tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; if ( !journal->description().isEmpty() ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } int categoryCount = journal->categories().count(); if ( categoryCount > 0 ) { tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } tmpStr += "
" + i18n( "Calendar:" ) + "" + calStr + "
" + i18n( "Date:" ) + "" + IncidenceFormatter::dateToString( journal->dtStart(), false ) + "
" + i18n( "Description:" ) + "" + journal->description() + "
" + i18n( "Category:", "%n Categories:", categoryCount ) + "" + displayViewFormatCategories( journal ) + "
"; tmpStr += "" + displayViewFormatCreationDate( journal ) + ""; return tmpStr; } static TQString displayViewFormatFreeBusy( Calendar * /*calendar*/, FreeBusy *fb ) { if ( !fb ) { return TQString(); } TQString tmpStr = htmlAddTag( "h2", htmlAddTag( "b", i18n("Free/Busy information for %1"). arg( fb->organizer().fullName() ) ) ); tmpStr += htmlAddTag( "h4", i18n("Busy times in date range %1 - %2:"). arg( IncidenceFormatter::dateToString( fb->dtStart(), true ) ). arg( IncidenceFormatter::dateToString( fb->dtEnd(), true ) ) ); TQValueList periods = fb->busyPeriods(); TQString text = htmlAddTag( "em", htmlAddTag( "b", i18n("Busy:") ) ); TQValueList::iterator it; for ( it = periods.begin(); it != periods.end(); ++it ) { Period per = *it; if ( per.hasDuration() ) { int dur = per.duration().asSeconds(); TQString cont; if ( dur >= 3600 ) { cont += i18n("1 hour ", "%n hours ", dur / 3600 ); dur %= 3600; } if ( dur >= 60 ) { cont += i18n("1 minute ", "%n minutes ", dur / 60); dur %= 60; } if ( dur > 0 ) { cont += i18n("1 second", "%n seconds", dur); } text += i18n("startDate for duration", "%1 for %2"). arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ). arg( cont ); text += "
"; } else { if ( per.start().date() == per.end().date() ) { text += i18n("date, fromTime - toTime ", "%1, %2 - %3"). arg( IncidenceFormatter::dateToString( per.start().date(), true ) ). arg( IncidenceFormatter::timeToString( per.start(), true ) ). arg( IncidenceFormatter::timeToString( per.end(), true ) ); } else { text += i18n("fromDateTime - toDateTime", "%1 - %2"). arg( IncidenceFormatter::dateTimeToString( per.start(), false, true ) ). arg( IncidenceFormatter::dateTimeToString( per.end(), false, true ) ); } text += "
"; } } tmpStr += htmlAddTag( "p", text ); return tmpStr; } class IncidenceFormatter::EventViewerVisitor : public IncidenceBase::Visitor { public: EventViewerVisitor() : mCalendar( 0 ), mResult( "" ) {} bool act( Calendar *calendar, IncidenceBase *incidence, const TQDate &date ) { mCalendar = calendar; mDate = date; mResult = ""; return incidence->accept( *this ); } TQString result() const { return mResult; } protected: bool visit( Event *event ) { mResult = displayViewFormatEvent( mCalendar, event, mDate ); return !mResult.isEmpty(); } bool visit( Todo *todo ) { mResult = displayViewFormatTodo( mCalendar, todo, mDate ); return !mResult.isEmpty(); } bool visit( Journal *journal ) { mResult = displayViewFormatJournal( mCalendar, journal ); return !mResult.isEmpty(); } bool visit( FreeBusy *fb ) { mResult = displayViewFormatFreeBusy( mCalendar, fb ); return !mResult.isEmpty(); } protected: Calendar *mCalendar; TQDate mDate; TQString mResult; }; TQString IncidenceFormatter::extensiveDisplayString( IncidenceBase *incidence ) { return extensiveDisplayStr( 0, incidence, TQDate() ); } TQString IncidenceFormatter::extensiveDisplayStr( Calendar *calendar, IncidenceBase *incidence, const TQDate &date ) { if ( !incidence ) { return TQString(); } EventViewerVisitor v; if ( v.act( calendar, incidence, date ) ) { return v.result(); } else { return TQString(); } } /*********************************************************************** * Helper functions for the body part formatter of kmail (Invitations) ***********************************************************************/ static TQString string2HTML( const TQString& str ) { return TQStyleSheet::convertFromPlainText(str, TQStyleSheetItem::WhiteSpaceNormal); } static TQString cleanHtml( const TQString &html ) { TQRegExp rx( "]*>(.*)" ); rx.setCaseSensitive( false ); rx.search( html ); TQString body = rx.cap( 1 ); return TQStyleSheet::escape( body.remove( TQRegExp( "<[^>]*>" ) ).stripWhiteSpace() ); } static TQString eventStartTimeStr( Event *event ) { TQString tmp; if ( !event->doesFloat() ) { tmp = i18n( "%1: Start Date, %2: Start Time", "%1 %2" ). arg( IncidenceFormatter::dateToString( event->dtStart(), true ), IncidenceFormatter::timeToString( event->dtStart(), true ) ); } else { tmp = i18n( "%1: Start Date", "%1 (all day)" ). arg( IncidenceFormatter::dateToString( event->dtStart(), true ) ); } return tmp; } static TQString eventEndTimeStr( Event *event ) { TQString tmp; if ( event->hasEndDate() && event->dtEnd().isValid() ) { if ( !event->doesFloat() ) { tmp = i18n( "%1: End Date, %2: End Time", "%1 %2" ). arg( IncidenceFormatter::dateToString( event->dtEnd(), true ), IncidenceFormatter::timeToString( event->dtEnd(), true ) ); } else { tmp = i18n( "%1: End Date", "%1 (all day)" ). arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) ); } } return tmp; } static TQString invitationRow( const TQString &cell1, const TQString &cell2 ) { return "" + cell1 + "" + cell2 + "\n"; } static Attendee *findDelegatedFromMyAttendee( Incidence *incidence ) { // Return the first attendee that was delegated-from me Attendee *attendee = 0; if ( !incidence ) { return attendee; } KEMailSettings settings; TQStringList profiles = settings.profiles(); for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { settings.setProfile( *it ); TQString delegatorName, delegatorEmail; Attendee::List attendees = incidence->attendees(); Attendee::List::ConstIterator it2; for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) { Attendee *a = *it2; KPIM::getNameAndMail( a->delegator(), delegatorName, delegatorEmail ); if ( settings.getSetting( KEMailSettings::EmailAddress ) == delegatorEmail ) { attendee = a; break; } } } return attendee; } static Attendee *findMyAttendee( Incidence *incidence ) { // Return the attendee for the incidence that is probably me Attendee *attendee = 0; if ( !incidence ) { return attendee; } KEMailSettings settings; TQStringList profiles = settings.profiles(); for( TQStringList::Iterator it=profiles.begin(); it!=profiles.end(); ++it ) { settings.setProfile( *it ); Attendee::List attendees = incidence->attendees(); Attendee::List::ConstIterator it2; for ( it2 = attendees.begin(); it2 != attendees.end(); ++it2 ) { Attendee *a = *it2; if ( settings.getSetting( KEMailSettings::EmailAddress ) == a->email() ) { attendee = a; break; } } } return attendee; } static Attendee *findAttendee( Incidence *incidence, const TQString &email ) { // Search for an attendee by email address Attendee *attendee = 0; if ( !incidence ) { return attendee; } Attendee::List attendees = incidence->attendees(); Attendee::List::ConstIterator it; for ( it = attendees.begin(); it != attendees.end(); ++it ) { Attendee *a = *it; if ( email == a->email() ) { attendee = a; break; } } return attendee; } static bool rsvpRequested( Incidence *incidence ) { if ( !incidence ) { return false; } //use a heuristic to determine if a response is requested. bool rsvp = true; // better send superfluously than not at all Attendee::List attendees = incidence->attendees(); Attendee::List::ConstIterator it; for ( it = attendees.begin(); it != attendees.end(); ++it ) { if ( it == attendees.begin() ) { rsvp = (*it)->RSVP(); // use what the first one has } else { if ( (*it)->RSVP() != rsvp ) { rsvp = true; // they differ, default break; } } } return rsvp; } static TQString rsvpRequestedStr( bool rsvpRequested, const TQString &role ) { if ( rsvpRequested ) { if ( role.isEmpty() ) { return i18n( "Your response is requested" ); } else { return i18n( "Your response as %1 is requested" ).arg( role ); } } else { if ( role.isEmpty() ) { return i18n( "No response is necessary" ); } else { return i18n( "No response as %1 is necessary" ).arg( role ); } } } static TQString myStatusStr( Incidence *incidence ) { TQString ret; Attendee *a = findMyAttendee( incidence ); if ( a && a->status() != Attendee::NeedsAction && a->status() != Attendee::Delegated ) { ret = i18n( "(Note: the Organizer preset your response to %1)" ). arg( Attendee::statusName( a->status() ) ); } return ret; } static TQString invitationPerson( const TQString& email, TQString name, TQString uid ) { // Make the search, if there is an email address to search on, // and either name or uid is missing if ( !email.isEmpty() && ( name.isEmpty() || uid.isEmpty() ) ) { TDEABC::AddressBook *add_book = TDEABC::StdAddressBook::self( true ); TDEABC::Addressee::List addressList = add_book->findByEmail( email ); if ( !addressList.isEmpty() ) { TDEABC::Addressee o = addressList.first(); if ( !o.isEmpty() && addressList.size() < 2 ) { if ( name.isEmpty() ) { // No name set, so use the one from the addressbook name = o.formattedName(); } uid = o.uid(); } else { // Email not found in the addressbook. Don't make a link uid = TQString(); } } } // Show the attendee TQString tmpString; if ( !uid.isEmpty() ) { // There is a UID, so make a link to the addressbook if ( name.isEmpty() ) { // Use the email address for text tmpString += htmlAddLink( "uid:" + uid, email ); } else { tmpString += htmlAddLink( "uid:" + uid, name ); } } else { // No UID, just show some text tmpString += ( name.isEmpty() ? email : name ); } tmpString += '\n'; // Make the mailto link if ( !email.isEmpty() ) { KCal::Person person( name, email ); KURL mailto; mailto.setProtocol( "mailto" ); mailto.setPath( person.fullName() ); const TQString iconPath = TDEGlobal::iconLoader()->iconPath( "mail-message-new", TDEIcon::Small ); tmpString += " " + htmlAddLink( mailto.url(), "" ) ; } tmpString += "\n"; return tmpString; } static TQString invitationsDetailsIncidence( Incidence *incidence, bool noHtmlMode ) { // if description and comment -> use both // if description, but no comment -> use the desc as the comment (and no desc) // if comment, but no description -> use the comment and no description TQString html; TQString descr; TQStringList comments; if ( incidence->comments().isEmpty() ) { if ( !incidence->description().isEmpty() ) { // use description as comments if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) { comments << string2HTML( incidence->description() ); } else { comments << incidence->description(); if ( noHtmlMode ) { comments[0] = cleanHtml( comments[0] ); } comments[0] = htmlAddTag( "p", comments[0] ); } } //else desc and comments are empty } else { // non-empty comments TQStringList cl = incidence->comments(); uint i = 0; for( TQStringList::Iterator it=cl.begin(); it!=cl.end(); ++it ) { if ( !TQStyleSheet::mightBeRichText( *it ) ) { comments.append( string2HTML( *it ) ); } else { if ( noHtmlMode ) { comments.append( cleanHtml( "" + (*it) + "" ) ); } else { comments.append( *it ); } } i++; } if ( !incidence->description().isEmpty() ) { // use description too if ( !TQStyleSheet::mightBeRichText( incidence->description() ) ) { descr = string2HTML( incidence->description() ); } else { descr = incidence->description(); if ( noHtmlMode ) { descr = cleanHtml( descr ); } descr = htmlAddTag( "p", descr ); } } } if( !descr.isEmpty() ) { html += "

"; html += ""; html += ""; html += ""; html += "
" + htmlAddTag( "u", i18n( "Description:" ) ) + "
" + descr + "
"; } if ( !comments.isEmpty() ) { html += "

"; html += ""; html += ""; html += ""; html += "
" + htmlAddTag( "u", i18n( "Comments:" ) ) + "
"; if ( comments.count() > 1 ) { html += "
    "; for ( uint i=0; i < comments.count(); ++i ) { html += "
  • " + comments[i] + "
  • "; } html += "
"; } else { html += comments[0]; } html += "
"; } return html; } static TQString invitationDetailsEvent( Event* event, bool noHtmlMode ) { // Invitation details are formatted into an HTML table if ( !event ) { return TQString(); } TQString sSummary = i18n( "Summary unspecified" ); if ( !event->summary().isEmpty() ) { if ( !TQStyleSheet::mightBeRichText( event->summary() ) ) { sSummary = TQStyleSheet::escape( event->summary() ); } else { sSummary = event->summary(); if ( noHtmlMode ) { sSummary = cleanHtml( sSummary ); } } } TQString sLocation = i18n( "Location unspecified" ); if ( !event->location().isEmpty() ) { if ( !TQStyleSheet::mightBeRichText( event->location() ) ) { sLocation = TQStyleSheet::escape( event->location() ); } else { sLocation = event->location(); if ( noHtmlMode ) { sLocation = cleanHtml( sLocation ); } } } TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" ); TQString html = TQString("

\n").arg(dir); html += "\n"; // Invitation summary & location rows html += invitationRow( i18n( "What:" ), sSummary ); html += invitationRow( i18n( "Where:" ), sLocation ); if (event->doesRecur() == true) { html += invitationRow( i18n( "First Start Time:" ), eventStartTimeStr( event ) ); html += invitationRow( i18n( "First End Time:" ), eventEndTimeStr( event ) ); } // else { // If a 1 day event if ( event->dtStart().date() == event->dtEnd().date() ) { html += invitationRow( i18n( "Date:" ), IncidenceFormatter::dateToString( event->dtStart(), false ) ); if ( !event->doesFloat() ) { html += invitationRow( i18n( "Time:" ), IncidenceFormatter::timeToString( event->dtStart(), true ) + " - " + IncidenceFormatter::timeToString( event->dtEnd(), true ) ); } } else { html += invitationRow( i18n( "Starting date of an event", "From:" ), IncidenceFormatter::dateToString( event->dtStart(), false ) ); if ( !event->doesFloat() ) { html += invitationRow( i18n( "Starting time of an event", "At:" ), IncidenceFormatter::timeToString( event->dtStart(), true ) ); } if ( event->hasEndDate() ) { html += invitationRow( i18n( "Ending date of an event", "To:" ), IncidenceFormatter::dateToString( event->dtEnd(), false ) ); if ( !event->doesFloat() ) { html += invitationRow( i18n( "Starting time of an event", "At:" ), IncidenceFormatter::timeToString( event->dtEnd(), true ) ); } } else { html += invitationRow( i18n( "Ending date of an event", "To:" ), i18n( "no end date specified" ) ); } } // } // Invitation Duration Row TQString durStr = IncidenceFormatter::durationString( event ); if ( !durStr.isEmpty() ) { html += invitationRow( i18n( "Duration:" ), durStr ); } // Recurrence Information Rows if ( event->doesRecur() ) { Recurrence *recur = event->recurrence(); html += invitationRow( i18n( "Recurrence:" ), IncidenceFormatter::recurrenceString( event ) ); DateList exceptions = recur->exDates(); if (exceptions.isEmpty() == false) { bool isFirstExRow; isFirstExRow = true; DateList::ConstIterator ex_iter; for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) { if (isFirstExRow == true) { isFirstExRow = false; html += invitationRow( i18n("Cancelled on:"), TDEGlobal::locale()->formatDate(* ex_iter ) ); } else { html += invitationRow(" ", TDEGlobal::locale()->formatDate(* ex_iter ) ); } } } } html += "
\n"; html += invitationsDetailsIncidence( event, noHtmlMode ); html += "
\n"; return html; } static TQString invitationDetailsTodo( Todo *todo, bool noHtmlMode ) { // Task details are formatted into an HTML table if ( !todo ) { return TQString(); } TQString sSummary = i18n( "Summary unspecified" ); if ( !todo->summary().isEmpty() ) { if ( !TQStyleSheet::mightBeRichText( todo->summary() ) ) { sSummary = TQStyleSheet::escape( todo->summary() ); } else { sSummary = todo->summary(); if ( noHtmlMode ) { sSummary = cleanHtml( sSummary ); } } } TQString sLocation = i18n( "Location unspecified" ); if ( !todo->location().isEmpty() ) { if ( !TQStyleSheet::mightBeRichText( todo->location() ) ) { sLocation = TQStyleSheet::escape( todo->location() ); } else { sLocation = todo->location(); if ( noHtmlMode ) { sLocation = cleanHtml( sLocation ); } } } TQString dir = ( TQApplication::reverseLayout() ? "rtl" : "ltr" ); TQString html = TQString("
\n").arg(dir); html += "\n"; // Invitation summary & location rows html += invitationRow( i18n( "What:" ), sSummary ); html += invitationRow( i18n( "Where:" ), sLocation ); if ( todo->hasStartDate() && todo->dtStart().isValid() ) { html += invitationRow( i18n( "Start Date:" ), IncidenceFormatter::dateToString( todo->dtStart(), false ) ); if ( !todo->doesFloat() ) { html += invitationRow( i18n( "Start Time:" ), IncidenceFormatter::timeToString( todo->dtStart(), false ) ); } } if ( todo->hasDueDate() && todo->dtDue().isValid() ) { html += invitationRow( i18n( "Due Date:" ), IncidenceFormatter::dateToString( todo->dtDue(), false ) ); if ( !todo->doesFloat() ) { html += invitationRow( i18n( "Due Time:" ), IncidenceFormatter::timeToString( todo->dtDue(), false ) ); } } else { html += invitationRow( i18n( "Due Date:" ), i18n( "Due Date: None", "None" ) ); } html += "
\n"; html += invitationsDetailsIncidence( todo, noHtmlMode ); return html; } static TQString invitationDetailsJournal( Journal *journal, bool noHtmlMode ) { if ( !journal ) { return TQString(); } TQString sSummary = i18n( "Summary unspecified" ); TQString sDescr = i18n( "Description unspecified" ); if ( ! journal->summary().isEmpty() ) { sSummary = journal->summary(); if ( noHtmlMode ) { sSummary = cleanHtml( sSummary ); } } if ( ! journal->description().isEmpty() ) { sDescr = journal->description(); if ( noHtmlMode ) { sDescr = cleanHtml( sDescr ); } } TQString html( "\n" ); html += invitationRow( i18n( "Summary:" ), sSummary ); html += invitationRow( i18n( "Date:" ), IncidenceFormatter::dateToString( journal->dtStart(), false ) ); html += invitationRow( i18n( "Description:" ), sDescr ); html += "
\n"; html += invitationsDetailsIncidence( journal, noHtmlMode ); return html; } static TQString invitationDetailsFreeBusy( FreeBusy *fb, bool /*noHtmlMode*/ ) { if ( !fb ) return TQString(); TQString html( "\n" ); html += invitationRow( i18n("Person:"), fb->organizer().fullName() ); html += invitationRow( i18n("Start date:"), IncidenceFormatter::dateToString( fb->dtStart(), true ) ); html += invitationRow( i18n("End date:"), TDEGlobal::locale()->formatDate( fb->dtEnd().date(), true ) ); html += "\n"; html += "\n"; TQValueList periods = fb->busyPeriods(); TQValueList::iterator it; for ( it = periods.begin(); it != periods.end(); ++it ) { Period per = *it; if ( per.hasDuration() ) { int dur = per.duration().asSeconds(); TQString cont; if ( dur >= 3600 ) { cont += i18n("1 hour ", "%n hours ", dur / 3600); dur %= 3600; } if ( dur >= 60 ) { cont += i18n("1 minute", "%n minutes ", dur / 60); dur %= 60; } if ( dur > 0 ) { cont += i18n("1 second", "%n seconds", dur); } html += invitationRow( TQString(), i18n("startDate for duration", "%1 for %2") .arg( TDEGlobal::locale()->formatDateTime( per.start(), false ) ) .arg(cont) ); } else { TQString cont; if ( per.start().date() == per.end().date() ) { cont = i18n("date, fromTime - toTime ", "%1, %2 - %3") .arg( TDEGlobal::locale()->formatDate( per.start().date() ) ) .arg( TDEGlobal::locale()->formatTime( per.start().time() ) ) .arg( TDEGlobal::locale()->formatTime( per.end().time() ) ); } else { cont = i18n("fromDateTime - toDateTime", "%1 - %2") .arg( TDEGlobal::locale()->formatDateTime( per.start(), false ) ) .arg( TDEGlobal::locale()->formatDateTime( per.end(), false ) ); } html += invitationRow( TQString(), cont ); } } html += "

Busy periods given in this free/busy object:
\n"; return html; } static bool replyMeansCounter( Incidence */*incidence*/ ) { return false; /** see kolab/issue 3665 for an example of when we might use this for something bool status = false; if ( incidence ) { // put code here that looks at the incidence and determines that // the reply is meant to be a counter proposal. We think this happens // with Outlook counter proposals, but we aren't sure how yet. if ( condition ) { status = true; } } return status; */ } static TQString invitationHeaderEvent( Event *event, Incidence *existingIncidence, ScheduleMessage *msg, const TQString &sender ) { if ( !msg || !event ) return TQString(); switch ( msg->method() ) { case Scheduler::Publish: return i18n( "This invitation has been published" ); case Scheduler::Request: if ( existingIncidence && event->revision() > 0 ) { return i18n( "This invitation has been updated by the organizer %1" ). arg( event->organizer().fullName() ); } if ( iamOrganizer( event ) ) { return i18n( "I created this invitation" ); } else { TQString orgStr; if ( !event->organizer().fullName().isEmpty() ) { orgStr = event->organizer().fullName(); } else if ( !event->organizer().email().isEmpty() ) { orgStr = event->organizer().email(); } if ( senderIsOrganizer( event, sender ) ) { if ( !orgStr.isEmpty() ) { return i18n( "You received an invitation from %1" ).arg( orgStr ); } else { return i18n( "You received an invitation" ); } } else { if ( !orgStr.isEmpty() ) { return i18n( "You received an invitation from %1 as a representative of %2" ). arg( sender, orgStr ); } else { return i18n( "You received an invitation from %1 as the organizer's representative" ). arg( sender ); } } } case Scheduler::Refresh: return i18n( "This invitation was refreshed" ); case Scheduler::Cancel: return i18n( "This invitation has been canceled" ); case Scheduler::Add: return i18n( "Addition to the invitation" ); case Scheduler::Reply: { if ( replyMeansCounter( event ) ) { return i18n( "%1 makes this counter proposal" ). arg( firstAttendeeName( event, i18n( "Sender" ) ) ); } Attendee::List attendees = event->attendees(); if( attendees.count() == 0 ) { kdDebug(5850) << "No attendees in the iCal reply!" << endl; return TQString(); } if( attendees.count() != 1 ) { kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " << "but is " << attendees.count() << endl; } TQString attendeeName = firstAttendeeName( event, i18n( "Sender" ) ); TQString delegatorName, dummy; Attendee* attendee = *attendees.begin(); KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy ); if ( delegatorName.isEmpty() ) { delegatorName = attendee->delegator(); } switch( attendee->status() ) { case Attendee::NeedsAction: return i18n( "%1 indicates this invitation still needs some action" ).arg( attendeeName ); case Attendee::Accepted: if ( event->revision() > 0 ) { if ( !sender.isEmpty() ) { return i18n( "This invitation has been updated by attendee %1" ).arg( sender ); } else { return i18n( "This invitation has been updated by an attendee" ); } } else { if ( delegatorName.isEmpty() ) { return i18n( "%1 accepts this invitation" ).arg( attendeeName ); } else { return i18n( "%1 accepts this invitation on behalf of %2" ). arg( attendeeName ).arg( delegatorName ); } } case Attendee::Tentative: if ( delegatorName.isEmpty() ) { return i18n( "%1 tentatively accepts this invitation" ). arg( attendeeName ); } else { return i18n( "%1 tentatively accepts this invitation on behalf of %2" ). arg( attendeeName ).arg( delegatorName ); } case Attendee::Declined: if ( delegatorName.isEmpty() ) { return i18n( "%1 declines this invitation" ).arg( attendeeName ); } else { return i18n( "%1 declines this invitation on behalf of %2" ). arg( attendeeName ).arg( delegatorName ); } case Attendee::Delegated: { TQString delegate, dummy; KPIM::getNameAndMail( attendee->delegate(), delegate, dummy ); if ( delegate.isEmpty() ) { delegate = attendee->delegate(); } if ( !delegate.isEmpty() ) { return i18n( "%1 has delegated this invitation to %2" ). arg( attendeeName ) .arg( delegate ); } else { return i18n( "%1 has delegated this invitation" ).arg( attendeeName ); } } case Attendee::Completed: return i18n( "This invitation is now completed" ); case Attendee::InProcess: return i18n( "%1 is still processing the invitation" ). arg( attendeeName ); default: return i18n( "Unknown response to this invitation" ); } break; } case Scheduler::Counter: return i18n( "%1 makes this counter proposal" ). arg( firstAttendeeName( event, i18n( "Sender" ) ) ); case Scheduler::Declinecounter: return i18n( "%1 declines the counter proposal" ). arg( firstAttendeeName( event, i18n( "Sender" ) ) ); case Scheduler::NoMethod: return i18n("Error: iMIP message with unknown method: '%1'"). arg( msg->method() ); } return TQString(); } static TQString invitationHeaderTodo( Todo *todo, Incidence *existingIncidence, ScheduleMessage *msg, const TQString &sender ) { if ( !msg || !todo ) { return TQString(); } switch ( msg->method() ) { case Scheduler::Publish: return i18n("This task has been published"); case Scheduler::Request: if ( existingIncidence && todo->revision() > 0 ) { return i18n( "This task has been updated by the organizer %1" ). arg( todo->organizer().fullName() ); } else { if ( iamOrganizer( todo ) ) { return i18n( "I created this task" ); } else { TQString orgStr; if ( !todo->organizer().fullName().isEmpty() ) { orgStr = todo->organizer().fullName(); } else if ( !todo->organizer().email().isEmpty() ) { orgStr = todo->organizer().email(); } if ( senderIsOrganizer( todo, sender ) ) { if ( !orgStr.isEmpty() ) { return i18n( "You have been assigned this task by %1" ).arg( orgStr ); } else { return i18n( "You have been assigned this task" ); } } else { if ( !orgStr.isEmpty() ) { return i18n( "You have been assigned this task by %1 as a representative of %2" ). arg( sender, orgStr ); } else { return i18n( "You have been assigned this task by %1 as the organizer's representative" ). arg( sender ); } } } } case Scheduler::Refresh: return i18n( "This task was refreshed" ); case Scheduler::Cancel: return i18n( "This task was canceled" ); case Scheduler::Add: return i18n( "Addition to the task" ); case Scheduler::Reply: { if ( replyMeansCounter( todo ) ) { return i18n( "%1 makes this counter proposal" ). arg( firstAttendeeName( todo, i18n( "Sender" ) ) ); } Attendee::List attendees = todo->attendees(); if( attendees.count() == 0 ) { kdDebug(5850) << "No attendees in the iCal reply!" << endl; return TQString(); } if( attendees.count() != 1 ) { kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " << "but is " << attendees.count() << endl; } TQString attendeeName = firstAttendeeName( todo, i18n( "Sender" ) ); TQString delegatorName, dummy; Attendee* attendee = *attendees.begin(); KPIM::getNameAndMail( attendee->delegator(), delegatorName, dummy ); if ( delegatorName.isEmpty() ) { delegatorName = attendee->delegator(); } switch( attendee->status() ) { case Attendee::NeedsAction: return i18n( "%1 indicates this task assignment still needs some action" ).arg( attendeeName ); case Attendee::Accepted: if ( todo->revision() > 0 ) { if ( !sender.isEmpty() ) { if ( todo->isCompleted() ) { return i18n( "This task has been completed by assignee %1" ).arg( sender ); } else { return i18n( "This task has been updated by assignee %1" ).arg( sender ); } } else { if ( todo->isCompleted() ) { return i18n( "This task has been completed by an assignee" ); } else { return i18n( "This task has been updated by an assignee" ); } } } else { if ( delegatorName.isEmpty() ) { return i18n( "%1 accepts this task" ).arg( attendeeName ); } else { return i18n( "%1 accepts this task on behalf of %2" ). arg( attendeeName ).arg( delegatorName ); } } case Attendee::Tentative: if ( delegatorName.isEmpty() ) { return i18n( "%1 tentatively accepts this task" ). arg( attendeeName ); } else { return i18n( "%1 tentatively accepts this task on behalf of %2" ). arg( attendeeName ).arg( delegatorName ); } case Attendee::Declined: if ( delegatorName.isEmpty() ) { return i18n( "%1 declines this task" ).arg( attendeeName ); } else { return i18n( "%1 declines this task on behalf of %2" ). arg( attendeeName ).arg( delegatorName ); } case Attendee::Delegated: { TQString delegate, dummy; KPIM::getNameAndMail( attendee->delegate(), delegate, dummy ); if ( delegate.isEmpty() ) { delegate = attendee->delegate(); } if ( !delegate.isEmpty() ) { return i18n( "%1 has delegated this request for the task to %2" ). arg( attendeeName ).arg( delegate ); } else { return i18n( "%1 has delegated this request for the task" ). arg( attendeeName ); } } case Attendee::Completed: return i18n( "The request for this task is now completed" ); case Attendee::InProcess: return i18n( "%1 is still processing the task" ). arg( attendeeName ); default: return i18n( "Unknown response to this task" ); } break; } case Scheduler::Counter: return i18n( "%1 makes this counter proposal" ). arg( firstAttendeeName( todo, i18n( "Sender" ) ) ); case Scheduler::Declinecounter: return i18n( "%1 declines the counter proposal" ). arg( firstAttendeeName( todo, i18n( "Sender" ) ) ); case Scheduler::NoMethod: return i18n( "Error: iMIP message with unknown method: '%1'" ). arg( msg->method() ); } return TQString(); } static TQString invitationHeaderJournal( Journal *journal, ScheduleMessage *msg ) { if ( !msg || !journal ) { return TQString(); } switch ( msg->method() ) { case Scheduler::Publish: return i18n("This journal has been published"); case Scheduler::Request: return i18n( "You have been assigned this journal" ); case Scheduler::Refresh: return i18n( "This journal was refreshed" ); case Scheduler::Cancel: return i18n( "This journal was canceled" ); case Scheduler::Add: return i18n( "Addition to the journal" ); case Scheduler::Reply: { if ( replyMeansCounter( journal ) ) { return i18n( "Sender makes this counter proposal" ); } Attendee::List attendees = journal->attendees(); if( attendees.count() == 0 ) { kdDebug(5850) << "No attendees in the iCal reply!" << endl; return TQString(); } if( attendees.count() != 1 ) { kdDebug(5850) << "Warning: attendeecount in the reply should be 1 " << "but is " << attendees.count() << endl; } Attendee* attendee = *attendees.begin(); switch( attendee->status() ) { case Attendee::NeedsAction: return i18n( "Sender indicates this journal assignment still needs some action" ); case Attendee::Accepted: return i18n( "Sender accepts this journal" ); case Attendee::Tentative: return i18n( "Sender tentatively accepts this journal" ); case Attendee::Declined: return i18n( "Sender declines this journal" ); case Attendee::Delegated: return i18n( "Sender has delegated this request for the journal" ); case Attendee::Completed: return i18n( "The request for this journal is now completed" ); case Attendee::InProcess: return i18n( "Sender is still processing the invitation" ); default: return i18n( "Unknown response to this journal" ); } break; } case Scheduler::Counter: return i18n( "Sender makes this counter proposal" ); case Scheduler::Declinecounter: return i18n( "Sender declines the counter proposal" ); case Scheduler::NoMethod: return i18n("Error: iMIP message with unknown method: '%1'"). arg( msg->method() ); } return TQString(); } static TQString invitationHeaderFreeBusy( FreeBusy *fb, ScheduleMessage *msg ) { if ( !msg || !fb ) { return TQString(); } switch ( msg->method() ) { case Scheduler::Publish: return i18n("This free/busy list has been published"); case Scheduler::Request: return i18n( "The free/busy list has been requested" ); case Scheduler::Refresh: return i18n( "This free/busy list was refreshed" ); case Scheduler::Cancel: return i18n( "This free/busy list was canceled" ); case Scheduler::Add: return i18n( "Addition to the free/busy list" ); case Scheduler::NoMethod: default: return i18n("Error: Free/Busy iMIP message with unknown method: '%1'"). arg( msg->method() ); } } static TQString invitationAttendees( Incidence *incidence ) { TQString tmpStr; if ( !incidence ) { return tmpStr; } if ( incidence->type() == "Todo" ) { tmpStr += htmlAddTag( "u", i18n( "Assignees" ) ); } else { tmpStr += htmlAddTag( "u", i18n( "Attendees" ) ); } tmpStr += "
"; int count=0; Attendee::List attendees = incidence->attendees(); if ( !attendees.isEmpty() ) { Attendee::List::ConstIterator it; for( it = attendees.begin(); it != attendees.end(); ++it ) { Attendee *a = *it; if ( !iamAttendee( a ) ) { count++; if ( count == 1 ) { tmpStr += ""; } tmpStr += ""; tmpStr += ""; tmpStr += ""; tmpStr += ""; } } } if ( count ) { tmpStr += "
"; tmpStr += invitationPerson( a->email(), a->name(), TQString() ); if ( !a->delegator().isEmpty() ) { tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); } if ( !a->delegate().isEmpty() ) { tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); } tmpStr += "" + a->statusStr() + "
"; } else { tmpStr += "" + i18n( "No attendee", "None" ) + ""; } return tmpStr; } static TQString invitationAttachments( InvitationFormatterHelper *helper, Incidence *incidence ) { TQString tmpStr; if ( !incidence ) { return tmpStr; } Attachment::List attachments = incidence->attachments(); if ( !attachments.isEmpty() ) { tmpStr += i18n( "Attached Documents:" ) + "
    "; Attachment::List::ConstIterator it; for( it = attachments.begin(); it != attachments.end(); ++it ) { Attachment *a = *it; tmpStr += "
  1. "; // Attachment icon KMimeType::Ptr mimeType = KMimeType::mimeType( a->mimeType() ); const TQString iconStr = mimeType ? mimeType->icon( a->uri(), false ) : TQString( "application-octet-stream" ); const TQString iconPath = TDEGlobal::iconLoader()->iconPath( iconStr, TDEIcon::Small ); if ( !iconPath.isEmpty() ) { tmpStr += ""; } tmpStr += helper->makeLink( "ATTACH:" + a->label(), a->label() ); tmpStr += "
  2. "; } tmpStr += "
"; } return tmpStr; } class IncidenceFormatter::ScheduleMessageVisitor : public IncidenceBase::Visitor { public: ScheduleMessageVisitor() : mExistingIncidence( 0 ), mMessage( 0 ) { mResult = ""; } bool act( IncidenceBase *incidence, Incidence *existingIncidence, ScheduleMessage *msg, const TQString &sender ) { mExistingIncidence = existingIncidence; mMessage = msg; mSender = sender; return incidence->accept( *this ); } TQString result() const { return mResult; } protected: TQString mResult; Incidence *mExistingIncidence; ScheduleMessage *mMessage; TQString mSender; }; class IncidenceFormatter::InvitationHeaderVisitor : public IncidenceFormatter::ScheduleMessageVisitor { protected: bool visit( Event *event ) { mResult = invitationHeaderEvent( event, mExistingIncidence, mMessage, mSender ); return !mResult.isEmpty(); } bool visit( Todo *todo ) { mResult = invitationHeaderTodo( todo, mExistingIncidence, mMessage, mSender ); return !mResult.isEmpty(); } bool visit( Journal *journal ) { mResult = invitationHeaderJournal( journal, mMessage ); return !mResult.isEmpty(); } bool visit( FreeBusy *fb ) { mResult = invitationHeaderFreeBusy( fb, mMessage ); return !mResult.isEmpty(); } }; class IncidenceFormatter::InvitationBodyVisitor : public IncidenceFormatter::ScheduleMessageVisitor { public: InvitationBodyVisitor( bool noHtmlMode ) : ScheduleMessageVisitor(), mNoHtmlMode( noHtmlMode ) {} protected: bool visit( Event *event ) { mResult = invitationDetailsEvent( event, mNoHtmlMode ); return !mResult.isEmpty(); } bool visit( Todo *todo ) { mResult = invitationDetailsTodo( todo, mNoHtmlMode ); return !mResult.isEmpty(); } bool visit( Journal *journal ) { mResult = invitationDetailsJournal( journal, mNoHtmlMode ); return !mResult.isEmpty(); } bool visit( FreeBusy *fb ) { mResult = invitationDetailsFreeBusy( fb, mNoHtmlMode ); return !mResult.isEmpty(); } private: bool mNoHtmlMode; }; class IncidenceFormatter::IncidenceCompareVisitor : public IncidenceBase::Visitor { public: IncidenceCompareVisitor() : mExistingIncidence(0) {} bool act( IncidenceBase *incidence, Incidence *existingIncidence, int method ) { Incidence *inc = dynamic_cast( incidence ); if ( !inc || !existingIncidence || inc->revision() <= existingIncidence->revision() ) return false; mExistingIncidence = existingIncidence; mMethod = method; return incidence->accept( *this ); } TQString result() const { if ( mChanges.isEmpty() ) { return TQString(); } TQString html = "
  • "; html += mChanges.join( "
  • " ); html += "
    "; return html; } protected: bool visit( Event *event ) { compareEvents( event, dynamic_cast( mExistingIncidence ) ); compareIncidences( event, mExistingIncidence, mMethod ); return !mChanges.isEmpty(); } bool visit( Todo *todo ) { compareTodos( todo, dynamic_cast( mExistingIncidence ) ); compareIncidences( todo, mExistingIncidence, mMethod ); return !mChanges.isEmpty(); } bool visit( Journal *journal ) { compareIncidences( journal, mExistingIncidence, mMethod ); return !mChanges.isEmpty(); } bool visit( FreeBusy *fb ) { Q_UNUSED( fb ); return !mChanges.isEmpty(); } private: void compareEvents( Event *newEvent, Event *oldEvent ) { if ( !oldEvent || !newEvent ) return; if ( oldEvent->dtStart() != newEvent->dtStart() || oldEvent->doesFloat() != newEvent->doesFloat() ) mChanges += i18n( "The invitation starting time has been changed from %1 to %2" ) .arg( eventStartTimeStr( oldEvent ) ).arg( eventStartTimeStr( newEvent ) ); if ( oldEvent->dtEnd() != newEvent->dtEnd() || oldEvent->doesFloat() != newEvent->doesFloat() ) mChanges += i18n( "The invitation ending time has been changed from %1 to %2" ) .arg( eventEndTimeStr( oldEvent ) ).arg( eventEndTimeStr( newEvent ) ); } void compareTodos( Todo *newTodo, Todo *oldTodo ) { if ( !oldTodo || !newTodo ) { return; } if ( !oldTodo->isCompleted() && newTodo->isCompleted() ) { mChanges += i18n( "The task has been completed" ); } if ( oldTodo->isCompleted() && !newTodo->isCompleted() ) { mChanges += i18n( "The task is no longer completed" ); } if ( oldTodo->percentComplete() != newTodo->percentComplete() ) { const TQString oldPer = i18n( "%1%" ).arg( oldTodo->percentComplete() ); const TQString newPer = i18n( "%1%" ).arg( newTodo->percentComplete() ); mChanges += i18n( "The task completed percentage has changed from %1 to %2" ). arg( oldPer, newPer ); } if ( !oldTodo->hasStartDate() && newTodo->hasStartDate() ) { mChanges += i18n( "A task starting time has been added" ); } if ( oldTodo->hasStartDate() && !newTodo->hasStartDate() ) { mChanges += i18n( "The task starting time has been removed" ); } if ( oldTodo->hasStartDate() && newTodo->hasStartDate() && oldTodo->dtStart() != newTodo->dtStart() ) { mChanges += i18n( "The task starting time has been changed from %1 to %2" ). arg( dateTimeToString( oldTodo->dtStart(), oldTodo->doesFloat(), false ), dateTimeToString( newTodo->dtStart(), newTodo->doesFloat(), false ) ); } if ( !oldTodo->hasDueDate() && newTodo->hasDueDate() ) { mChanges += i18n( "A task due time has been added" ); } if ( oldTodo->hasDueDate() && !newTodo->hasDueDate() ) { mChanges += i18n( "The task due time has been removed" ); } if ( oldTodo->hasDueDate() && newTodo->hasDueDate() && oldTodo->dtDue() != newTodo->dtDue() ) { mChanges += i18n( "The task due time has been changed from %1 to %2" ). arg( dateTimeToString( oldTodo->dtDue(), oldTodo->doesFloat(), false ), dateTimeToString( newTodo->dtDue(), newTodo->doesFloat(), false ) ); } } void compareIncidences( Incidence *newInc, Incidence *oldInc, int method ) { if ( !oldInc || !newInc ) return; if ( oldInc->summary() != newInc->summary() ) mChanges += i18n( "The summary has been changed to: \"%1\"" ).arg( newInc->summary() ); if ( oldInc->location() != newInc->location() ) mChanges += i18n( "The location has been changed to: \"%1\"" ).arg( newInc->location() ); if ( oldInc->description() != newInc->description() ) mChanges += i18n( "The description has been changed to: \"%1\"" ).arg( newInc->description() ); Attendee::List oldAttendees = oldInc->attendees(); Attendee::List newAttendees = newInc->attendees(); for ( Attendee::List::ConstIterator it = newAttendees.constBegin(); it != newAttendees.constEnd(); ++it ) { Attendee *oldAtt = oldInc->attendeeByMail( (*it)->email() ); if ( !oldAtt ) { mChanges += i18n( "Attendee %1 has been added" ).arg( (*it)->fullName() ); } else { if ( oldAtt->status() != (*it)->status() ) mChanges += i18n( "The status of attendee %1 has been changed to: %2" ). arg( (*it)->fullName() ).arg( (*it)->statusStr() ); } } if ( method == Scheduler::Request ) { for ( Attendee::List::ConstIterator it = oldAttendees.constBegin(); it != oldAttendees.constEnd(); ++it ) { if ( (*it)->email() != oldInc->organizer().email() ) { Attendee *newAtt = newInc->attendeeByMail( (*it)->email() ); if ( !newAtt ) { mChanges += i18n( "Attendee %1 has been removed" ).arg( (*it)->fullName() ); } } } } } private: Incidence *mExistingIncidence; int mMethod; TQStringList mChanges; }; TQString InvitationFormatterHelper::makeLink( const TQString &id, const TQString &text ) { if ( !id.startsWith( "ATTACH:" ) ) { TQString res = TQString( "%2" ). arg( generateLinkURL( id ), text ); return res; } else { // draw the attachment links in non-bold face TQString res = TQString( "%2" ). arg( generateLinkURL( id ), text ); return res; } } // Check if the given incidence is likely one that we own instead one from // a shared calendar (Kolab-specific) static bool incidenceOwnedByMe( Calendar *calendar, Incidence *incidence ) { CalendarResources *cal = dynamic_cast( calendar ); if ( !cal || !incidence ) { return true; } ResourceCalendar *res = cal->resource( incidence ); if ( !res ) { return true; } const TQString subRes = res->subresourceIdentifier( incidence ); if ( !subRes.contains( "/.INBOX.directory/" ) ) { return false; } return true; } // The spacer for the invitation buttons static TQString spacer = "   "; // The open & close table cell tags for the invitation buttons static TQString tdOpen = ""; static TQString tdClose = "" + spacer; static TQString responseButtons( Incidence *inc, bool rsvpReq, bool rsvpRec, InvitationFormatterHelper *helper ) { TQString html; if ( !helper ) { return html; } if ( !rsvpReq && ( inc && inc->revision() == 0 ) ) { // Record only html += tdOpen; html += helper->makeLink( "record", i18n( "[Record]" ) ); html += tdClose; // Move to trash html += tdOpen; html += helper->makeLink( "delete", i18n( "[Move to Trash]" ) ); html += tdClose; } else { // Accept html += tdOpen; html += helper->makeLink( "accept", i18n( "[Accept]" ) ); html += tdClose; // Tentative html += tdOpen; html += helper->makeLink( "accept_conditionally", i18n( "Accept conditionally", "[Accept cond.]" ) ); html += tdClose; // Counter proposal html += tdOpen; html += helper->makeLink( "counter", i18n( "[Counter proposal]" ) ); html += tdClose; // Decline html += tdOpen; html += helper->makeLink( "decline", i18n( "[Decline]" ) ); html += tdClose; } if ( !rsvpRec || ( inc && inc->revision() > 0 ) ) { // Delegate html += tdOpen; html += helper->makeLink( "delegate", i18n( "[Delegate]" ) ); html += tdClose; // Forward html += tdOpen; html += helper->makeLink( "forward", i18n( "[Forward]" ) ); html += tdClose; // Check calendar if ( inc && inc->type() == "Event" ) { html += tdOpen; html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) ); html += tdClose; } } return html; } static TQString counterButtons( Incidence *incidence, InvitationFormatterHelper *helper ) { TQString html; if ( !helper ) { return html; } // Accept proposal html += tdOpen; html += helper->makeLink( "accept_counter", i18n("[Accept]") ); html += tdClose; // Decline proposal html += tdOpen; html += helper->makeLink( "decline_counter", i18n("[Decline]") ); html += tdClose; // Check calendar if ( incidence && incidence->type() == "Event" ) { html += tdOpen; html += helper->makeLink( "check_calendar", i18n("[Check my calendar]" ) ); html += tdClose; } return html; } TQString IncidenceFormatter::formatICalInvitationHelper( TQString invitation, Calendar *mCalendar, InvitationFormatterHelper *helper, bool noHtmlMode, const TQString &sender ) { if ( invitation.isEmpty() ) { return TQString(); } ICalFormat format; // parseScheduleMessage takes the tz from the calendar, no need to set it manually here for the format! ScheduleMessage *msg = format.parseScheduleMessage( mCalendar, invitation ); if( !msg ) { kdDebug( 5850 ) << "Failed to parse the scheduling message" << endl; Q_ASSERT( format.exception() ); kdDebug( 5850 ) << format.exception()->message() << endl; return TQString(); } IncidenceBase *incBase = msg->event(); // Determine if this incidence is in my calendar (and owned by me) Incidence *existingIncidence = 0; if ( incBase && helper->calendar() ) { existingIncidence = helper->calendar()->incidence( incBase->uid() ); if ( !incidenceOwnedByMe( helper->calendar(), existingIncidence ) ) { existingIncidence = 0; } if ( !existingIncidence ) { const Incidence::List list = helper->calendar()->incidences(); for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) { if ( (*it)->schedulingID() == incBase->uid() && incidenceOwnedByMe( helper->calendar(), *it ) ) { existingIncidence = *it; break; } } } } // First make the text of the message TQString html; TQString tableStyle = TQString::fromLatin1( "style=\"border: solid 1px; margin: 0em;\"" ); TQString tableHead = TQString::fromLatin1( "
    " "" "
    ").arg(tableStyle); html += tableHead; InvitationHeaderVisitor headerVisitor; // The InvitationHeaderVisitor returns false if the incidence is somehow invalid, or not handled if ( !headerVisitor.act( incBase, existingIncidence, msg, sender ) ) return TQString(); html += "" + headerVisitor.result() + ""; InvitationBodyVisitor bodyVisitor( noHtmlMode ); if ( !bodyVisitor.act( incBase, existingIncidence, msg, sender ) ) return TQString(); html += bodyVisitor.result(); if ( msg->method() == Scheduler::Request ) { IncidenceCompareVisitor compareVisitor; if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) { html += "

    "; html += i18n( "The following changes have been made by the organizer:" ); html += "

    "; html += compareVisitor.result(); } } if ( msg->method() == Scheduler::Reply ) { IncidenceCompareVisitor compareVisitor; if ( compareVisitor.act( incBase, existingIncidence, msg->method() ) ) { html += "

    "; if ( !sender.isEmpty() ) { html += i18n( "The following changes have been made by %1:" ).arg( sender ); } else { html += i18n( "The following changes have been made by an attendee:" ); } html += "

    "; html += compareVisitor.result(); } } Incidence *inc = dynamic_cast( incBase ); // determine if I am the organizer for this invitation bool myInc = iamOrganizer( inc ); // determine if the invitation response has already been recorded bool rsvpRec = false; Attendee *ea = 0; if ( !myInc ) { Incidence *rsvpIncidence = existingIncidence; if ( !rsvpIncidence && inc && inc->revision() > 0 ) { rsvpIncidence = inc; } if ( rsvpIncidence ) { ea = findMyAttendee( rsvpIncidence ); } if ( ea && ( ea->status() == Attendee::Accepted || ea->status() == Attendee::Declined || ea->status() == Attendee::Tentative ) ) { rsvpRec = true; } } // determine invitation role TQString role; bool isDelegated = false; Attendee *a = findMyAttendee( inc ); if ( !a && inc ) { if ( !inc->attendees().isEmpty() ) { a = inc->attendees().first(); } } if ( a ) { isDelegated = ( a->status() == Attendee::Delegated ); role = Attendee::roleName( a->role() ); } // determine if RSVP needed, not-needed, or response already recorded bool rsvpReq = rsvpRequested( inc ); if ( !myInc && a ) { html += "
    "; html += ""; if ( rsvpRec && inc ) { if ( inc->revision() == 0 ) { html += i18n( "Your %1 response has already been recorded" ). arg( ea->statusStr() ); } else { html += i18n( "Your status for this invitation is %1" ). arg( ea->statusStr() ); } rsvpReq = false; } else if ( msg->method() == Scheduler::Cancel ) { html += i18n( "This invitation was declined" ); } else if ( msg->method() == Scheduler::Add ) { html += i18n( "This invitation was accepted" ); } else { if ( !isDelegated ) { html += rsvpRequestedStr( rsvpReq, role ); } else { html += i18n( "Awaiting delegation response" ); } } html += ""; } // Print if the organizer gave you a preset status if ( !myInc ) { if ( inc && inc->revision() == 0 ) { TQString statStr = myStatusStr( inc ); if ( !statStr.isEmpty() ) { html += "
    "; html += ""; html += statStr; html += ""; } } } // Add groupware links html += "
    "; switch ( msg->method() ) { case Scheduler::Publish: case Scheduler::Request: case Scheduler::Refresh: case Scheduler::Add: { if ( inc && inc->revision() > 0 && ( existingIncidence || !helper->calendar() ) ) { html += ""; if ( inc->type() == "Todo" ) { html += ""; } if ( !myInc && a ) { html += "" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + ""; } break; } case Scheduler::Cancel: // Remove invitation if ( inc ) { html += ""; if ( inc->type() == "Todo" ) { html += ""; } break; case Scheduler::Reply: { // Record invitation response Attendee *a = 0; Attendee *ea = 0; if ( inc ) { // First, determine if this reply is really a counter in disguise. if ( replyMeansCounter( inc ) ) { html += "" + counterButtons( inc, helper ) + ""; break; } // Next, maybe this is a declined reply that was delegated from me? // find first attendee who is delegated-from me // look a their PARTSTAT response, if the response is declined, // then we need to start over which means putting all the action // buttons and NOT putting on the [Record response..] button a = findDelegatedFromMyAttendee( inc ); if ( a ) { if ( a->status() != Attendee::Accepted || a->status() != Attendee::Tentative ) { html += "" + responseButtons( inc, rsvpReq, rsvpRec, helper ) + ""; break; } } // Finally, simply allow a Record of the reply if ( !inc->attendees().isEmpty() ) { a = inc->attendees().first(); } if ( a ) { ea = findAttendee( existingIncidence, a->email() ); } } if ( ea && ( ea->status() != Attendee::NeedsAction ) && ( ea->status() == a->status() ) ) { if ( inc && inc->revision() > 0 ) { html += "
    "; html += i18n( "The response has been recorded [%1]" ).arg( ea->statusStr() ); html += ""; } } else { if ( inc ) { html += ""; } } break; } case Scheduler::Counter: // Counter proposal html += "" + counterButtons( inc, helper ) + ""; break; case Scheduler::Declinecounter: case Scheduler::NoMethod: break; } // close the groupware table html += "
     
    "; html += helper->makeLink( "reply", i18n( "[Record invitation in my task list]" ) ); } else { html += ""; html += helper->makeLink( "reply", i18n( "[Record invitation in my calendar]" ) ); } html += "
    "; html += helper->makeLink( "cancel", i18n( "[Remove invitation from my task list]" ) ); } else { html += ""; html += helper->makeLink( "cancel", i18n( "[Remove invitation from my calendar]" ) ); } html += "
    "; if ( inc->type() == "Todo" ) { html += helper->makeLink( "reply", i18n( "[Record response in my task list]" ) ); } else { html += helper->makeLink( "reply", i18n( "[Record response in my calendar]" ) ); } html += "
    "; // Add the attendee list if I am the organizer if ( myInc && helper->calendar() ) { html += invitationAttendees( helper->calendar()->incidence( inc->uid() ) ); } // close the top-level table html += "

    "; // Add the attachment list html += invitationAttachments( helper, inc ); return html; } TQString IncidenceFormatter::formatICalInvitation( TQString invitation, Calendar *mCalendar, InvitationFormatterHelper *helper ) { return formatICalInvitationHelper( invitation, mCalendar, helper, false, TQString() ); } TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation, Calendar *mCalendar, InvitationFormatterHelper *helper ) { return formatICalInvitationHelper( invitation, mCalendar, helper, true, TQString() ); } TQString IncidenceFormatter::formatICalInvitationNoHtml( TQString invitation, Calendar *mCalendar, InvitationFormatterHelper *helper, const TQString &sender ) { return formatICalInvitationHelper( invitation, mCalendar, helper, true, sender ); } /******************************************************************* * Helper functions for the msTNEF -> VPart converter *******************************************************************/ //----------------------------------------------------------------------------- static TQString stringProp( KTNEFMessage* tnefMsg, const TQ_UINT32& key, const TQString& fallback = TQString()) { return tnefMsg->findProp( key < 0x10000 ? key & 0xFFFF : key >> 16, fallback ); } static TQString sNamedProp( KTNEFMessage* tnefMsg, const TQString& name, const TQString& fallback = TQString() ) { return tnefMsg->findNamedProp( name, fallback ); } struct save_tz { char* old_tz; char* tz_env_str; }; /* temporarily go to a different timezone */ static struct save_tz set_tz( const char* _tc ) { const char *tc = _tc?_tc:"UTC"; struct save_tz rv; rv.old_tz = 0; rv.tz_env_str = 0; //kdDebug(5006) << "set_tz(), timezone before = " << timezone << endl; char* tz_env = 0; if( getenv( "TZ" ) ) { tz_env = strdup( getenv( "TZ" ) ); rv.old_tz = tz_env; } char* tmp_env = (char*)malloc( strlen( tc ) + 4 ); strcpy( tmp_env, "TZ=" ); strcpy( tmp_env+3, tc ); putenv( tmp_env ); rv.tz_env_str = tmp_env; /* tmp_env is not free'ed -- it is part of the environment */ tzset(); //kdDebug(5006) << "set_tz(), timezone after = " << timezone << endl; return rv; } /* restore previous timezone */ static void unset_tz( struct save_tz old_tz ) { if( old_tz.old_tz ) { char* tmp_env = (char*)malloc( strlen( old_tz.old_tz ) + 4 ); strcpy( tmp_env, "TZ=" ); strcpy( tmp_env+3, old_tz.old_tz ); putenv( tmp_env ); /* tmp_env is not free'ed -- it is part of the environment */ free( old_tz.old_tz ); } else { /* clear TZ from env */ putenv( strdup("TZ") ); } tzset(); /* is this OK? */ if( old_tz.tz_env_str ) free( old_tz.tz_env_str ); } static TQDateTime utc2Local( const TQDateTime& utcdt ) { struct tm tmL; save_tz tmp_tz = set_tz("UTC"); time_t utc = utcdt.toTime_t(); unset_tz( tmp_tz ); localtime_r( &utc, &tmL ); return TQDateTime( TQDate( tmL.tm_year+1900, tmL.tm_mon+1, tmL.tm_mday ), TQTime( tmL.tm_hour, tmL.tm_min, tmL.tm_sec ) ); } static TQDateTime pureISOToLocalTQDateTime( const TQString& dtStr, bool bDateOnly = false ) { TQDate tmpDate; TQTime tmpTime; int year, month, day, hour, minute, second; if( bDateOnly ) { year = dtStr.left( 4 ).toInt(); month = dtStr.mid( 4, 2 ).toInt(); day = dtStr.mid( 6, 2 ).toInt(); hour = 0; minute = 0; second = 0; } else { year = dtStr.left( 4 ).toInt(); month = dtStr.mid( 4, 2 ).toInt(); day = dtStr.mid( 6, 2 ).toInt(); hour = dtStr.mid( 9, 2 ).toInt(); minute = dtStr.mid( 11, 2 ).toInt(); second = dtStr.mid( 13, 2 ).toInt(); } tmpDate.setYMD( year, month, day ); tmpTime.setHMS( hour, minute, second ); if( tmpDate.isValid() && tmpTime.isValid() ) { TQDateTime dT = TQDateTime( tmpDate, tmpTime ); if( !bDateOnly ) { // correct for GMT ( == Zulu time == UTC ) if (dtStr.at(dtStr.length()-1) == 'Z') { //dT = dT.addSecs( 60 * KRFCDate::localUTCOffset() ); //localUTCOffset( dT ) ); dT = utc2Local( dT ); } } return dT; } else return TQDateTime(); } TQString IncidenceFormatter::msTNEFToVPart( const TQByteArray& tnef ) { bool bOk = false; KTNEFParser parser; TQBuffer buf( tnef ); CalendarLocal cal ( TQString::fromLatin1( "UTC" ) ); TDEABC::Addressee addressee; TDEABC::VCardConverter cardConv; ICalFormat calFormat; Event* event = new Event(); if( parser.openDevice( &buf ) ) { KTNEFMessage* tnefMsg = parser.message(); //TQMap props = parser.message()->properties(); // Everything depends from property PR_MESSAGE_CLASS // (this is added by KTNEFParser): TQString msgClass = tnefMsg->findProp( 0x001A, TQString(), true ) .upper(); if( !msgClass.isEmpty() ) { // Match the old class names that might be used by Outlook for // compatibility with Microsoft Mail for Windows for Workgroups 3.1. bool bCompatClassAppointment = false; bool bCompatMethodRequest = false; bool bCompatMethodCancled = false; bool bCompatMethodAccepted = false; bool bCompatMethodAcceptedCond = false; bool bCompatMethodDeclined = false; if( msgClass.startsWith( "IPM.MICROSOFT SCHEDULE." ) ) { bCompatClassAppointment = true; if( msgClass.endsWith( ".MTGREQ" ) ) bCompatMethodRequest = true; if( msgClass.endsWith( ".MTGCNCL" ) ) bCompatMethodCancled = true; if( msgClass.endsWith( ".MTGRESPP" ) ) bCompatMethodAccepted = true; if( msgClass.endsWith( ".MTGRESPA" ) ) bCompatMethodAcceptedCond = true; if( msgClass.endsWith( ".MTGRESPN" ) ) bCompatMethodDeclined = true; } bool bCompatClassNote = ( msgClass == "IPM.MICROSOFT MAIL.NOTE" ); if( bCompatClassAppointment || "IPM.APPOINTMENT" == msgClass ) { // Compose a vCal bool bIsReply = false; TQString prodID = "-//Microsoft Corporation//Outlook "; prodID += tnefMsg->findNamedProp( "0x8554", "9.0" ); prodID += "MIMEDIR/EN\n"; prodID += "VERSION:2.0\n"; calFormat.setApplication( "Outlook", prodID ); Scheduler::Method method; if( bCompatMethodRequest ) method = Scheduler::Request; else if( bCompatMethodCancled ) method = Scheduler::Cancel; else if( bCompatMethodAccepted || bCompatMethodAcceptedCond || bCompatMethodDeclined ) { method = Scheduler::Reply; bIsReply = true; } else { // pending(khz): verify whether "0x0c17" is the right tag ??? // // at the moment we think there are REQUESTS and UPDATES // // but WHAT ABOUT REPLIES ??? // // if( tnefMsg->findProp(0x0c17) == "1" ) bIsReply = true; method = Scheduler::Request; } /// ### FIXME Need to get this attribute written ScheduleMessage schedMsg(event, method, ScheduleMessage::Unknown ); TQString sSenderSearchKeyEmail( tnefMsg->findProp( 0x0C1D ) ); if( !sSenderSearchKeyEmail.isEmpty() ) { int colon = sSenderSearchKeyEmail.find( ':' ); // May be e.g. "SMTP:KHZ@KDE.ORG" if( sSenderSearchKeyEmail.find( ':' ) == -1 ) sSenderSearchKeyEmail.remove( 0, colon+1 ); } TQString s( tnefMsg->findProp( 0x0e04 ) ); TQStringList attendees = TQStringList::split( ';', s ); if( attendees.count() ) { for( TQStringList::Iterator it = attendees.begin(); it != attendees.end(); ++it ) { // Skip all entries that have no '@' since these are // no mail addresses if( (*it).find('@') == -1 ) { s = (*it).stripWhiteSpace(); Attendee *attendee = new Attendee( s, s, true ); if( bIsReply ) { if( bCompatMethodAccepted ) attendee->setStatus( Attendee::Accepted ); if( bCompatMethodDeclined ) attendee->setStatus( Attendee::Declined ); if( bCompatMethodAcceptedCond ) attendee->setStatus(Attendee::Tentative); } else { attendee->setStatus( Attendee::NeedsAction ); attendee->setRole( Attendee::ReqParticipant ); } event->addAttendee(attendee); } } } else { // Oops, no attendees? // This must be old style, let us use the PR_SENDER_SEARCH_KEY. s = sSenderSearchKeyEmail; if( !s.isEmpty() ) { Attendee *attendee = new Attendee( TQString(), TQString(), true ); if( bIsReply ) { if( bCompatMethodAccepted ) attendee->setStatus( Attendee::Accepted ); if( bCompatMethodAcceptedCond ) attendee->setStatus( Attendee::Declined ); if( bCompatMethodDeclined ) attendee->setStatus( Attendee::Tentative ); } else { attendee->setStatus(Attendee::NeedsAction); attendee->setRole(Attendee::ReqParticipant); } event->addAttendee(attendee); } } s = tnefMsg->findProp( 0x0c1f ); // look for organizer property if( s.isEmpty() && !bIsReply ) s = sSenderSearchKeyEmail; // TODO: Use the common name? if( !s.isEmpty() ) event->setOrganizer( s ); s = tnefMsg->findProp( 0x8516 ).replace( TQChar( '-' ), TQString() ) .replace( TQChar( ':' ), TQString() ); event->setDtStart( TQDateTime::fromString( s ) ); // ## Format?? s = tnefMsg->findProp( 0x8517 ).replace( TQChar( '-' ), TQString() ) .replace( TQChar( ':' ), TQString() ); event->setDtEnd( TQDateTime::fromString( s ) ); s = tnefMsg->findProp( 0x8208 ); event->setLocation( s ); // is it OK to set this to OPAQUE always ?? //vPart += "TRANSP:OPAQUE\n"; ###FIXME, portme! //vPart += "SEQUENCE:0\n"; // is "0x0023" OK - or should we look for "0x0003" ?? s = tnefMsg->findProp( 0x0023 ); event->setUid( s ); // PENDING(khz): is this value in local timezone? Must it be // adjusted? Most likely this is a bug in the server or in // Outlook - we ignore it for now. s = tnefMsg->findProp( 0x8202 ).replace( TQChar( '-' ), TQString() ) .replace( TQChar( ':' ), TQString() ); // ### libkcal always uses currentDateTime() // event->setDtStamp(TQDateTime::fromString(s)); s = tnefMsg->findNamedProp( "Keywords" ); event->setCategories( s ); s = tnefMsg->findProp( 0x1000 ); event->setDescription( s ); s = tnefMsg->findProp( 0x0070 ); event->setSummary( s ); s = tnefMsg->findProp( 0x0026 ); event->setPriority( s.toInt() ); // is reminder flag set ? if(!tnefMsg->findProp(0x8503).isEmpty()) { Alarm *alarm = new Alarm(event); TQDateTime highNoonTime = pureISOToLocalTQDateTime( tnefMsg->findProp( 0x8502 ) .replace( TQChar( '-' ), "" ) .replace( TQChar( ':' ), "" ) ); TQDateTime wakeMeUpTime = pureISOToLocalTQDateTime( tnefMsg->findProp( 0x8560, "" ) .replace( TQChar( '-' ), "" ) .replace( TQChar( ':' ), "" ) ); alarm->setTime(wakeMeUpTime); if( highNoonTime.isValid() && wakeMeUpTime.isValid() ) alarm->setStartOffset( Duration( highNoonTime, wakeMeUpTime ) ); else // default: wake them up 15 minutes before the appointment alarm->setStartOffset( Duration( 15*60 ) ); alarm->setDisplayAlarm( i18n( "Reminder" ) ); // Sorry: the different action types are not known (yet) // so we always set 'DISPLAY' (no sounds, no images...) event->addAlarm( alarm ); } cal.addEvent( event ); bOk = true; // we finished composing a vCal } else if( bCompatClassNote || "IPM.CONTACT" == msgClass ) { addressee.setUid( stringProp( tnefMsg, attMSGID ) ); addressee.setFormattedName( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME ) ); addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL1EMAILADDRESS ), true ); addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL2EMAILADDRESS ), false ); addressee.insertEmail( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_EMAIL3EMAILADDRESS ), false ); addressee.insertCustom( "KADDRESSBOOK", "X-IMAddress", sNamedProp( tnefMsg, MAPI_TAG_CONTACT_IMADDRESS ) ); addressee.insertCustom( "KADDRESSBOOK", "X-SpousesName", stringProp( tnefMsg, MAPI_TAG_PR_SPOUSE_NAME ) ); addressee.insertCustom( "KADDRESSBOOK", "X-ManagersName", stringProp( tnefMsg, MAPI_TAG_PR_MANAGER_NAME ) ); addressee.insertCustom( "KADDRESSBOOK", "X-AssistantsName", stringProp( tnefMsg, MAPI_TAG_PR_ASSISTANT ) ); addressee.insertCustom( "KADDRESSBOOK", "X-Department", stringProp( tnefMsg, MAPI_TAG_PR_DEPARTMENT_NAME ) ); addressee.insertCustom( "KADDRESSBOOK", "X-Office", stringProp( tnefMsg, MAPI_TAG_PR_OFFICE_LOCATION ) ); addressee.insertCustom( "KADDRESSBOOK", "X-Profession", stringProp( tnefMsg, MAPI_TAG_PR_PROFESSION ) ); TQString s = tnefMsg->findProp( MAPI_TAG_PR_WEDDING_ANNIVERSARY ) .replace( TQChar( '-' ), TQString() ) .replace( TQChar( ':' ), TQString() ); if( !s.isEmpty() ) addressee.insertCustom( "KADDRESSBOOK", "X-Anniversary", s ); addressee.setUrl( KURL( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_WEBPAGE ) ) ); // collect parts of Name entry addressee.setFamilyName( stringProp( tnefMsg, MAPI_TAG_PR_SURNAME ) ); addressee.setGivenName( stringProp( tnefMsg, MAPI_TAG_PR_GIVEN_NAME ) ); addressee.setAdditionalName( stringProp( tnefMsg, MAPI_TAG_PR_MIDDLE_NAME ) ); addressee.setPrefix( stringProp( tnefMsg, MAPI_TAG_PR_DISPLAY_NAME_PREFIX ) ); addressee.setSuffix( stringProp( tnefMsg, MAPI_TAG_PR_GENERATION ) ); addressee.setNickName( stringProp( tnefMsg, MAPI_TAG_PR_NICKNAME ) ); addressee.setRole( stringProp( tnefMsg, MAPI_TAG_PR_TITLE ) ); addressee.setOrganization( stringProp( tnefMsg, MAPI_TAG_PR_COMPANY_NAME ) ); /* the MAPI property ID of this (multiline) )field is unknown: vPart += stringProp(tnefMsg, "\n","NOTE", ... , "" ); */ TDEABC::Address adr; adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_PO_BOX ) ); adr.setStreet( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STREET ) ); adr.setLocality( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_CITY ) ); adr.setRegion( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_STATE_OR_PROVINCE ) ); adr.setPostalCode( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_POSTAL_CODE ) ); adr.setCountry( stringProp( tnefMsg, MAPI_TAG_PR_HOME_ADDRESS_COUNTRY ) ); adr.setType(TDEABC::Address::Home); addressee.insertAddress(adr); adr.setPostOfficeBox( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOBOX ) ); adr.setStreet( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTREET ) ); adr.setLocality( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCITY ) ); adr.setRegion( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSSTATE ) ); adr.setPostalCode( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSPOSTALCODE ) ); adr.setCountry( sNamedProp( tnefMsg, MAPI_TAG_CONTACT_BUSINESSADDRESSCOUNTRY ) ); adr.setType( TDEABC::Address::Work ); addressee.insertAddress( adr ); adr.setPostOfficeBox( stringProp( tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_PO_BOX ) ); adr.setStreet( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STREET ) ); adr.setLocality( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_CITY ) ); adr.setRegion( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_STATE_OR_PROVINCE ) ); adr.setPostalCode( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_POSTAL_CODE ) ); adr.setCountry( stringProp(tnefMsg, MAPI_TAG_PR_OTHER_ADDRESS_COUNTRY ) ); adr.setType( TDEABC::Address::Dom ); addressee.insertAddress(adr); // problem: the 'other' address was stored by KOrganizer in // a line looking like the following one: // 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 TQString nr; nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_TELEPHONE_NUMBER ); addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Home ) ); nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_TELEPHONE_NUMBER ); addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Work ) ); nr = stringProp( tnefMsg, MAPI_TAG_PR_MOBILE_TELEPHONE_NUMBER ); addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Cell ) ); nr = stringProp( tnefMsg, MAPI_TAG_PR_HOME_FAX_NUMBER ); addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Fax | TDEABC::PhoneNumber::Home ) ); nr = stringProp( tnefMsg, MAPI_TAG_PR_BUSINESS_FAX_NUMBER ); addressee.insertPhoneNumber( TDEABC::PhoneNumber( nr, TDEABC::PhoneNumber::Fax | TDEABC::PhoneNumber::Work ) ); s = tnefMsg->findProp( MAPI_TAG_PR_BIRTHDAY ) .replace( TQChar( '-' ), TQString() ) .replace( TQChar( ':' ), TQString() ); if( !s.isEmpty() ) addressee.setBirthday( TQDateTime::fromString( s ) ); bOk = ( !addressee.isEmpty() ); } else if( "IPM.NOTE" == msgClass ) { } // else if ... and so on ... } } // Compose return string TQString iCal = calFormat.toString( &cal ); if( !iCal.isEmpty() ) // This was an iCal return iCal; // Not an iCal - try a vCard TDEABC::VCardConverter converter; return converter.createVCard( addressee ); } TQString IncidenceFormatter::formatTNEFInvitation( const TQByteArray& tnef, Calendar *mCalendar, InvitationFormatterHelper *helper ) { TQString vPart = IncidenceFormatter::msTNEFToVPart( tnef ); TQString iCal = IncidenceFormatter::formatICalInvitation( vPart, mCalendar, helper ); if( !iCal.isEmpty() ) return iCal; return vPart; } /******************************************************************* * Helper functions for the Incidence tooltips *******************************************************************/ class IncidenceFormatter::ToolTipVisitor : public IncidenceBase::Visitor { public: ToolTipVisitor() : mCalendar( 0 ), mRichText( true ), mResult( "" ) {} bool act( Calendar *calendar, IncidenceBase *incidence, const TQDate &date=TQDate(), bool richText=true ) { mCalendar = calendar; mDate = date; mRichText = richText; mResult = ""; return incidence ? incidence->accept( *this ) : false; } TQString result() const { return mResult; } protected: bool visit( Event *event ); bool visit( Todo *todo ); bool visit( Journal *journal ); bool visit( FreeBusy *fb ); TQString dateRangeText( Event *event, const TQDate &date ); TQString dateRangeText( Todo *todo, const TQDate &date ); TQString dateRangeText( Journal *journal ); TQString dateRangeText( FreeBusy *fb ); TQString generateToolTip( Incidence* incidence, TQString dtRangeText ); protected: Calendar *mCalendar; TQDate mDate; bool mRichText; TQString mResult; }; TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Event *event, const TQDate &date ) { TQString ret; TQString tmp; TQDateTime startDt = event->dtStart(); TQDateTime endDt = event->dtEnd(); if ( event->doesRecur() ) { if ( date.isValid() ) { TQDateTime dt( date, TQTime( 0, 0, 0 ) ); int diffDays = startDt.daysTo( dt ); dt = dt.addSecs( -1 ); startDt.setDate( event->recurrence()->getNextDateTime( dt ).date() ); if ( event->hasEndDate() ) { endDt = endDt.addDays( diffDays ); if ( startDt > endDt ) { startDt.setDate( event->recurrence()->getPreviousDateTime( dt ).date() ); endDt = startDt.addDays( event->dtStart().daysTo( event->dtEnd() ) ); } } } } if ( event->isMultiDay() ) { tmp = "
    " + i18n("Event start", "From: %1"); if (event->doesFloat()) ret += tmp.arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") ); else ret += tmp.arg( IncidenceFormatter::dateToString( startDt ).replace(" ", " ") ); tmp = "
    " + i18n("Event end","To: %1"); if (event->doesFloat()) ret += tmp.arg( IncidenceFormatter::dateToString( endDt, false ).replace(" ", " ") ); else ret += tmp.arg( IncidenceFormatter::dateToString( endDt ).replace(" ", " ") ); } else { ret += "
    "+i18n("Date: %1"). arg( IncidenceFormatter::dateToString( startDt, false ).replace(" ", " ") ); if ( !event->doesFloat() ) { const TQString dtStartTime = IncidenceFormatter::timeToString( startDt, true ).replace( " ", " " ); const TQString dtEndTime = IncidenceFormatter::timeToString( endDt, true ).replace( " ", " " ); if ( dtStartTime == dtEndTime ) { // to prevent 'Time: 17:00 - 17:00' tmp = "
    " + i18n("time for event,   to prevent ugly line breaks", "Time: %1"). arg( dtStartTime ); } else { tmp = "
    " + i18n("time range for event,   to prevent ugly line breaks", "Time: %1 - %2"). arg( dtStartTime, dtEndTime ); } ret += tmp; } } return ret; } TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Todo *todo, const TQDate &date ) { TQString ret; bool floats( todo->doesFloat() ); if ( todo->hasStartDate() && todo->dtStart().isValid() ) { TQDateTime startDt = todo->dtStart(); if ( todo->doesRecur() ) { if ( date.isValid() ) { startDt.setDate( date ); } } ret += "
    " + i18n("Start: %1"). arg( IncidenceFormatter::dateTimeToString( startDt, floats, false ). replace( " ", " " ) ); } if ( todo->hasDueDate() && todo->dtDue().isValid() ) { TQDateTime dueDt = todo->dtDue(); if ( todo->doesRecur() ) { if ( date.isValid() ) { TQDateTime dt( date, TQTime( 0, 0, 0 ) ); dt = dt.addSecs( -1 ); dueDt.setDate( todo->recurrence()->getNextDateTime( dt ).date() ); } } ret += "
    " + i18n("Due: %1"). arg( IncidenceFormatter::dateTimeToString( dueDt, floats, false ). replace( " ", " " ) ); } // Print priority and completed info here, for lack of a better place if ( todo->priority() > 0 ) { ret += "
    "; ret += "" + i18n( "Priority:" ) + "" + " "; ret += TQString::number( todo->priority() ); } ret += "
    "; if ( todo->isCompleted() ) { ret += "" + i18n( "Completed:" ) + "" + " "; ret += todo->completedStr().replace( " ", " " ); } else { ret += "" + i18n( "Percent Done:" ) + "" + " "; ret += i18n( "%1%" ).arg( todo->percentComplete() ); } return ret; } TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( Journal*journal ) { TQString ret; if (journal->dtStart().isValid() ) { ret += "
    " + i18n("Date: %1"). arg( IncidenceFormatter::dateToString( journal->dtStart(), false ) ); } return ret; } TQString IncidenceFormatter::ToolTipVisitor::dateRangeText( FreeBusy *fb ) { TQString tmp( "
    " + i18n("Period start: %1") ); TQString ret = tmp.arg( TDEGlobal::locale()->formatDateTime( fb->dtStart() ) ); tmp = "
    " + i18n("Period start: %1"); ret += tmp.arg( TDEGlobal::locale()->formatDateTime( fb->dtEnd() ) ); return ret; } bool IncidenceFormatter::ToolTipVisitor::visit( Event *event ) { mResult = generateToolTip( event, dateRangeText( event, mDate ) ); return !mResult.isEmpty(); } bool IncidenceFormatter::ToolTipVisitor::visit( Todo *todo ) { mResult = generateToolTip( todo, dateRangeText( todo, mDate ) ); return !mResult.isEmpty(); } bool IncidenceFormatter::ToolTipVisitor::visit( Journal *journal ) { mResult = generateToolTip( journal, dateRangeText( journal ) ); return !mResult.isEmpty(); } bool IncidenceFormatter::ToolTipVisitor::visit( FreeBusy *fb ) { mResult = "" + i18n("Free/Busy information for %1") .arg(fb->organizer().fullName()) + ""; mResult += dateRangeText( fb ); mResult += ""; return !mResult.isEmpty(); } static TQString tooltipPerson( const TQString& email, TQString name ) { // Make the search, if there is an email address to search on, // and name is missing if ( name.isEmpty() && !email.isEmpty() ) { TDEABC::AddressBook *add_book = TDEABC::StdAddressBook::self( true ); TDEABC::Addressee::List addressList = add_book->findByEmail( email ); if ( !addressList.isEmpty() ) { TDEABC::Addressee o = addressList.first(); if ( !o.isEmpty() && addressList.size() < 2 ) { // use the name from the addressbook name = o.formattedName(); } } } // Show the attendee TQString tmpString = ( name.isEmpty() ? email : name ); return tmpString; } static TQString etc = i18n( "elipsis", "..." ); static TQString tooltipFormatAttendeeRoleList( Incidence *incidence, Attendee::Role role ) { int maxNumAtts = 8; // maximum number of people to print per attendee role TQString sep = i18n( "separator for lists of people names", ", " ); int sepLen = sep.length(); int i = 0; TQString tmpStr; Attendee::List::ConstIterator it; Attendee::List attendees = incidence->attendees(); for( it = attendees.begin(); it != attendees.end(); ++it ) { Attendee *a = *it; if ( a->role() != role ) { // skip not this role continue; } if ( a->email() == incidence->organizer().email() ) { // skip attendee that is also the organizer continue; } if ( i == maxNumAtts ) { tmpStr += etc; break; } tmpStr += tooltipPerson( a->email(), a->name() ); if ( !a->delegator().isEmpty() ) { tmpStr += i18n(" (delegated by %1)" ).arg( a->delegator() ); } if ( !a->delegate().isEmpty() ) { tmpStr += i18n(" (delegated to %1)" ).arg( a->delegate() ); } tmpStr += sep; i++; } if ( tmpStr.endsWith( sep ) ) { tmpStr.truncate( tmpStr.length() - sepLen ); } return tmpStr; } static TQString tooltipFormatAttendees( Incidence *incidence ) { TQString tmpStr, str; // Add organizer link int attendeeCount = incidence->attendees().count(); if ( attendeeCount > 1 || ( attendeeCount == 1 && incidence->organizer().email() != incidence->attendees().first()->email() ) ) { tmpStr += "" + i18n( "Organizer:" ) + "" + " "; tmpStr += tooltipPerson( incidence->organizer().email(), incidence->organizer().name() ); } // Add "chair" str = tooltipFormatAttendeeRoleList( incidence, Attendee::Chair ); if ( !str.isEmpty() ) { tmpStr += "
    " + i18n( "Chair:" ) + "" + " "; tmpStr += str; } // Add required participants str = tooltipFormatAttendeeRoleList( incidence, Attendee::ReqParticipant ); if ( !str.isEmpty() ) { tmpStr += "
    " + i18n( "Required Participants:" ) + "" + " "; tmpStr += str; } // Add optional participants str = tooltipFormatAttendeeRoleList( incidence, Attendee::OptParticipant ); if ( !str.isEmpty() ) { tmpStr += "
    " + i18n( "Optional Participants:" ) + "" + " "; tmpStr += str; } // Add observers str = tooltipFormatAttendeeRoleList( incidence, Attendee::NonParticipant ); if ( !str.isEmpty() ) { tmpStr += "
    " + i18n( "Observers:" ) + "" + " "; tmpStr += str; } return tmpStr; } TQString IncidenceFormatter::ToolTipVisitor::generateToolTip( Incidence* incidence, TQString dtRangeText ) { uint maxDescLen = 120; // maximum description chars to print (before elipsis) if ( !incidence ) { return TQString(); } TQString tmp = ""; // header tmp += "" + incidence->summary().replace( "\n", "
    " ) + "
    "; //NOTE: using
    seems to confuse TQt3 tooltips in some cases so use "-----" tmp += "
    ----------
    "; if ( mCalendar ) { TQString calStr = IncidenceFormatter::resourceString( mCalendar, incidence ); if ( !calStr.isEmpty() ) { tmp += "" + i18n( "Calendar:" ) + "" + " "; tmp += calStr; } } tmp += dtRangeText; if ( !incidence->location().isEmpty() ) { tmp += "
    "; tmp += "" + i18n( "Location:" ) + "" + " "; tmp += incidence->location().replace( "\n", "
    " ); } TQString durStr = IncidenceFormatter::durationString( incidence ); if ( !durStr.isEmpty() ) { tmp += "
    "; tmp += "" + i18n( "Duration:" ) + "" + " "; tmp += durStr; } if ( incidence->doesRecur() ) { tmp += "
    "; tmp += "" + i18n( "Recurrence:" ) + "" + " "; tmp += IncidenceFormatter::recurrenceString( incidence ); } if ( !incidence->description().isEmpty() ) { TQString desc( incidence->description() ); if ( desc.length() > maxDescLen ) { desc = desc.left( maxDescLen ) + etc; } tmp += "
    ----------
    "; tmp += "" + i18n( "Description:" ) + "" + "
    "; tmp += desc.replace( "\n", "
    " ); tmp += "
    ----------"; } int reminderCount = incidence->alarms().count(); if ( reminderCount > 0 && incidence->isAlarmEnabled() ) { tmp += "
    "; tmp += "" + i18n( "Reminder:", "%n Reminders:", reminderCount ) + "" + " "; tmp += IncidenceFormatter::reminderStringList( incidence ).join( ", " ); } tmp += "
    "; tmp += tooltipFormatAttendees( incidence ); int categoryCount = incidence->categories().count(); if ( categoryCount > 0 ) { tmp += "
    "; tmp += "" + i18n( "Category:", "%n Categories:", categoryCount ) + "" + " "; tmp += incidence->categories().join( ", " ); } tmp += "
    "; return tmp; } TQString IncidenceFormatter::toolTipString( IncidenceBase *incidence, bool richText ) { return toolTipStr( 0, incidence, TQDate(), richText ); } TQString IncidenceFormatter::toolTipStr( Calendar *calendar, IncidenceBase *incidence, const TQDate &date, bool richText ) { ToolTipVisitor v; if ( v.act( calendar, incidence, date, richText ) ) { return v.result(); } else { return TQString(); } } /******************************************************************* * Helper functions for the Incidence tooltips *******************************************************************/ class IncidenceFormatter::MailBodyVisitor : public IncidenceBase::Visitor { public: MailBodyVisitor() : mResult( "" ) {} bool act( IncidenceBase *incidence ) { mResult = ""; return incidence ? incidence->accept( *this ) : false; } TQString result() const { return mResult; } protected: bool visit( Event *event ); bool visit( Todo *todo ); bool visit( Journal *journal ); bool visit( FreeBusy * ) { mResult = i18n("This is a Free Busy Object"); return !mResult.isEmpty(); } protected: TQString mResult; }; static TQString mailBodyIncidence( Incidence *incidence ) { TQString body; if ( !incidence->summary().isEmpty() ) { body += i18n("Summary: %1\n").arg( incidence->summary() ); } if ( !incidence->organizer().isEmpty() ) { body += i18n("Organizer: %1\n").arg( incidence->organizer().fullName() ); } if ( !incidence->location().isEmpty() ) { body += i18n("Location: %1\n").arg( incidence->location() ); } return body; } bool IncidenceFormatter::MailBodyVisitor::visit( Event *event ) { TQString recurrence[]= {i18n("no recurrence", "None"), i18n("Minutely"), i18n("Hourly"), i18n("Daily"), i18n("Weekly"), i18n("Monthly Same Day"), i18n("Monthly Same Position"), i18n("Yearly"), i18n("Yearly"), i18n("Yearly")}; mResult = mailBodyIncidence( event ); mResult += i18n("Start Date: %1\n"). arg( IncidenceFormatter::dateToString( event->dtStart(), true ) ); if ( !event->doesFloat() ) { mResult += i18n("Start Time: %1\n"). arg( IncidenceFormatter::timeToString( event->dtStart(), true ) ); } if ( event->dtStart() != event->dtEnd() ) { mResult += i18n("End Date: %1\n"). arg( IncidenceFormatter::dateToString( event->dtEnd(), true ) ); } if ( !event->doesFloat() ) { mResult += i18n("End Time: %1\n"). arg( IncidenceFormatter::timeToString( event->dtEnd(), true ) ); } if ( event->doesRecur() ) { Recurrence *recur = event->recurrence(); // TODO: Merge these two to one of the form "Recurs every 3 days" mResult += i18n("Recurs: %1\n") .arg( recurrence[ recur->recurrenceType() ] ); mResult += i18n("Frequency: %1\n") .arg( event->recurrence()->frequency() ); if ( recur->duration() > 0 ) { mResult += i18n ("Repeats once", "Repeats %n times", recur->duration()); mResult += '\n'; } else { if ( recur->duration() != -1 ) { // TODO_Recurrence: What to do with floating TQString endstr; if ( event->doesFloat() ) { endstr = TDEGlobal::locale()->formatDate( recur->endDate() ); } else { endstr = TDEGlobal::locale()->formatDateTime( recur->endDateTime() ); } mResult += i18n("Repeat until: %1\n").arg( endstr ); } else { mResult += i18n("Repeats forever\n"); } } DateList exceptions = recur->exDates(); if (exceptions.isEmpty() == false) { mResult += i18n("This recurring meeting has been cancelled on the following days:\n"); DateList::ConstIterator ex_iter; for ( ex_iter = exceptions.begin(); ex_iter != exceptions.end(); ++ex_iter ) { mResult += i18n(" %1\n").arg( TDEGlobal::locale()->formatDate(* ex_iter ) ); } } } TQString details = event->description(); if ( !details.isEmpty() ) { mResult += i18n("Details:\n%1\n").arg( details ); } return !mResult.isEmpty(); } bool IncidenceFormatter::MailBodyVisitor::visit( Todo *todo ) { mResult = mailBodyIncidence( todo ); if ( todo->hasStartDate() ) { mResult += i18n("Start Date: %1\n"). arg( IncidenceFormatter::dateToString( todo->dtStart( false ), true ) ); if ( !todo->doesFloat() ) { mResult += i18n("Start Time: %1\n"). arg( IncidenceFormatter::timeToString( todo->dtStart( false ),true ) ); } } if ( todo->hasDueDate() ) { mResult += i18n("Due Date: %1\n"). arg( IncidenceFormatter::dateToString( todo->dtDue(), true ) ); if ( !todo->doesFloat() ) { mResult += i18n("Due Time: %1\n"). arg( IncidenceFormatter::timeToString( todo->dtDue(), true ) ); } } TQString details = todo->description(); if ( !details.isEmpty() ) { mResult += i18n("Details:\n%1\n").arg( details ); } return !mResult.isEmpty(); } bool IncidenceFormatter::MailBodyVisitor::visit( Journal *journal ) { mResult = mailBodyIncidence( journal ); mResult += i18n("Date: %1\n"). arg( IncidenceFormatter::dateToString( journal->dtStart(), true ) ); if ( !journal->doesFloat() ) { mResult += i18n("Time: %1\n"). arg( IncidenceFormatter::timeToString( journal->dtStart(), true ) ); } if ( !journal->description().isEmpty() ) mResult += i18n("Text of the journal:\n%1\n").arg( journal->description() ); return !mResult.isEmpty(); } TQString IncidenceFormatter::mailBodyString( IncidenceBase *incidence ) { if ( !incidence ) return TQString(); MailBodyVisitor v; if ( v.act( incidence ) ) { return v.result(); } return TQString(); } static TQString recurEnd( Incidence *incidence ) { TQString endstr; if ( incidence->doesFloat() ) { endstr = TDEGlobal::locale()->formatDate( incidence->recurrence()->endDate() ); } else { endstr = TDEGlobal::locale()->formatDateTime( incidence->recurrence()->endDateTime() ); } return endstr; } /************************************ * More static formatting functions ************************************/ TQString IncidenceFormatter::recurrenceString( Incidence *incidence ) { if ( !incidence->doesRecur() ) { return i18n( "No recurrence" ); } TQStringList dayList; dayList.append( i18n( "31st Last" ) ); dayList.append( i18n( "30th Last" ) ); dayList.append( i18n( "29th Last" ) ); dayList.append( i18n( "28th Last" ) ); dayList.append( i18n( "27th Last" ) ); dayList.append( i18n( "26th Last" ) ); dayList.append( i18n( "25th Last" ) ); dayList.append( i18n( "24th Last" ) ); dayList.append( i18n( "23rd Last" ) ); dayList.append( i18n( "22nd Last" ) ); dayList.append( i18n( "21st Last" ) ); dayList.append( i18n( "20th Last" ) ); dayList.append( i18n( "19th Last" ) ); dayList.append( i18n( "18th Last" ) ); dayList.append( i18n( "17th Last" ) ); dayList.append( i18n( "16th Last" ) ); dayList.append( i18n( "15th Last" ) ); dayList.append( i18n( "14th Last" ) ); dayList.append( i18n( "13th Last" ) ); dayList.append( i18n( "12th Last" ) ); dayList.append( i18n( "11th Last" ) ); dayList.append( i18n( "10th Last" ) ); dayList.append( i18n( "9th Last" ) ); dayList.append( i18n( "8th Last" ) ); dayList.append( i18n( "7th Last" ) ); dayList.append( i18n( "6th Last" ) ); dayList.append( i18n( "5th Last" ) ); dayList.append( i18n( "4th Last" ) ); dayList.append( i18n( "3rd Last" ) ); dayList.append( i18n( "2nd Last" ) ); dayList.append( i18n( "last day of the month", "Last" ) ); dayList.append( i18n( "unknown day of the month", "unknown" ) ); //#31 - zero offset from UI dayList.append( i18n( "1st" ) ); dayList.append( i18n( "2nd" ) ); dayList.append( i18n( "3rd" ) ); dayList.append( i18n( "4th" ) ); dayList.append( i18n( "5th" ) ); dayList.append( i18n( "6th" ) ); dayList.append( i18n( "7th" ) ); dayList.append( i18n( "8th" ) ); dayList.append( i18n( "9th" ) ); dayList.append( i18n( "10th" ) ); dayList.append( i18n( "11th" ) ); dayList.append( i18n( "12th" ) ); dayList.append( i18n( "13th" ) ); dayList.append( i18n( "14th" ) ); dayList.append( i18n( "15th" ) ); dayList.append( i18n( "16th" ) ); dayList.append( i18n( "17th" ) ); dayList.append( i18n( "18th" ) ); dayList.append( i18n( "19th" ) ); dayList.append( i18n( "20th" ) ); dayList.append( i18n( "21st" ) ); dayList.append( i18n( "22nd" ) ); dayList.append( i18n( "23rd" ) ); dayList.append( i18n( "24th" ) ); dayList.append( i18n( "25th" ) ); dayList.append( i18n( "26th" ) ); dayList.append( i18n( "27th" ) ); dayList.append( i18n( "28th" ) ); dayList.append( i18n( "29th" ) ); dayList.append( i18n( "30th" ) ); dayList.append( i18n( "31st" ) ); int weekStart = TDEGlobal::locale()->weekStartDay(); TQString dayNames; TQString recurStr, txt; const KCalendarSystem *calSys = TDEGlobal::locale()->calendar(); Recurrence *recur = incidence->recurrence(); switch ( recur->recurrenceType() ) { case Recurrence::rNone: return i18n( "No recurrence" ); case Recurrence::rMinutely: recurStr = i18n( "Recurs every minute", "Recurs every %n minutes", recur->frequency() ); if ( recur->duration() != -1 ) { txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); } return txt; } return recurStr; case Recurrence::rHourly: recurStr = i18n( "Recurs hourly", "Recurs every %n hours", recur->frequency() ); if ( recur->duration() != -1 ) { txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); } return txt; } return recurStr; case Recurrence::rDaily: recurStr = i18n( "Recurs daily", "Recurs every %n days", recur->frequency() ); if ( recur->duration() != -1 ) { txt = i18n( "%1 until %2" ).arg( recurStr ).arg( recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); } return txt; } return recurStr; case Recurrence::rWeekly: { recurStr = i18n( "Recurs weekly", "Recurs every %n weeks", recur->frequency() ); bool addSpace = false; for ( int i = 0; i < 7; ++i ) { if ( recur->days().testBit( ( i + weekStart + 6 ) % 7 ) ) { if ( addSpace ) { dayNames.append( i18n( "separator for list of days", ", " ) ); } dayNames.append( calSys->weekDayName( ( ( i + weekStart + 6 ) % 7 ) + 1, true ) ); addSpace = true; } } if ( dayNames.isEmpty() ) { dayNames = i18n( "Recurs weekly on no days", "no days" ); } if ( recur->duration() != -1 ) { txt = i18n( "%1 on %2 until %3" ). arg( recurStr ).arg( dayNames ).arg( recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); } return txt; } txt = i18n( "%1 on %2" ).arg( recurStr ).arg( dayNames ); return txt; } case Recurrence::rMonthlyPos: { recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() ); if ( !recur->monthPositions().isEmpty() ) { KCal::RecurrenceRule::WDayPos rule = recur->monthPositions()[0]; if ( recur->duration() != -1 ) { txt = i18n( "%1 on the %2 %3 until %4" ). arg( recurStr ). arg( dayList[rule.pos() + 31] ). arg( calSys->weekDayName( rule.day(), false ) ). arg( recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); } return txt; } txt = i18n( "%1 on the %2 %3" ). arg( recurStr ). arg( dayList[rule.pos() + 31] ). arg( calSys->weekDayName( rule.day(), false ) ); return txt; } else { return recurStr; } break; } case Recurrence::rMonthlyDay: { recurStr = i18n( "Recurs monthly", "Recurs every %n months", recur->frequency() ); if ( !recur->monthDays().isEmpty() ) { int days = recur->monthDays()[0]; if ( recur->duration() != -1 ) { txt = i18n( "%1 on the %2 day until %3" ). arg( recurStr ). arg( dayList[days + 31] ). arg( recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); } return txt; } txt = i18n( "%1 on the %2 day" ).arg( recurStr ).arg( dayList[days + 31] ); return txt; } else { return recurStr; } break; } case Recurrence::rYearlyMonth: { recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() ); if ( recur->duration() != -1 ) { if ( !recur->yearDates().isEmpty() ) { txt = i18n( "%1 on %2 %3 until %4" ). arg( recurStr ). arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). arg( dayList[ recur->yearDates()[0] + 31 ] ). arg( recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); } return txt; } } if ( !recur->yearDates().isEmpty() ) { txt = i18n( "%1 on %2 %3" ). arg( recurStr ). arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). arg( dayList[ recur->yearDates()[0] + 31 ] ); return txt; } else { if ( !recur->yearMonths().isEmpty() ) { txt = i18n( "Recurs yearly on %1 %2" ). arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). arg( dayList[ recur->startDate().day() + 31 ] ); } else { txt = i18n( "Recurs yearly on %1 %2" ). arg( calSys->monthName( recur->startDate().month(), recur->startDate().year() ) ). arg( dayList[ recur->startDate().day() + 31 ] ); } return txt; } break; } case Recurrence::rYearlyDay: { recurStr = i18n( "Recurs yearly", "Recurs every %n years", recur->frequency() ); if ( !recur->yearDays().isEmpty() ) { if ( recur->duration() != -1 ) { txt = i18n( "%1 on day %2 until %3" ). arg( recurStr ). arg( recur->yearDays()[0] ). arg( recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); } return txt; } txt = i18n( "%1 on day %2" ).arg( recurStr ).arg( recur->yearDays()[0] ); return txt; } else { return recurStr; } break; } case Recurrence::rYearlyPos: { recurStr = i18n( "Every year", "Every %n years", recur->frequency() ); if ( !recur->yearPositions().isEmpty() && !recur->yearMonths().isEmpty() ) { KCal::RecurrenceRule::WDayPos rule = recur->yearPositions()[0]; if ( recur->duration() != -1 ) { txt = i18n( "%1 on the %2 %3 of %4 until %5" ). arg( recurStr ). arg( dayList[rule.pos() + 31] ). arg( calSys->weekDayName( rule.day(), false ) ). arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ). arg( recurEnd( incidence ) ); if ( recur->duration() > 0 ) { txt += i18n( " (%1 occurrences)" ).arg( recur->duration() ); } return txt; } txt = i18n( "%1 on the %2 %3 of %4" ). arg( recurStr ). arg( dayList[rule.pos() + 31] ). arg( calSys->weekDayName( rule.day(), false ) ). arg( calSys->monthName( recur->yearMonths()[0], recur->startDate().year() ) ); return txt; } else { return recurStr; } break; } } return i18n( "Incidence recurs" ); } TQString IncidenceFormatter::timeToString( const TQDateTime &date, bool shortfmt ) { return TDEGlobal::locale()->formatTime( date.time(), !shortfmt ); } TQString IncidenceFormatter::dateToString( const TQDateTime &date, bool shortfmt ) { return TDEGlobal::locale()->formatDate( date.date(), shortfmt ); } TQString IncidenceFormatter::dateTimeToString( const TQDateTime &date, bool allDay, bool shortfmt ) { if ( allDay ) { return dateToString( date, shortfmt ); } return TDEGlobal::locale()->formatDateTime( date, shortfmt ); } TQString IncidenceFormatter::resourceString( Calendar *calendar, Incidence *incidence ) { if ( !calendar || !incidence ) { return TQString(); } CalendarResources *calendarResource = dynamic_cast( calendar ); if ( !calendarResource ) { return TQString(); } ResourceCalendar *resourceCalendar = calendarResource->resource( incidence ); if ( resourceCalendar ) { if ( !resourceCalendar->subresources().isEmpty() ) { TQString subRes = resourceCalendar->subresourceIdentifier( incidence ); if ( subRes.isEmpty() ) { return resourceCalendar->resourceName(); } else { return resourceCalendar->labelForSubresource( subRes ); } } return resourceCalendar->resourceName(); } return TQString(); } static TQString secs2Duration( int secs ) { TQString tmp; int days = secs / 86400; if ( days > 0 ) { tmp += i18n( "1 day", "%n days", days ); tmp += ' '; secs -= ( days * 86400 ); } int hours = secs / 3600; if ( hours > 0 ) { tmp += i18n( "1 hour", "%n hours", hours ); tmp += ' '; secs -= ( hours * 3600 ); } int mins = secs / 60; if ( mins > 0 ) { tmp += i18n( "1 minute", "%n minutes", mins ); } return tmp; } TQString IncidenceFormatter::durationString( Incidence *incidence ) { TQString tmp; if ( incidence->type() == "Event" ) { Event *event = static_cast( incidence ); if ( event->hasEndDate() ) { if ( !event->doesFloat() ) { tmp = secs2Duration( event->dtStart().secsTo( event->dtEnd() ) ); } else { tmp = i18n( "1 day", "%n days", event->dtStart().date().daysTo( event->dtEnd().date() ) + 1 ); } } else { tmp = i18n( "forever" ); } } else if ( incidence->type() == "Todo" ) { Todo *todo = static_cast( incidence ); if ( todo->hasDueDate() ) { if ( todo->hasStartDate() ) { if ( !todo->doesFloat() ) { tmp = secs2Duration( todo->dtStart().secsTo( todo->dtDue() ) ); } else { tmp = i18n( "1 day", "%n days", todo->dtStart().date().daysTo( todo->dtDue().date() ) + 1 ); } } } } return tmp; } TQStringList IncidenceFormatter::reminderStringList( Incidence *incidence, bool shortfmt ) { //TODO: implement shortfmt=false Q_UNUSED( shortfmt ); TQStringList reminderStringList; if ( incidence ) { Alarm::List alarms = incidence->alarms(); Alarm::List::ConstIterator it; for ( it = alarms.begin(); it != alarms.end(); ++it ) { Alarm *alarm = *it; int offset = 0; TQString remStr, atStr, offsetStr; if ( alarm->hasTime() ) { offset = 0; if ( alarm->time().isValid() ) { atStr = TDEGlobal::locale()->formatDateTime( alarm->time() ); } } else if ( alarm->hasStartOffset() ) { offset = alarm->startOffset().asSeconds(); if ( offset < 0 ) { offset = -offset; offsetStr = i18n( "N days/hours/minutes before the start datetime", "%1 before the start" ); } else if ( offset > 0 ) { offsetStr = i18n( "N days/hours/minutes after the start datetime", "%1 after the start" ); } else { //offset is 0 if ( incidence->dtStart().isValid() ) { atStr = TDEGlobal::locale()->formatDateTime( incidence->dtStart() ); } } } else if ( alarm->hasEndOffset() ) { offset = alarm->endOffset().asSeconds(); if ( offset < 0 ) { offset = -offset; if ( incidence->type() == "Todo" ) { offsetStr = i18n( "N days/hours/minutes before the due datetime", "%1 before the to-do is due" ); } else { offsetStr = i18n( "N days/hours/minutes before the end datetime", "%1 before the end" ); } } else if ( offset > 0 ) { if ( incidence->type() == "Todo" ) { offsetStr = i18n( "N days/hours/minutes after the due datetime", "%1 after the to-do is due" ); } else { offsetStr = i18n( "N days/hours/minutes after the end datetime", "%1 after the end" ); } } else { //offset is 0 if ( incidence->type() == "Todo" ) { Todo *t = static_cast( incidence ); if ( t->dtDue().isValid() ) { atStr = TDEGlobal::locale()->formatDateTime( t->dtDue() ); } } else { Event *e = static_cast( incidence ); if ( e->dtEnd().isValid() ) { atStr = TDEGlobal::locale()->formatDateTime( e->dtEnd() ); } } } } if ( offset == 0 ) { if ( !atStr.isEmpty() ) { remStr = i18n( "reminder occurs at datetime", "at %1" ).arg( atStr ); } } else { remStr = offsetStr.arg( secs2Duration( offset ) ); } if ( alarm->repeatCount() > 0 ) { TQString countStr = i18n( "repeats once", "repeats %n times", alarm->repeatCount() ); TQString intervalStr = i18n( "interval is N days/hours/minutes", "interval is %1" ). arg( secs2Duration( alarm->snoozeTime().asSeconds() ) ); TQString repeatStr = i18n( "(repeat string, interval string)", "(%1, %2)" ). arg( countStr, intervalStr ); remStr = remStr + ' ' + repeatStr; } reminderStringList << remStr; } } return reminderStringList; }