• Skip to content
  • Skip to link menu
KDE 4.8 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

KAlarm Library

kaevent.cpp
00001 /*
00002  *  kaevent.cpp  -  represents calendar events
00003  *  This file is part of kalarmcal library, which provides access to KAlarm
00004  *  calendar data.
00005  *  Copyright © 2001-2011 by David Jarvie <djarvie@kde.org>
00006  *
00007  *  This library is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU Library General Public License as published
00009  *  by the Free Software Foundation; either version 2 of the License, or (at
00010  *  your option) any later version.
00011  *
00012  *  This library is distributed in the hope that it will be useful, but WITHOUT
00013  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00015  *  License for more details.
00016  *
00017  *  You should have received a copy of the GNU Library General Public License
00018  *  along with this library; see the file COPYING.LIB.  If not, write to the
00019  *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00020  *  MA 02110-1301, USA.
00021  */
00022 
00023 #include "kaevent.h"
00024 
00025 #include "alarmtext.h"
00026 #include "identities.h"
00027 #include "version.h"
00028 
00029 #ifndef USE_KRESOURCES
00030 #include <kcalcore/memorycalendar.h>
00031 #else
00032 #include <kcal/calendarlocal.h>
00033 #endif
00034 #include <kholidays/holidays.h>
00035 using namespace KHolidays;
00036 
00037 #include <ksystemtimezone.h>
00038 #include <klocale.h>
00039 #ifdef USE_KRESOURCES
00040 #include <kglobal.h>
00041 #include <kconfiggroup.h>
00042 #endif
00043 #include <kdebug.h>
00044 
00045 #ifndef USE_KRESOURCES
00046 using namespace KCalCore;
00047 #else
00048 using namespace KCal;
00049 #endif
00050 using namespace KHolidays;
00051 
00052 namespace KAlarmCal
00053 {
00054 
00055 #ifndef USE_KRESOURCES
00056 typedef KCalCore::Person  EmailAddress;
00057 class EmailAddressList : public KCalCore::Person::List
00058 #else
00059 typedef KCal::Person  EmailAddress;
00060 class EmailAddressList : public QList<KCal::Person>
00061 #endif
00062 {
00063     public:
00064 #ifndef USE_KRESOURCES
00065         EmailAddressList() : KCalCore::Person::List() { }
00066         EmailAddressList(const KCalCore::Person::List& list)  { operator=(list); }
00067         EmailAddressList& operator=(const KCalCore::Person::List&);
00068 #else
00069         EmailAddressList() : QList<KCal::Person>() { }
00070         EmailAddressList(const QList<KCal::Person>& list)  { operator=(list); }
00071         EmailAddressList& operator=(const QList<KCal::Person>&);
00072 #endif
00073         operator QStringList() const;
00074         QString     join(const QString& separator) const;
00075         QStringList pureAddresses() const;
00076         QString     pureAddresses(const QString& separator) const;
00077     private:
00078         QString     address(int index) const;
00079 };
00080 
00081 
00082 class KAAlarm::Private
00083 {
00084     public:
00085         Private();
00086 
00087         Action      mActionType;       // alarm action type
00088         Type        mType;             // alarm type
00089         DateTime    mNextMainDateTime; // next time to display the alarm, excluding repetitions
00090         Repetition  mRepetition;       // sub-repetition count and interval
00091         int         mNextRepeat;       // repetition count of next due sub-repetition
00092         bool        mRepeatAtLogin;    // whether to repeat the alarm at every login
00093         bool        mRecurs;           // there is a recurrence rule for the alarm
00094         bool        mDeferred;         // whether the alarm is an extra deferred/deferred-reminder alarm
00095         bool        mTimedDeferral;    // if mDeferred = true: true if the deferral is timed, false if date-only
00096 };
00097 
00098 
00099 class KAEvent::Private : public QSharedData
00100 {
00101     public:
00102         // Read-only internal flags additional to KAEvent::Flags enum values.
00103         // NOTE: If any values are added to those in KAEvent::Flags, ensure
00104         //       that these values don't overlap them.
00105         enum
00106         {
00107             REMINDER        = 0x100000,
00108             DEFERRAL        = 0x200000,
00109             TIMED_FLAG      = 0x400000,
00110             DATE_DEFERRAL   = DEFERRAL,
00111             TIME_DEFERRAL   = DEFERRAL | TIMED_FLAG,
00112             DISPLAYING_     = 0x800000,
00113             READ_ONLY_FLAGS = 0xF00000  
00114         };
00115         enum ReminderType   // current active state of reminder
00116         {
00117             NO_REMINDER,       // reminder is not due
00118             ACTIVE_REMINDER,   // reminder is due
00119             HIDDEN_REMINDER    // reminder-after is disabled due to main alarm being deferred past it
00120         };
00121         enum DeferType
00122         {
00123             NO_DEFERRAL = 0,   // there is no deferred alarm
00124             NORMAL_DEFERRAL,   // the main alarm, a recurrence or a repeat is deferred
00125             REMINDER_DEFERRAL  // a reminder alarm is deferred
00126         };
00127         // Alarm types.
00128         // This uses the same scheme as KAAlarm::Type, with some extra values.
00129         // Note that the actual enum values need not be the same as in KAAlarm::Type.
00130         enum AlarmType
00131         {
00132             INVALID_ALARM       = 0,     // Not an alarm
00133             MAIN_ALARM          = 1,     // THE real alarm. Must be the first in the enumeration.
00134             REMINDER_ALARM      = 0x02,  // Reminder in advance of/after the main alarm
00135             DEFERRED_ALARM      = 0x04,  // Deferred alarm
00136             DEFERRED_REMINDER_ALARM = REMINDER_ALARM | DEFERRED_ALARM,  // Deferred reminder alarm
00137             // The following values must be greater than the preceding ones, to
00138             // ensure that in ordered processing they are processed afterwards.
00139             AT_LOGIN_ALARM      = 0x10,  // Additional repeat-at-login trigger
00140             DISPLAYING_ALARM    = 0x20,  // Copy of the alarm currently being displayed
00141             // The following are extra internal KAEvent values
00142             AUDIO_ALARM         = 0x30,  // sound to play when displaying the alarm
00143             PRE_ACTION_ALARM    = 0x40,  // command to execute before displaying the alarm
00144             POST_ACTION_ALARM   = 0x50   // command to execute after the alarm window is closed
00145         };
00146 
00147         struct AlarmData
00148         {
00149 #ifndef USE_KRESOURCES
00150             Alarm::Ptr                  alarm;
00151 #else
00152             const Alarm*                alarm;
00153 #endif
00154             QString                     cleanText;       // text or audio file name
00155             uint                        emailFromId;
00156             QFont                       font;
00157             QColor                      bgColour, fgColour;
00158             float                       soundVolume;
00159             float                       fadeVolume;
00160             int                         fadeSeconds;
00161             int                         repeatSoundPause;
00162             int                         nextRepeat;
00163             bool                        speak;
00164             KAEvent::Private::AlarmType type;
00165             KAAlarm::Action             action;
00166             int                         displayingFlags;
00167             bool                        defaultFont;
00168             bool                        isEmailText;
00169             bool                        commandScript;
00170             bool                        cancelOnPreActErr;
00171             bool                        dontShowPreActErr;
00172             bool                        timedDeferral;
00173             bool                        hiddenReminder;
00174         };
00175         typedef QMap<AlarmType, AlarmData> AlarmMap;
00176 
00177         Private();
00178         Private(const KDateTime&, const QString& message, const QColor& bg, const QColor& fg,
00179                 const QFont& f, SubAction, int lateCancel, Flags flags, bool changesPending = false);
00180 #ifndef USE_KRESOURCES
00181         explicit Private(const KCalCore::Event::Ptr&);
00182 #else
00183         explicit Private(const KCal::Event*);
00184 #endif
00185         Private(const Private&);
00186         ~Private()         { delete mRecurrence; }
00187         Private&           operator=(const Private& e)       { if (&e != this) copy(e);  return *this; }
00188 #ifndef USE_KRESOURCES
00189         void               set(const KCalCore::Event::Ptr&);
00190 #else
00191         void               set(const KCal::Event*);
00192 #endif
00193         void               set(const KDateTime&, const QString& message, const QColor& bg, const QColor& fg,
00194                                const QFont&, SubAction, int lateCancel, Flags flags, bool changesPending = false);
00195         void               setAudioFile(const QString& filename, float volume, float fadeVolume, int fadeSeconds, int repeatPause, bool allowEmptyFile);
00196         OccurType          setNextOccurrence(const KDateTime& preDateTime);
00197         void               setFirstRecurrence();
00198         void               setCategory(CalEvent::Type);
00199         void               setRepeatAtLogin(bool);
00200         void               setRepeatAtLoginTrue(bool clearReminder);
00201         void               setReminder(int minutes, bool onceOnly);
00202         void               activateReminderAfter(const DateTime& mainAlarmTime);
00203         void               defer(const DateTime&, bool reminder, bool adjustRecurrence = false);
00204         void               cancelDefer();
00205 #ifndef USE_KRESOURCES
00206         bool               setDisplaying(const Private&, KAAlarm::Type, Akonadi::Collection::Id, const KDateTime& dt, bool showEdit, bool showDefer);
00207         void               reinstateFromDisplaying(const KCalCore::Event::Ptr&, Akonadi::Collection::Id&, bool& showEdit, bool& showDefer);
00208 #else
00209         bool               setDisplaying(const Private&, KAAlarm::Type, const QString& resourceID, const KDateTime& dt, bool showEdit, bool showDefer);
00210         void               reinstateFromDisplaying(const KCal::Event*, QString& resourceID, bool& showEdit, bool& showDefer);
00211         void               setCommandError(const QString& configString);
00212         void               setCommandError(CmdErrType, bool writeConfig) const;
00213 #endif
00214         void               startChanges()                 { ++mChangeCount; }
00215         void               endChanges();
00216         void               removeExpiredAlarm(KAAlarm::Type);
00217         KAAlarm            alarm(KAAlarm::Type) const;
00218         KAAlarm            firstAlarm() const;
00219         KAAlarm            nextAlarm(KAAlarm::Type) const;
00220 #ifndef USE_KRESOURCES
00221         bool               updateKCalEvent(const KCalCore::Event::Ptr&, UidAction, bool setCustomProperties = true) const;
00222 #else
00223         bool               updateKCalEvent(KCal::Event*, UidAction) const;
00224 #endif
00225         DateTime           mainDateTime(bool withRepeats = false) const
00226                                    { return (withRepeats && mNextRepeat && mRepetition)
00227                                             ? mRepetition.duration(mNextRepeat).end(mNextMainDateTime.kDateTime()) : mNextMainDateTime; }
00228         DateTime           mainEndRepeatTime() const
00229                                    { return mRepetition ? mRepetition.duration().end(mNextMainDateTime.kDateTime()) : mNextMainDateTime; }
00230         DateTime           deferralLimit(DeferLimitType* = 0) const;
00231         Flags              flags() const;
00232         bool               isWorkingTime(const KDateTime&) const;
00233         bool               setRepetition(const Repetition&);
00234         bool               occursAfter(const KDateTime& preDateTime, bool includeRepetitions) const;
00235         OccurType          nextOccurrence(const KDateTime& preDateTime, DateTime& result, OccurOption = IGNORE_REPETITION) const;
00236         OccurType          previousOccurrence(const KDateTime& afterDateTime, DateTime& result, bool includeRepetitions = false) const;
00237         void               setRecurrence(const KARecurrence&);
00238 #ifndef USE_KRESOURCES
00239         bool               setRecur(KCalCore::RecurrenceRule::PeriodType, int freq, int count, const QDate& end, KARecurrence::Feb29Type = KARecurrence::Feb29_None);
00240         bool               setRecur(KCalCore::RecurrenceRule::PeriodType, int freq, int count, const KDateTime& end, KARecurrence::Feb29Type = KARecurrence::Feb29_None);
00241 #else
00242         bool               setRecur(KCal::RecurrenceRule::PeriodType, int freq, int count, const QDate& end, KARecurrence::Feb29Type = KARecurrence::Feb29_None);
00243         bool               setRecur(KCal::RecurrenceRule::PeriodType, int freq, int count, const KDateTime& end, KARecurrence::Feb29Type = KARecurrence::Feb29_None);
00244 #endif
00245         KARecurrence::Type checkRecur() const;
00246         void               clearRecur();
00247         void               calcTriggerTimes() const;
00248 #ifdef KDE_NO_DEBUG_OUTPUT
00249         void               dumpDebug() const  { }
00250 #else
00251         void               dumpDebug() const;
00252 #endif
00253 #ifndef USE_KRESOURCES
00254         static bool        convertRepetition(const KCalCore::Event::Ptr&);
00255         static bool        convertStartOfDay(const KCalCore::Event::Ptr&);
00256         static DateTime    readDateTime(const KCalCore::Event::Ptr&, bool dateOnly, DateTime& start);
00257         static void        readAlarms(const KCalCore::Event::Ptr&, void* alarmMap, bool cmdDisplay = false);
00258         static void        readAlarm(const KCalCore::Alarm::Ptr&, AlarmData&, bool audioMain, bool cmdDisplay = false);
00259 #else
00260         static bool        convertRepetition(KCal::Event*);
00261         static bool        convertStartOfDay(KCal::Event*);
00262         static DateTime    readDateTime(const KCal::Event*, bool dateOnly, DateTime& start);
00263         static void        readAlarms(const KCal::Event*, void* alarmMap, bool cmdDisplay = false);
00264         static void        readAlarm(const KCal::Alarm*, AlarmData&, bool audioMain, bool cmdDisplay = false);
00265 #endif
00266 
00267     private:
00268         void               copy(const Private&);
00269         bool               mayOccurDailyDuringWork(const KDateTime&) const;
00270         int                nextWorkRepetition(const KDateTime& pre) const;
00271         void               calcNextWorkingTime(const DateTime& nextTrigger) const;
00272         DateTime           nextWorkingTime() const;
00273         OccurType          nextRecurrence(const KDateTime& preDateTime, DateTime& result) const;
00274 #ifndef USE_KRESOURCES
00275         void               setAudioAlarm(const KCalCore::Alarm::Ptr&) const;
00276         KCalCore::Alarm::Ptr initKCalAlarm(const KCalCore::Event::Ptr&, const DateTime&, const QStringList& types, AlarmType = INVALID_ALARM) const;
00277         KCalCore::Alarm::Ptr initKCalAlarm(const KCalCore::Event::Ptr&, int startOffsetSecs, const QStringList& types, AlarmType = INVALID_ALARM) const;
00278 #else
00279         void               setAudioAlarm(KCal::Alarm*) const;
00280         KCal::Alarm*       initKCalAlarm(KCal::Event*, const DateTime&, const QStringList& types, AlarmType = INVALID_ALARM) const;
00281         KCal::Alarm*       initKCalAlarm(KCal::Event*, int startOffsetSecs, const QStringList& types, AlarmType = INVALID_ALARM) const;
00282 #endif
00283         inline void        set_deferral(DeferType);
00284         inline void        activate_reminder(bool activate);
00285 
00286     public:
00287 #ifdef USE_KRESOURCES
00288         static QString     mCmdErrConfigGroup; // config file group for command error recording
00289 #endif
00290         static QFont       mDefaultFont;       // default alarm message font
00291         static const KHolidays::HolidayRegion* mHolidays;  // holiday region to use
00292         static QBitArray   mWorkDays;          // working days of the week
00293         static QTime       mWorkDayStart;      // start time of the working day
00294         static QTime       mWorkDayEnd;        // end time of the working day
00295         static int         mWorkTimeIndex;     // incremented every time working days/times are changed
00296 #ifdef USE_KRESOURCES
00297         AlarmResource*     mResource;          // resource which owns the event (for convenience - not used by this class)
00298 #endif
00299         mutable DateTime   mAllTrigger;        // next trigger time, including reminders, ignoring working hours
00300         mutable DateTime   mMainTrigger;       // next trigger time, ignoring reminders and working hours
00301         mutable DateTime   mAllWorkTrigger;    // next trigger time, taking account of reminders and working hours
00302         mutable DateTime   mMainWorkTrigger;   // next trigger time, ignoring reminders but taking account of working hours
00303         mutable CmdErrType mCommandError;      // command execution error last time the alarm triggered
00304 
00305         QString            mEventID;           // UID: KCal::Event unique ID
00306         QString            mTemplateName;      // alarm template's name, or null if normal event
00307 #ifndef USE_KRESOURCES
00308         QMap<QByteArray, QString> mCustomProperties;  // KCal::Event's non-KAlarm custom properties
00309         Akonadi::Item::Id  mItemId;            // Akonadi::Item ID for this event
00310         Akonadi::Collection::Id mOriginalCollectionId; // saved collection ID (not the collection the event is in)
00311 #else
00312         QString            mOriginalResourceId;// saved resource ID (not the resource the event is in)
00313 #endif
00314         QString            mText;              // message text, file URL, command, email body [or audio file for KAAlarm]
00315         QString            mAudioFile;         // ATTACH: audio file to play
00316         QString            mPreAction;         // command to execute before alarm is displayed
00317         QString            mPostAction;        // command to execute after alarm window is closed
00318         DateTime           mStartDateTime;     // DTSTART and DTEND: start and end time for event
00319         KDateTime          mCreatedDateTime;   // CREATED: date event was created, or saved in archive calendar
00320         DateTime           mNextMainDateTime;  // next time to display the alarm, excluding repetitions
00321         KDateTime          mAtLoginDateTime;   // repeat-at-login end time
00322         DateTime           mDeferralTime;      // extra time to trigger alarm (if alarm or reminder deferred)
00323         DateTime           mDisplayingTime;    // date/time shown in the alarm currently being displayed
00324         int                mDisplayingFlags;   // type of alarm which is currently being displayed
00325         int                mReminderMinutes;   // how long in advance reminder is to be, or 0 if none (<0 for reminder AFTER the alarm)
00326         DateTime           mReminderAfterTime; // if mReminderActive true, time to trigger reminder AFTER the main alarm, or invalid if not pending
00327         ReminderType       mReminderActive;    // whether a reminder is due (before next, or after last, main alarm/recurrence)
00328         int                mDeferDefaultMinutes; // default number of minutes for deferral dialog, or 0 to select time control
00329         bool               mDeferDefaultDateOnly;// select date-only by default in deferral dialog
00330         int                mRevision;          // SEQUENCE: revision number of the original alarm, or 0
00331         KARecurrence*      mRecurrence;        // RECUR: recurrence specification, or 0 if none
00332         Repetition         mRepetition;        // sub-repetition count and interval
00333         int                mNextRepeat;        // repetition count of next due sub-repetition
00334         int                mAlarmCount;        // number of alarms: count of !mMainExpired, mRepeatAtLogin, mDeferral, mReminderActive, mDisplaying
00335         DeferType          mDeferral;          // whether the alarm is an extra deferred/deferred-reminder alarm
00336         unsigned long      mKMailSerialNumber; // if email text, message's KMail serial number
00337         int                mTemplateAfterTime; // time not specified: use n minutes after default time, or -1 (applies to templates only)
00338         QColor             mBgColour;          // background colour of alarm message
00339         QColor             mFgColour;          // foreground colour of alarm message, or invalid for default
00340         QFont              mFont;              // font of alarm message (ignored if mUseDefaultFont true)
00341         uint               mEmailFromIdentity; // standard email identity uoid for 'From' field, or empty
00342         EmailAddressList   mEmailAddresses;    // ATTENDEE: addresses to send email to
00343         QString            mEmailSubject;      // SUMMARY: subject line of email
00344         QStringList        mEmailAttachments;  // ATTACH: email attachment file names
00345         mutable int        mChangeCount;       // >0 = inhibit calling calcTriggerTimes()
00346         mutable bool       mTriggerChanged;    // true if need to recalculate trigger times
00347         QString            mLogFile;           // alarm output is to be logged to this URL
00348         float              mSoundVolume;       // volume for sound file (range 0 - 1), or < 0 for unspecified
00349         float              mFadeVolume;        // initial volume for sound file (range 0 - 1), or < 0 for no fade
00350         int                mFadeSeconds;       // fade time (seconds) for sound file, or 0 if none
00351         int                mRepeatSoundPause;  // seconds to pause between sound file repetitions, or -1 if no repetition
00352         int                mLateCancel;        // how many minutes late will cancel the alarm, or 0 for no cancellation
00353         mutable const KHolidays::HolidayRegion*
00354                            mExcludeHolidays;   // non-null to not trigger alarms on holidays (= mHolidays when trigger calculated)
00355         mutable int        mWorkTimeOnly;      // non-zero to trigger alarm only during working hours (= mWorkTimeIndex when trigger calculated)
00356         SubAction          mActionSubType;     // sub-action type for the event's main alarm
00357         CalEvent::Type     mCategory;      // event category (active, archived, template, ...)
00358 #ifndef USE_KRESOURCES
00359         KACalendar::Compat mCompatibility; // event's storage format compatibility
00360         bool               mReadOnly;          // event is read-only in its original calendar file
00361 #endif
00362         bool               mCancelOnPreActErr; // cancel alarm if pre-alarm action fails
00363         bool               mDontShowPreActErr; // don't notify error if pre-alarm action fails
00364         bool               mConfirmAck;        // alarm acknowledgement requires confirmation by user
00365         bool               mUseDefaultFont;    // use default message font, not mFont
00366         bool               mCommandScript;     // the command text is a script, not a shell command line
00367         bool               mCommandXterm;      // command alarm is to be executed in a terminal window
00368         bool               mCommandDisplay;    // command output is to be displayed in an alarm window
00369         bool               mEmailBcc;          // blind copy the email to the user
00370         bool               mBeep;              // whether to beep when the alarm is displayed
00371         bool               mSpeak;             // whether to speak the message when the alarm is displayed
00372         bool               mCopyToKOrganizer;  // KOrganizer should hold a copy of the event
00373         bool               mReminderOnceOnly;  // the reminder is output only for the first recurrence
00374         bool               mAutoClose;         // whether to close the alarm window after the late-cancel period
00375         bool               mMainExpired;       // main alarm has expired (in which case a deferral alarm will exist)
00376         bool               mRepeatAtLogin;     // whether to repeat the alarm at every login
00377         bool               mArchiveRepeatAtLogin; // if now archived, original event was repeat-at-login
00378         bool               mArchive;           // event has triggered in the past, so archive it when closed
00379         bool               mDisplaying;        // whether the alarm is currently being displayed (i.e. in displaying calendar)
00380         bool               mDisplayingDefer;   // show Defer button (applies to displaying calendar only)
00381         bool               mDisplayingEdit;    // show Edit button (applies to displaying calendar only)
00382         bool               mEnabled;           // false if event is disabled
00383 
00384     public:
00385         static const QByteArray FLAGS_PROPERTY;
00386         static const QString DATE_ONLY_FLAG;
00387         static const QString EMAIL_BCC_FLAG;
00388         static const QString CONFIRM_ACK_FLAG;
00389         static const QString KORGANIZER_FLAG;
00390         static const QString EXCLUDE_HOLIDAYS_FLAG;
00391         static const QString WORK_TIME_ONLY_FLAG;
00392         static const QString REMINDER_ONCE_FLAG;
00393         static const QString DEFER_FLAG;
00394         static const QString LATE_CANCEL_FLAG;
00395         static const QString AUTO_CLOSE_FLAG;
00396         static const QString TEMPL_AFTER_TIME_FLAG;
00397         static const QString KMAIL_SERNUM_FLAG;
00398         static const QString ARCHIVE_FLAG;
00399         static const QByteArray NEXT_RECUR_PROPERTY;
00400         static const QByteArray REPEAT_PROPERTY;
00401         static const QByteArray LOG_PROPERTY;
00402         static const QString xtermURL;
00403         static const QString displayURL;
00404         static const QByteArray TYPE_PROPERTY;
00405         static const QString FILE_TYPE;
00406         static const QString AT_LOGIN_TYPE;
00407         static const QString REMINDER_TYPE;
00408         static const QString REMINDER_ONCE_TYPE;
00409         static const QString TIME_DEFERRAL_TYPE;
00410         static const QString DATE_DEFERRAL_TYPE;
00411         static const QString DISPLAYING_TYPE;
00412         static const QString PRE_ACTION_TYPE;
00413         static const QString POST_ACTION_TYPE;
00414         static const QString SOUND_REPEAT_TYPE;
00415         static const QByteArray NEXT_REPEAT_PROPERTY;
00416         static const QString HIDDEN_REMINDER_FLAG;
00417         static const QByteArray FONT_COLOUR_PROPERTY;
00418         static const QByteArray VOLUME_PROPERTY;
00419         static const QString EMAIL_ID_FLAG;
00420         static const QString SPEAK_FLAG;
00421         static const QString CANCEL_ON_ERROR_FLAG;
00422         static const QString DONT_SHOW_ERROR_FLAG;
00423         static const QString DISABLED_STATUS;
00424         static const QString DISP_DEFER;
00425         static const QString DISP_EDIT;
00426         static const QString CMD_ERROR_VALUE;
00427         static const QString CMD_ERROR_PRE_VALUE;
00428         static const QString CMD_ERROR_POST_VALUE;
00429         static const QString SC;
00430 };
00431 
00432 
00433 // KAlarm version which first used the current calendar/event format.
00434 // If this changes, KAEvent::convertKCalEvents() must be changed correspondingly.
00435 // The string version is the KAlarm version string used in the calendar file.
00436 QByteArray KAEvent::currentCalendarVersionString()  { return QByteArray("2.7.0"); }
00437 int        KAEvent::currentCalendarVersion()        { return Version(2,7,0); }
00438 
00439 // Custom calendar properties.
00440 // Note that all custom property names are prefixed with X-KDE-KALARM- in the calendar file.
00441 
00442 // Event properties
00443 const QByteArray KAEvent::Private::FLAGS_PROPERTY("FLAGS");              // X-KDE-KALARM-FLAGS property
00444 const QString    KAEvent::Private::DATE_ONLY_FLAG        = QLatin1String("DATE");
00445 const QString    KAEvent::Private::EMAIL_BCC_FLAG        = QLatin1String("BCC");
00446 const QString    KAEvent::Private::CONFIRM_ACK_FLAG      = QLatin1String("ACKCONF");
00447 const QString    KAEvent::Private::KORGANIZER_FLAG       = QLatin1String("KORG");
00448 const QString    KAEvent::Private::EXCLUDE_HOLIDAYS_FLAG = QLatin1String("EXHOLIDAYS");
00449 const QString    KAEvent::Private::WORK_TIME_ONLY_FLAG   = QLatin1String("WORKTIME");
00450 const QString    KAEvent::Private::REMINDER_ONCE_FLAG    = QLatin1String("ONCE");
00451 const QString    KAEvent::Private::DEFER_FLAG            = QLatin1String("DEFER");   // default defer interval for this alarm
00452 const QString    KAEvent::Private::LATE_CANCEL_FLAG      = QLatin1String("LATECANCEL");
00453 const QString    KAEvent::Private::AUTO_CLOSE_FLAG       = QLatin1String("LATECLOSE");
00454 const QString    KAEvent::Private::TEMPL_AFTER_TIME_FLAG = QLatin1String("TMPLAFTTIME");
00455 const QString    KAEvent::Private::KMAIL_SERNUM_FLAG     = QLatin1String("KMAIL");
00456 const QString    KAEvent::Private::ARCHIVE_FLAG          = QLatin1String("ARCHIVE");
00457 
00458 const QByteArray KAEvent::Private::NEXT_RECUR_PROPERTY("NEXTRECUR");     // X-KDE-KALARM-NEXTRECUR property
00459 const QByteArray KAEvent::Private::REPEAT_PROPERTY("REPEAT");            // X-KDE-KALARM-REPEAT property
00460 const QByteArray KAEvent::Private::LOG_PROPERTY("LOG");                  // X-KDE-KALARM-LOG property
00461 const QString    KAEvent::Private::xtermURL = QLatin1String("xterm:");
00462 const QString    KAEvent::Private::displayURL = QLatin1String("display:");
00463 
00464 // - General alarm properties
00465 const QByteArray KAEvent::Private::TYPE_PROPERTY("TYPE");                // X-KDE-KALARM-TYPE property
00466 const QString    KAEvent::Private::FILE_TYPE                  = QLatin1String("FILE");
00467 const QString    KAEvent::Private::AT_LOGIN_TYPE              = QLatin1String("LOGIN");
00468 const QString    KAEvent::Private::REMINDER_TYPE              = QLatin1String("REMINDER");
00469 const QString    KAEvent::Private::TIME_DEFERRAL_TYPE         = QLatin1String("DEFERRAL");
00470 const QString    KAEvent::Private::DATE_DEFERRAL_TYPE         = QLatin1String("DATE_DEFERRAL");
00471 const QString    KAEvent::Private::DISPLAYING_TYPE            = QLatin1String("DISPLAYING");   // used only in displaying calendar
00472 const QString    KAEvent::Private::PRE_ACTION_TYPE            = QLatin1String("PRE");
00473 const QString    KAEvent::Private::POST_ACTION_TYPE           = QLatin1String("POST");
00474 const QString    KAEvent::Private::SOUND_REPEAT_TYPE          = QLatin1String("SOUNDREPEAT");
00475 const QByteArray KAEvent::Private::NEXT_REPEAT_PROPERTY("NEXTREPEAT");   // X-KDE-KALARM-NEXTREPEAT property
00476 const QString    KAEvent::Private::HIDDEN_REMINDER_FLAG = QLatin1String("HIDE");
00477 // - Display alarm properties
00478 const QByteArray KAEvent::Private::FONT_COLOUR_PROPERTY("FONTCOLOR");    // X-KDE-KALARM-FONTCOLOR property
00479 // - Email alarm properties
00480 const QString    KAEvent::Private::EMAIL_ID_FLAG        = QLatin1String("EMAILID");
00481 // - Audio alarm properties
00482 const QByteArray KAEvent::Private::VOLUME_PROPERTY("VOLUME");            // X-KDE-KALARM-VOLUME property
00483 const QString    KAEvent::Private::SPEAK_FLAG           = QLatin1String("SPEAK");
00484 // - Command alarm properties
00485 const QString    KAEvent::Private::CANCEL_ON_ERROR_FLAG = QLatin1String("ERRCANCEL");
00486 const QString    KAEvent::Private::DONT_SHOW_ERROR_FLAG = QLatin1String("ERRNOSHOW");
00487 
00488 // Event status strings
00489 const QString    KAEvent::Private::DISABLED_STATUS            = QLatin1String("DISABLED");
00490 
00491 // Displaying event ID identifier
00492 const QString    KAEvent::Private::DISP_DEFER = QLatin1String("DEFER");
00493 const QString    KAEvent::Private::DISP_EDIT  = QLatin1String("EDIT");
00494 
00495 // Command error strings
00496 #ifdef USE_KRESOURCES
00497 QString          KAEvent::Private::mCmdErrConfigGroup = QLatin1String("CommandErrors");
00498 #endif
00499 const QString    KAEvent::Private::CMD_ERROR_VALUE      = QLatin1String("MAIN");
00500 const QString    KAEvent::Private::CMD_ERROR_PRE_VALUE  = QLatin1String("PRE");
00501 const QString    KAEvent::Private::CMD_ERROR_POST_VALUE = QLatin1String("POST");
00502 
00503 const QString    KAEvent::Private::SC = QLatin1String(";");
00504 
00505 QFont                           KAEvent::Private::mDefaultFont;
00506 const KHolidays::HolidayRegion* KAEvent::Private::mHolidays = 0;
00507 QBitArray                       KAEvent::Private::mWorkDays(7);
00508 QTime                           KAEvent::Private::mWorkDayStart(9, 0, 0);
00509 QTime                           KAEvent::Private::mWorkDayEnd(17, 0, 0);
00510 int                             KAEvent::Private::mWorkTimeIndex = 1;
00511 
00512 
00513 #ifndef USE_KRESOURCES
00514 static void setProcedureAlarm(const Alarm::Ptr&, const QString& commandLine);
00515 #else
00516 static void setProcedureAlarm(Alarm*, const QString& commandLine);
00517 #endif
00518 static QString reminderToString(int minutes);
00519 
00520 
00521 /*=============================================================================
00522 = Class KAEvent
00523 = Corresponds to a KCal::Event instance.
00524 =============================================================================*/
00525 
00526 inline void KAEvent::Private::set_deferral(DeferType type)
00527 {
00528     if (type)
00529     {
00530         if (mDeferral == NO_DEFERRAL)
00531             ++mAlarmCount;
00532     }
00533     else
00534     {
00535         if (mDeferral != NO_DEFERRAL)
00536             --mAlarmCount;
00537     }
00538     mDeferral = type;
00539 }
00540 
00541 inline void KAEvent::Private::activate_reminder(bool activate)
00542 {
00543     if (activate  &&  mReminderActive != ACTIVE_REMINDER  &&  mReminderMinutes)
00544     {
00545         if (mReminderActive == NO_REMINDER)
00546             ++mAlarmCount;
00547         mReminderActive = ACTIVE_REMINDER;
00548     }
00549     else if (!activate  &&  mReminderActive != NO_REMINDER)
00550     {
00551         mReminderActive = NO_REMINDER;
00552         mReminderAfterTime = DateTime();
00553         --mAlarmCount;
00554     }
00555 }
00556 
00557 KAEvent::KAEvent()
00558     : d(new Private)
00559 { }
00560 
00561 KAEvent::Private::Private()
00562     :
00563 #ifdef USE_KRESOURCES
00564       mResource(0),
00565 #endif
00566       mCommandError(CMD_NO_ERROR),
00567 #ifndef USE_KRESOURCES
00568       mItemId(-1),
00569 #endif
00570       mReminderMinutes(0),
00571       mReminderActive(NO_REMINDER),
00572       mRevision(0),
00573       mRecurrence(0),
00574       mNextRepeat(0),
00575       mAlarmCount(0),
00576       mDeferral(NO_DEFERRAL),
00577       mChangeCount(0),
00578       mTriggerChanged(false),
00579       mLateCancel(0),
00580       mExcludeHolidays(0),
00581       mWorkTimeOnly(0),
00582       mCategory(CalEvent::EMPTY),
00583 #ifndef USE_KRESOURCES
00584       mCompatibility(KACalendar::Current),
00585       mReadOnly(false),
00586 #endif
00587       mConfirmAck(false),
00588       mEmailBcc(false),
00589       mBeep(false),
00590       mAutoClose(false),
00591       mRepeatAtLogin(false),
00592       mDisplaying(false)
00593 { }
00594 
00595 KAEvent::KAEvent(const KDateTime& dt, const QString& message, const QColor& bg, const QColor& fg, const QFont& f,
00596                  SubAction action, int lateCancel, Flags flags, bool changesPending)
00597     : d(new Private(dt, message, bg, fg, f, action, lateCancel, flags, changesPending))
00598 {
00599 }
00600 
00601 KAEvent::Private::Private(const KDateTime& dt, const QString& message, const QColor& bg, const QColor& fg, const QFont& f,
00602                           SubAction action, int lateCancel, Flags flags, bool changesPending)
00603     : mRecurrence(0)
00604 {
00605     set(dt, message, bg, fg, f, action, lateCancel, flags, changesPending);
00606 }
00607 
00608 #ifndef USE_KRESOURCES
00609 KAEvent::KAEvent(const Event::Ptr& e)
00610 #else
00611 KAEvent::KAEvent(const Event* e)
00612 #endif
00613     : d(new Private(e))
00614 {
00615 }
00616 
00617 #ifndef USE_KRESOURCES
00618 KAEvent::Private::Private(const Event::Ptr& e)
00619 #else
00620 KAEvent::Private::Private(const Event* e)
00621 #endif
00622     : mRecurrence(0)
00623 {
00624     set(e);
00625 }
00626 
00627 KAEvent::Private::Private(const KAEvent::Private& e)
00628     : QSharedData(e),
00629       mRecurrence(0)
00630 {
00631     copy(e);
00632 }
00633 
00634 KAEvent::KAEvent(const KAEvent& other)
00635     : d(other.d)
00636 { }
00637 
00638 KAEvent::~KAEvent()
00639 { }
00640 
00641 KAEvent& KAEvent::operator=(const KAEvent& other)
00642 {
00643     if (&other != this)
00644         d = other.d;
00645     return *this;
00646 }
00647 
00648 /******************************************************************************
00649 * Copies the data from another instance.
00650 */
00651 void KAEvent::Private::copy(const KAEvent::Private& event)
00652 {
00653 #ifdef USE_KRESOURCES
00654     mResource                = event.mResource;
00655 #endif
00656     mAllTrigger              = event.mAllTrigger;
00657     mMainTrigger             = event.mMainTrigger;
00658     mAllWorkTrigger          = event.mAllWorkTrigger;
00659     mMainWorkTrigger         = event.mMainWorkTrigger;
00660     mCommandError            = event.mCommandError;
00661     mEventID                 = event.mEventID;
00662     mTemplateName            = event.mTemplateName;
00663 #ifndef USE_KRESOURCES
00664     mCustomProperties        = event.mCustomProperties;
00665     mItemId                  = event.mItemId;
00666     mOriginalCollectionId    = event.mOriginalCollectionId;
00667 #else
00668     mOriginalResourceId      = event.mOriginalResourceId;
00669 #endif
00670     mText                    = event.mText;
00671     mAudioFile               = event.mAudioFile;
00672     mPreAction               = event.mPreAction;
00673     mPostAction              = event.mPostAction;
00674     mStartDateTime           = event.mStartDateTime;
00675     mCreatedDateTime         = event.mCreatedDateTime;
00676     mNextMainDateTime        = event.mNextMainDateTime;
00677     mAtLoginDateTime         = event.mAtLoginDateTime;
00678     mDeferralTime            = event.mDeferralTime;
00679     mDisplayingTime          = event.mDisplayingTime;
00680     mDisplayingFlags         = event.mDisplayingFlags;
00681     mReminderMinutes         = event.mReminderMinutes;
00682     mReminderAfterTime       = event.mReminderAfterTime;
00683     mReminderActive          = event.mReminderActive;
00684     mDeferDefaultMinutes     = event.mDeferDefaultMinutes;
00685     mDeferDefaultDateOnly    = event.mDeferDefaultDateOnly;
00686     mRevision                = event.mRevision;
00687     mRepetition              = event.mRepetition;
00688     mNextRepeat              = event.mNextRepeat;
00689     mAlarmCount              = event.mAlarmCount;
00690     mDeferral                = event.mDeferral;
00691     mKMailSerialNumber       = event.mKMailSerialNumber;
00692     mTemplateAfterTime       = event.mTemplateAfterTime;
00693     mBgColour                = event.mBgColour;
00694     mFgColour                = event.mFgColour;
00695     mFont                    = event.mFont;
00696     mEmailFromIdentity       = event.mEmailFromIdentity;
00697     mEmailAddresses          = event.mEmailAddresses;
00698     mEmailSubject            = event.mEmailSubject;
00699     mEmailAttachments        = event.mEmailAttachments;
00700     mLogFile                 = event.mLogFile;
00701     mSoundVolume             = event.mSoundVolume;
00702     mFadeVolume              = event.mFadeVolume;
00703     mFadeSeconds             = event.mFadeSeconds;
00704     mRepeatSoundPause        = event.mRepeatSoundPause;
00705     mLateCancel              = event.mLateCancel;
00706     mExcludeHolidays         = event.mExcludeHolidays;
00707     mWorkTimeOnly            = event.mWorkTimeOnly;
00708     mActionSubType           = event.mActionSubType;
00709     mCategory                = event.mCategory;
00710 #ifndef USE_KRESOURCES
00711     mCompatibility           = event.mCompatibility;
00712     mReadOnly                = event.mReadOnly;
00713 #endif
00714     mCancelOnPreActErr       = event.mCancelOnPreActErr;
00715     mDontShowPreActErr       = event.mDontShowPreActErr;
00716     mConfirmAck              = event.mConfirmAck;
00717     mUseDefaultFont          = event.mUseDefaultFont;
00718     mCommandScript           = event.mCommandScript;
00719     mCommandXterm            = event.mCommandXterm;
00720     mCommandDisplay          = event.mCommandDisplay;
00721     mEmailBcc                = event.mEmailBcc;
00722     mBeep                    = event.mBeep;
00723     mSpeak                   = event.mSpeak;
00724     mCopyToKOrganizer        = event.mCopyToKOrganizer;
00725     mReminderOnceOnly        = event.mReminderOnceOnly;
00726     mAutoClose               = event.mAutoClose;
00727     mMainExpired             = event.mMainExpired;
00728     mRepeatAtLogin           = event.mRepeatAtLogin;
00729     mArchiveRepeatAtLogin    = event.mArchiveRepeatAtLogin;
00730     mArchive                 = event.mArchive;
00731     mDisplaying              = event.mDisplaying;
00732     mDisplayingDefer         = event.mDisplayingDefer;
00733     mDisplayingEdit          = event.mDisplayingEdit;
00734     mEnabled                 = event.mEnabled;
00735     mChangeCount             = 0;
00736     mTriggerChanged          = event.mTriggerChanged;
00737     delete mRecurrence;
00738     if (event.mRecurrence)
00739         mRecurrence = new KARecurrence(*event.mRecurrence);
00740     else
00741         mRecurrence = 0;
00742 }
00743 
00744 #ifndef USE_KRESOURCES
00745 void KAEvent::set(const Event::Ptr& e)
00746 #else
00747 void KAEvent::set(const Event* e)
00748 #endif
00749 {
00750     d->set(e);
00751 }
00752 
00753 /******************************************************************************
00754 * Initialise the KAEvent::Private from a KCal::Event.
00755 */
00756 #ifndef USE_KRESOURCES
00757 void KAEvent::Private::set(const Event::Ptr& event)
00758 #else
00759 void KAEvent::Private::set(const Event* event)
00760 #endif
00761 {
00762     startChanges();
00763     // Extract status from the event
00764     mCommandError           = CMD_NO_ERROR;
00765 #ifdef USE_KRESOURCES
00766     mResource               = 0;
00767 #endif
00768     mEventID                = event->uid();
00769     mRevision               = event->revision();
00770     mTemplateName.clear();
00771     mLogFile.clear();
00772 #ifndef USE_KRESOURCES
00773     mItemId                 = -1;
00774     mOriginalCollectionId   = -1;
00775 #else
00776     mOriginalResourceId.clear();
00777 #endif
00778     mTemplateAfterTime      = -1;
00779     mBeep                   = false;
00780     mSpeak                  = false;
00781     mEmailBcc               = false;
00782     mCommandXterm           = false;
00783     mCommandDisplay         = false;
00784     mCopyToKOrganizer       = false;
00785     mConfirmAck             = false;
00786     mArchive                = false;
00787     mReminderOnceOnly       = false;
00788     mAutoClose              = false;
00789     mArchiveRepeatAtLogin   = false;
00790     mDisplayingDefer        = false;
00791     mDisplayingEdit         = false;
00792     mDeferDefaultDateOnly   = false;
00793     mReminderActive         = NO_REMINDER;
00794     mReminderMinutes        = 0;
00795     mDeferDefaultMinutes    = 0;
00796     mLateCancel             = 0;
00797     mKMailSerialNumber      = 0;
00798     mExcludeHolidays        = 0;
00799     mWorkTimeOnly           = 0;
00800     mChangeCount            = 0;
00801     mBgColour               = QColor(255, 255, 255);    // missing/invalid colour - return white background
00802     mFgColour               = QColor(0, 0, 0);          // and black foreground
00803 #ifndef USE_KRESOURCES
00804     mCompatibility          = KACalendar::Current;
00805     mReadOnly               = event->isReadOnly();
00806 #endif
00807     mUseDefaultFont         = true;
00808     mEnabled                = true;
00809     clearRecur();
00810     QString param;
00811     bool ok;
00812     mCategory               = CalEvent::status(event, &param);
00813     if (mCategory == CalEvent::DISPLAYING)
00814     {
00815         // It's a displaying calendar event - set values specific to displaying alarms
00816         const QStringList params = param.split(SC, QString::KeepEmptyParts);
00817         int n = params.count();
00818         if (n)
00819         {
00820 #ifndef USE_KRESOURCES
00821             const qlonglong id = params[0].toLongLong(&ok);
00822             if (ok)
00823                 mOriginalCollectionId = id;
00824 #else
00825             mOriginalResourceId = params[0];
00826 #endif
00827             for (int i = 1;  i < n;  ++i)
00828             {
00829                 if (params[i] == DISP_DEFER)
00830                     mDisplayingDefer = true;
00831                 if (params[i] == DISP_EDIT)
00832                     mDisplayingEdit = true;
00833             }
00834         }
00835     }
00836 #ifndef USE_KRESOURCES
00837     // Store the non-KAlarm custom properties of the event
00838     const QByteArray kalarmKey = "X-KDE-" + KACalendar::APPNAME + '-';
00839     mCustomProperties = event->customProperties();
00840     for (QMap<QByteArray, QString>::Iterator it = mCustomProperties.begin();  it != mCustomProperties.end(); )
00841     {
00842         if (it.key().startsWith(kalarmKey))
00843             it = mCustomProperties.erase(it);
00844         else
00845             ++it;
00846     }
00847 #endif
00848 
00849     bool dateOnly = false;
00850     QStringList flags = event->customProperty(KACalendar::APPNAME, FLAGS_PROPERTY).split(SC, QString::SkipEmptyParts);
00851     flags << QString() << QString();    // to avoid having to check for end of list
00852     for (int i = 0, end = flags.count() - 1;  i < end;  ++i)
00853     {
00854         if (flags[i] == DATE_ONLY_FLAG)
00855             dateOnly = true;
00856         else if (flags[i] == CONFIRM_ACK_FLAG)
00857             mConfirmAck = true;
00858         else if (flags[i] == EMAIL_BCC_FLAG)
00859             mEmailBcc = true;
00860         else if (flags[i] == KORGANIZER_FLAG)
00861             mCopyToKOrganizer = true;
00862         else if (flags[i] == EXCLUDE_HOLIDAYS_FLAG)
00863             mExcludeHolidays = mHolidays;
00864         else if (flags[i] == WORK_TIME_ONLY_FLAG)
00865             mWorkTimeOnly = 1;
00866         else if (flags[i]== KMAIL_SERNUM_FLAG)
00867         {
00868             const unsigned long n = flags[i + 1].toULong(&ok);
00869             if (!ok)
00870                 continue;
00871             mKMailSerialNumber = n;
00872             ++i;
00873         }
00874         else if (flags[i] == Private::ARCHIVE_FLAG)
00875             mArchive = true;
00876         else if (flags[i] == Private::AT_LOGIN_TYPE)
00877             mArchiveRepeatAtLogin = true;
00878         else if (flags[i] == Private::REMINDER_TYPE)
00879         {
00880             if (flags[++i] == Private::REMINDER_ONCE_FLAG)
00881             {
00882                 mReminderOnceOnly = true;
00883                 ++i;
00884             }
00885             const int len = flags[i].length() - 1;
00886             mReminderMinutes = -flags[i].left(len).toInt();    // -> 0 if conversion fails
00887             switch (flags[i].at(len).toLatin1())
00888             {
00889                 case 'M':  break;
00890                 case 'H':  mReminderMinutes *= 60;  break;
00891                 case 'D':  mReminderMinutes *= 1440;  break;
00892                 default:   mReminderMinutes = 0;  break;
00893             }
00894         }
00895         else if (flags[i] == DEFER_FLAG)
00896         {
00897             QString mins = flags[i + 1];
00898             if (mins.endsWith('D'))
00899             {
00900                 mDeferDefaultDateOnly = true;
00901                 mins.truncate(mins.length() - 1);
00902             }
00903             const int n = static_cast<int>(mins.toUInt(&ok));
00904             if (!ok)
00905                 continue;
00906             mDeferDefaultMinutes = n;
00907             ++i;
00908         }
00909         else if (flags[i] == TEMPL_AFTER_TIME_FLAG)
00910         {
00911             const int n = static_cast<int>(flags[i + 1].toUInt(&ok));
00912             if (!ok)
00913                 continue;
00914             mTemplateAfterTime = n;
00915             ++i;
00916         }
00917         else if (flags[i] == LATE_CANCEL_FLAG)
00918         {
00919             mLateCancel = static_cast<int>(flags[i + 1].toUInt(&ok));
00920             if (ok)
00921                 ++i;
00922             if (!ok  ||  !mLateCancel)
00923                 mLateCancel = 1;    // invalid parameter defaults to 1 minute
00924         }
00925         else if (flags[i] == AUTO_CLOSE_FLAG)
00926         {
00927             mLateCancel = static_cast<int>(flags[i + 1].toUInt(&ok));
00928             if (ok)
00929                 ++i;
00930             if (!ok  ||  !mLateCancel)
00931                 mLateCancel = 1;    // invalid parameter defaults to 1 minute
00932             mAutoClose = true;
00933         }
00934     }
00935 
00936     QString prop = event->customProperty(KACalendar::APPNAME, LOG_PROPERTY);
00937     if (!prop.isEmpty())
00938     {
00939         if (prop == xtermURL)
00940             mCommandXterm = true;
00941         else if (prop == displayURL)
00942             mCommandDisplay = true;
00943         else
00944             mLogFile = prop;
00945     }
00946     prop = event->customProperty(KACalendar::APPNAME, REPEAT_PROPERTY);
00947     if (!prop.isEmpty())
00948     {
00949         // This property is used when the main alarm has expired
00950         const QStringList list = prop.split(QLatin1Char(':'));
00951         if (list.count() >= 2)
00952         {
00953             const int interval = static_cast<int>(list[0].toUInt());
00954             const int count = static_cast<int>(list[1].toUInt());
00955             if (interval && count)
00956             {
00957                 if (interval % (24*60))
00958                     mRepetition.set(Duration(interval * 60, Duration::Seconds), count);
00959                 else
00960                     mRepetition.set(Duration(interval / (24*60), Duration::Days), count);
00961             }
00962         }
00963     }
00964     mNextMainDateTime = readDateTime(event, dateOnly, mStartDateTime);
00965     mCreatedDateTime = event->created();
00966     if (dateOnly  &&  !mRepetition.isDaily())
00967         mRepetition.set(Duration(mRepetition.intervalDays(), Duration::Days));
00968     if (mCategory == CalEvent::TEMPLATE)
00969         mTemplateName = event->summary();
00970 #ifndef USE_KRESOURCES
00971     if (event->customStatus() == DISABLED_STATUS)
00972 #else
00973     if (event->statusStr() == DISABLED_STATUS)
00974 #endif
00975         mEnabled = false;
00976 
00977     // Extract status from the event's alarms.
00978     // First set up defaults.
00979     mActionSubType     = MESSAGE;
00980     mMainExpired       = true;
00981     mRepeatAtLogin     = false;
00982     mDisplaying        = false;
00983     mCommandScript     = false;
00984     mCancelOnPreActErr = false;
00985     mDontShowPreActErr = false;
00986     mDeferral          = NO_DEFERRAL;
00987     mSoundVolume       = -1;
00988     mFadeVolume        = -1;
00989     mRepeatSoundPause  = -1;
00990     mFadeSeconds       = 0;
00991     mEmailFromIdentity = 0;
00992     mReminderAfterTime = DateTime();
00993     mText.clear();
00994     mAudioFile.clear();
00995     mPreAction.clear();
00996     mPostAction.clear();
00997     mEmailSubject.clear();
00998     mEmailAddresses.clear();
00999     mEmailAttachments.clear();
01000 
01001     // Extract data from all the event's alarms and index the alarms by sequence number
01002     AlarmMap alarmMap;
01003     readAlarms(event, &alarmMap, mCommandDisplay);
01004 
01005     // Incorporate the alarms' details into the overall event
01006     mAlarmCount = 0;       // initialise as invalid
01007     DateTime alTime;
01008     bool set = false;
01009     bool isEmailText = false;
01010     bool setDeferralTime = false;
01011     Duration deferralOffset;
01012     for (AlarmMap::ConstIterator it = alarmMap.constBegin();  it != alarmMap.constEnd();  ++it)
01013     {
01014         const AlarmData& data = it.value();
01015         const DateTime dateTime = data.alarm->hasStartOffset() ? data.alarm->startOffset().end(mNextMainDateTime.effectiveKDateTime()) : data.alarm->time();
01016         switch (data.type)
01017         {
01018             case MAIN_ALARM:
01019                 mMainExpired = false;
01020                 alTime = dateTime;
01021                 alTime.setDateOnly(mStartDateTime.isDateOnly());
01022                 if (data.alarm->repeatCount()  &&  data.alarm->snoozeTime())
01023                 {
01024                     mRepetition.set(data.alarm->snoozeTime(), data.alarm->repeatCount());   // values may be adjusted in setRecurrence()
01025                     mNextRepeat = data.nextRepeat;
01026                 }
01027                 if (data.action != KAAlarm::AUDIO)
01028                     break;
01029                 // Fall through to AUDIO_ALARM
01030             case AUDIO_ALARM:
01031                 mAudioFile   = data.cleanText;
01032                 mSpeak       = data.speak  &&  mAudioFile.isEmpty();
01033                 mBeep        = !mSpeak  &&  mAudioFile.isEmpty();
01034                 mSoundVolume = (!mBeep && !mSpeak) ? data.soundVolume : -1;
01035                 mFadeVolume  = (mSoundVolume >= 0  &&  data.fadeSeconds > 0) ? data.fadeVolume : -1;
01036                 mFadeSeconds = (mFadeVolume >= 0) ? data.fadeSeconds : 0;
01037                 mRepeatSoundPause = (!mBeep && !mSpeak) ? data.repeatSoundPause : -1;
01038                 break;
01039             case AT_LOGIN_ALARM:
01040                 mRepeatAtLogin   = true;
01041                 mAtLoginDateTime = dateTime.kDateTime();
01042                 alTime = mAtLoginDateTime;
01043                 break;
01044             case REMINDER_ALARM:
01045                 // N.B. there can be a start offset but no valid date/time (e.g. in template)
01046                 if (data.alarm->startOffset().asSeconds() / 60)
01047                 {
01048                     mReminderActive = ACTIVE_REMINDER;
01049                     if (mReminderMinutes < 0)
01050                     {
01051                         mReminderAfterTime = dateTime;   // the reminder is AFTER the main alarm
01052                         mReminderAfterTime.setDateOnly(dateOnly);
01053                         if (data.hiddenReminder)
01054                             mReminderActive = HIDDEN_REMINDER;
01055                     }
01056                 }
01057                 break;
01058             case DEFERRED_REMINDER_ALARM:
01059             case DEFERRED_ALARM:
01060                 mDeferral = (data.type == DEFERRED_REMINDER_ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL;
01061                 mDeferralTime = dateTime;
01062                 if (!data.timedDeferral)
01063                     mDeferralTime.setDateOnly(true);
01064                 if (data.alarm->hasStartOffset())
01065                     deferralOffset = data.alarm->startOffset();
01066                 break;
01067             case DISPLAYING_ALARM:
01068             {
01069                 mDisplaying      = true;
01070                 mDisplayingFlags = data.displayingFlags;
01071                 const bool dateOnly = (mDisplayingFlags & DEFERRAL) ? !(mDisplayingFlags & TIMED_FLAG)
01072                                       : mStartDateTime.isDateOnly();
01073                 mDisplayingTime = dateTime;
01074                 mDisplayingTime.setDateOnly(dateOnly);
01075                 alTime = mDisplayingTime;
01076                 break;
01077             }
01078             case PRE_ACTION_ALARM:
01079                 mPreAction         = data.cleanText;
01080                 mCancelOnPreActErr = data.cancelOnPreActErr;
01081                 mDontShowPreActErr = data.dontShowPreActErr;
01082                 break;
01083             case POST_ACTION_ALARM:
01084                 mPostAction = data.cleanText;
01085                 break;
01086             case INVALID_ALARM:
01087             default:
01088                 break;
01089         }
01090 
01091         bool noSetNextTime = false;
01092         switch (data.type)
01093         {
01094             case DEFERRED_REMINDER_ALARM:
01095             case DEFERRED_ALARM:
01096                 if (!set)
01097                 {
01098                     // The recurrence has to be evaluated before we can
01099                     // calculate the time of a deferral alarm.
01100                     setDeferralTime = true;
01101                     noSetNextTime = true;
01102                 }
01103                 // fall through to REMINDER_ALARM
01104             case REMINDER_ALARM:
01105             case AT_LOGIN_ALARM:
01106             case DISPLAYING_ALARM:
01107                 if (!set  &&  !noSetNextTime)
01108                     mNextMainDateTime = alTime;
01109                 // fall through to MAIN_ALARM
01110             case MAIN_ALARM:
01111                 // Ensure that the basic fields are set up even if there is no main
01112                 // alarm in the event (if it has expired and then been deferred)
01113                 if (!set)
01114                 {
01115                     mActionSubType = (KAEvent::SubAction)data.action;
01116                     mText = (mActionSubType == COMMAND) ? data.cleanText.trimmed() : data.cleanText;
01117                     switch (data.action)
01118                     {
01119                         case KAAlarm::COMMAND:
01120                             mCommandScript = data.commandScript;
01121                             if (!mCommandDisplay)
01122                                 break;
01123                             // fall through to MESSAGE
01124                         case KAAlarm::MESSAGE:
01125                             mFont           = data.font;
01126                             mUseDefaultFont = data.defaultFont;
01127                             if (data.isEmailText)
01128                                 isEmailText = true;
01129                             // fall through to FILE
01130                         case KAAlarm::FILE:
01131                             mBgColour = data.bgColour;
01132                             mFgColour = data.fgColour;
01133                             break;
01134                         case KAAlarm::EMAIL:
01135                             mEmailFromIdentity = data.emailFromId;
01136                             mEmailAddresses    = data.alarm->mailAddresses();
01137                             mEmailSubject      = data.alarm->mailSubject();
01138                             mEmailAttachments  = data.alarm->mailAttachments();
01139                             break;
01140                         case KAAlarm::AUDIO:
01141                             // Already mostly handled above
01142                             mRepeatSoundPause = data.repeatSoundPause;
01143                             break;
01144                         default:
01145                             break;
01146                     }
01147                     set = true;
01148                 }
01149                 if (data.action == KAAlarm::FILE  &&  mActionSubType == MESSAGE)
01150                     mActionSubType = FILE;
01151                 ++mAlarmCount;
01152                 break;
01153             case AUDIO_ALARM:
01154             case PRE_ACTION_ALARM:
01155             case POST_ACTION_ALARM:
01156             case INVALID_ALARM:
01157             default:
01158                 break;
01159         }
01160     }
01161     if (!isEmailText)
01162         mKMailSerialNumber = 0;
01163 
01164     Recurrence* recur = event->recurrence();
01165     if (recur  &&  recur->recurs())
01166     {
01167         const int nextRepeat = mNextRepeat;    // setRecurrence() clears mNextRepeat
01168         setRecurrence(*recur);
01169         if (nextRepeat <= mRepetition.count())
01170             mNextRepeat = nextRepeat;
01171     }
01172     else if (mRepetition)
01173     {
01174         // Convert a repetition with no recurrence into a recurrence
01175         if (mRepetition.isDaily())
01176             recur->setDaily(mRepetition.intervalDays());
01177         else
01178             recur->setMinutely(mRepetition.intervalMinutes());
01179         recur->setDuration(mRepetition.count() + 1);
01180         mRepetition.set(0, 0);
01181     }
01182 
01183     if (mRepeatAtLogin)
01184     {
01185         mArchiveRepeatAtLogin = false;
01186         if (mReminderMinutes > 0)
01187         {
01188             mReminderMinutes = 0;      // pre-alarm reminder not allowed for at-login alarm
01189             mReminderActive  = NO_REMINDER;
01190         }
01191         setRepeatAtLoginTrue(false);   // clear other incompatible statuses
01192     }
01193 
01194     if (mMainExpired  &&  deferralOffset  &&  checkRecur() != KARecurrence::NO_RECUR)
01195     {
01196         // Adjust the deferral time for an expired recurrence, since the
01197         // offset is relative to the first actual occurrence.
01198         DateTime dt = mRecurrence->getNextDateTime(mStartDateTime.addDays(-1).kDateTime());
01199         dt.setDateOnly(mStartDateTime.isDateOnly());
01200         if (mDeferralTime.isDateOnly())
01201         {
01202             mDeferralTime = deferralOffset.end(dt.kDateTime());
01203             mDeferralTime.setDateOnly(true);
01204         }
01205         else
01206             mDeferralTime = deferralOffset.end(dt.effectiveKDateTime());
01207     }
01208     if (mDeferral != NO_DEFERRAL)
01209     {
01210         if (setDeferralTime)
01211             mNextMainDateTime = mDeferralTime;
01212     }
01213     mTriggerChanged = true;
01214     endChanges();
01215 }
01216 
01217 void KAEvent::set(const KDateTime& dt, const QString& message, const QColor& bg, const QColor& fg,
01218                   const QFont& f, SubAction act, int lateCancel, Flags flags, bool changesPending)
01219 {
01220     d->set(dt, message, bg, fg, f, act, lateCancel, flags, changesPending);
01221 }
01222 
01223 /******************************************************************************
01224 * Initialise the instance with the specified parameters.
01225 */
01226 void KAEvent::Private::set(const KDateTime& dateTime, const QString& text, const QColor& bg, const QColor& fg,
01227                            const QFont& font, SubAction action, int lateCancel, Flags flags, bool changesPending)
01228 {
01229     clearRecur();
01230     mStartDateTime = dateTime;
01231     mStartDateTime.setDateOnly(flags & ANY_TIME);
01232     mNextMainDateTime = mStartDateTime;
01233     switch (action)
01234     {
01235         case MESSAGE:
01236         case FILE:
01237         case COMMAND:
01238         case EMAIL:
01239         case AUDIO:
01240             mActionSubType = (KAEvent::SubAction)action;
01241             break;
01242         default:
01243             mActionSubType = MESSAGE;
01244             break;
01245     }
01246     mEventID.clear();
01247     mTemplateName.clear();
01248 #ifndef USE_KRESOURCES
01249     mItemId                 = -1;
01250     mOriginalCollectionId   = -1;
01251 #else
01252     mResource               = 0;
01253     mOriginalResourceId.clear();
01254 #endif
01255     mPreAction.clear();
01256     mPostAction.clear();
01257     mText                   = (mActionSubType == COMMAND) ? text.trimmed()
01258                               : (mActionSubType == AUDIO) ? QString() : text;
01259     mCategory               = CalEvent::ACTIVE;
01260     mAudioFile              = (mActionSubType == AUDIO) ? text : QString();
01261     mSoundVolume            = -1;
01262     mFadeVolume             = -1;
01263     mTemplateAfterTime      = -1;
01264     mFadeSeconds            = 0;
01265     mBgColour               = bg;
01266     mFgColour               = fg;
01267     mFont                   = font;
01268     mAlarmCount             = 1;
01269     mLateCancel             = lateCancel;     // do this before setting flags
01270     mDeferral               = NO_DEFERRAL;    // do this before setting flags
01271 
01272     mStartDateTime.setDateOnly(flags & ANY_TIME);
01273     set_deferral((flags & DEFERRAL) ? NORMAL_DEFERRAL : NO_DEFERRAL);
01274     mRepeatAtLogin          = flags & REPEAT_AT_LOGIN;
01275     mConfirmAck             = flags & CONFIRM_ACK;
01276     mUseDefaultFont         = flags & DEFAULT_FONT;
01277     mCommandScript          = flags & SCRIPT;
01278     mCommandXterm           = flags & EXEC_IN_XTERM;
01279     mCommandDisplay         = flags & DISPLAY_COMMAND;
01280     mCopyToKOrganizer       = flags & COPY_KORGANIZER;
01281     mExcludeHolidays        = (flags & EXCL_HOLIDAYS) ? mHolidays : 0;
01282     mWorkTimeOnly           = flags & WORK_TIME_ONLY;
01283     mEmailBcc               = flags & EMAIL_BCC;
01284     mEnabled                = !(flags & DISABLED);
01285     mDisplaying             = flags & DISPLAYING_;
01286     mReminderOnceOnly       = flags & REMINDER_ONCE;
01287     mAutoClose              = (flags & AUTO_CLOSE) && mLateCancel;
01288     mRepeatSoundPause       = (flags & REPEAT_SOUND) ? 0 : -1;
01289     mSpeak                  = (flags & SPEAK) && action != AUDIO;
01290     mBeep                   = (flags & BEEP) && action != AUDIO && !mSpeak;
01291     if (mRepeatAtLogin)                       // do this after setting other flags
01292     {
01293         ++mAlarmCount;
01294         setRepeatAtLoginTrue(false);
01295     }
01296 
01297     mKMailSerialNumber      = 0;
01298     mReminderMinutes        = 0;
01299     mDeferDefaultMinutes    = 0;
01300     mDeferDefaultDateOnly   = false;
01301     mArchiveRepeatAtLogin   = false;
01302     mReminderActive         = NO_REMINDER;
01303     mDisplaying             = false;
01304     mMainExpired            = false;
01305     mDisplayingDefer        = false;
01306     mDisplayingEdit         = false;
01307     mArchive                = false;
01308     mCancelOnPreActErr      = false;
01309     mDontShowPreActErr      = false;
01310     mReminderAfterTime      = DateTime();
01311 #ifndef USE_KRESOURCES
01312     mCompatibility          = KACalendar::Current;
01313     mReadOnly               = false;
01314 #endif
01315     mCommandError           = CMD_NO_ERROR;
01316     mChangeCount            = changesPending ? 1 : 0;
01317     mTriggerChanged         = true;
01318 }
01319 
01320 /******************************************************************************
01321 * Update an existing KCal::Event with the KAEvent::Private data.
01322 * If 'setCustomProperties' is true, all the KCal::Event's existing custom
01323 * properties are cleared and replaced with the KAEvent's custom properties. If
01324 * false, the KCal::Event's non-KAlarm custom properties are left untouched.
01325 */
01326 #ifndef USE_KRESOURCES
01327 bool KAEvent::updateKCalEvent(const KCalCore::Event::Ptr& e, UidAction u, bool setCustomProperties) const
01328 {
01329     return d->updateKCalEvent(e, u, setCustomProperties);
01330 }
01331 
01332 #else
01333 bool KAEvent::updateKCalEvent(KCal::Event* e, UidAction u) const
01334 {
01335     return d->updateKCalEvent(e, u);
01336 }
01337 #endif
01338 
01339 #ifndef USE_KRESOURCES
01340 bool KAEvent::Private::updateKCalEvent(const Event::Ptr& ev, UidAction uidact, bool setCustomProperties) const
01341 #else
01342 bool KAEvent::Private::updateKCalEvent(Event* ev, UidAction uidact) const
01343 #endif
01344 {
01345     // If it's an archived event, the event start date/time will be adjusted to its original
01346     // value instead of its next occurrence, and the expired main alarm will be reinstated.
01347     const bool archived = (mCategory == CalEvent::ARCHIVED);
01348 
01349     if (!ev
01350     ||  (uidact == UID_CHECK  &&  !mEventID.isEmpty()  &&  mEventID != ev->uid())
01351     ||  (!mAlarmCount  &&  (!archived || !mMainExpired)))
01352         return false;
01353 
01354     ev->startUpdates();   // prevent multiple update notifications
01355     checkRecur();         // ensure recurrence/repetition data is consistent
01356     const bool readOnly = ev->isReadOnly();
01357     if (uidact == KAEvent::UID_SET)
01358         ev->setUid(mEventID);
01359 #ifndef USE_KRESOURCES
01360     ev->setReadOnly(mReadOnly);
01361 #else
01362     ev->setReadOnly(false);
01363 #endif
01364     ev->setTransparency(Event::Transparent);
01365 
01366     // Set up event-specific data
01367 
01368     // Set up custom properties.
01369 #ifndef USE_KRESOURCES
01370     if (setCustomProperties)
01371         ev->setCustomProperties(mCustomProperties);
01372 #endif
01373     ev->removeCustomProperty(KACalendar::APPNAME, FLAGS_PROPERTY);
01374     ev->removeCustomProperty(KACalendar::APPNAME, NEXT_RECUR_PROPERTY);
01375     ev->removeCustomProperty(KACalendar::APPNAME, REPEAT_PROPERTY);
01376     ev->removeCustomProperty(KACalendar::APPNAME, LOG_PROPERTY);
01377 
01378     QString param;
01379     if (mCategory == CalEvent::DISPLAYING)
01380     {
01381 #ifndef USE_KRESOURCES
01382         param = QString::number(mOriginalCollectionId);
01383 #else
01384         param = mOriginalResourceId;
01385 #endif
01386         if (mDisplayingDefer)
01387             param += SC + DISP_DEFER;
01388         if (mDisplayingEdit)
01389             param += SC + DISP_EDIT;
01390     }
01391 #ifndef USE_KRESOURCES
01392     CalEvent::setStatus(ev, mCategory, param);
01393 #else
01394     CalEvent::setStatus(ev, mCategory, param);
01395 #endif
01396     QStringList flags;
01397     if (mStartDateTime.isDateOnly())
01398         flags += DATE_ONLY_FLAG;
01399     if (mConfirmAck)
01400         flags += CONFIRM_ACK_FLAG;
01401     if (mEmailBcc)
01402         flags += EMAIL_BCC_FLAG;
01403     if (mCopyToKOrganizer)
01404         flags += KORGANIZER_FLAG;
01405     if (mExcludeHolidays)
01406         flags += EXCLUDE_HOLIDAYS_FLAG;
01407     if (mWorkTimeOnly)
01408         flags += WORK_TIME_ONLY_FLAG;
01409     if (mLateCancel)
01410         (flags += (mAutoClose ? AUTO_CLOSE_FLAG : LATE_CANCEL_FLAG)) += QString::number(mLateCancel);
01411     if (mReminderMinutes)
01412     {
01413         flags += REMINDER_TYPE;
01414         if (mReminderOnceOnly)
01415             flags += REMINDER_ONCE_FLAG;
01416         flags += reminderToString(-mReminderMinutes);
01417     }
01418     if (mDeferDefaultMinutes)
01419     {
01420         QString param = QString::number(mDeferDefaultMinutes);
01421         if (mDeferDefaultDateOnly)
01422             param += 'D';
01423         (flags += DEFER_FLAG) += param;
01424     }
01425     if (!mTemplateName.isEmpty()  &&  mTemplateAfterTime >= 0)
01426         (flags += TEMPL_AFTER_TIME_FLAG) += QString::number(mTemplateAfterTime);
01427     if (mKMailSerialNumber)
01428         (flags += KMAIL_SERNUM_FLAG) += QString::number(mKMailSerialNumber);
01429     if (mArchive  &&  !archived)
01430     {
01431         flags += ARCHIVE_FLAG;
01432         if (mArchiveRepeatAtLogin)
01433             flags += AT_LOGIN_TYPE;
01434     }
01435     if (!flags.isEmpty())
01436         ev->setCustomProperty(KACalendar::APPNAME, FLAGS_PROPERTY, flags.join(SC));
01437 
01438     if (mCommandXterm)
01439         ev->setCustomProperty(KACalendar::APPNAME, LOG_PROPERTY, xtermURL);
01440     else if (mCommandDisplay)
01441         ev->setCustomProperty(KACalendar::APPNAME, LOG_PROPERTY, displayURL);
01442     else if (!mLogFile.isEmpty())
01443         ev->setCustomProperty(KACalendar::APPNAME, LOG_PROPERTY, mLogFile);
01444 
01445     ev->setCustomStatus(mEnabled ? QString() : DISABLED_STATUS);
01446     ev->setRevision(mRevision);
01447     ev->clearAlarms();
01448 
01449     /* Always set DTSTART as date/time, and use the category "DATE" to indicate
01450      * a date-only event, instead of calling setAllDay(). This is necessary to
01451      * allow a time zone to be specified for a date-only event. Also, KAlarm
01452      * allows the alarm to float within the 24-hour period defined by the
01453      * start-of-day time (which is user-dependent and therefore can't be
01454      * written into the calendar) rather than midnight to midnight, and there
01455      * is no RFC2445 conformant way to specify this.
01456      * RFC2445 states that alarm trigger times specified in absolute terms
01457      * (rather than relative to DTSTART or DTEND) can only be specified as a
01458      * UTC DATE-TIME value. So always use a time relative to DTSTART instead of
01459      * an absolute time.
01460      */
01461     ev->setDtStart(mStartDateTime.calendarKDateTime());
01462     ev->setAllDay(false);
01463     ev->setHasEndDate(false);
01464 
01465     const DateTime dtMain = archived ? mStartDateTime : mNextMainDateTime;
01466     int      ancillaryType = 0;   // 0 = invalid, 1 = time, 2 = offset
01467     DateTime ancillaryTime;       // time for ancillary alarms (pre-action, extra audio, etc)
01468     int      ancillaryOffset = 0; // start offset for ancillary alarms
01469     if (!mMainExpired  ||  archived)
01470     {
01471         /* The alarm offset must always be zero for the main alarm. To determine
01472          * which recurrence is due, the property X-KDE-KALARM_NEXTRECUR is used.
01473          * If the alarm offset was non-zero, exception dates and rules would not
01474          * work since they apply to the event time, not the alarm time.
01475          */
01476         if (!archived  &&  checkRecur() != KARecurrence::NO_RECUR)
01477         {
01478             QDateTime dt = mNextMainDateTime.kDateTime().toTimeSpec(mStartDateTime.timeSpec()).dateTime();
01479             ev->setCustomProperty(KACalendar::APPNAME, NEXT_RECUR_PROPERTY,
01480                                   dt.toString(mNextMainDateTime.isDateOnly() ? "yyyyMMdd" : "yyyyMMddThhmmss"));
01481         }
01482         // Add the main alarm
01483         initKCalAlarm(ev, 0, QStringList(), MAIN_ALARM);
01484         ancillaryOffset = 0;
01485         ancillaryType = dtMain.isValid() ? 2 : 0;
01486     }
01487     else if (mRepetition)
01488     {
01489         // Alarm repetition is normally held in the main alarm, but since
01490         // the main alarm has expired, store in a custom property.
01491         const QString param = QString("%1:%2").arg(mRepetition.intervalMinutes()).arg(mRepetition.count());
01492         ev->setCustomProperty(KACalendar::APPNAME, REPEAT_PROPERTY, param);
01493     }
01494 
01495     // Add subsidiary alarms
01496     if (mRepeatAtLogin  ||  (mArchiveRepeatAtLogin && archived))
01497     {
01498         DateTime dtl;
01499         if (mArchiveRepeatAtLogin)
01500             dtl = mStartDateTime.calendarKDateTime().addDays(-1);
01501         else if (mAtLoginDateTime.isValid())
01502             dtl = mAtLoginDateTime;
01503         else if (mStartDateTime.isDateOnly())
01504             dtl = DateTime(KDateTime::currentLocalDate().addDays(-1), mStartDateTime.timeSpec());
01505         else
01506             dtl = KDateTime::currentUtcDateTime();
01507         initKCalAlarm(ev, dtl, QStringList(AT_LOGIN_TYPE));
01508         if (!ancillaryType  &&  dtl.isValid())
01509         {
01510             ancillaryTime = dtl;
01511             ancillaryType = 1;
01512         }
01513     }
01514 
01515     // Find the base date/time for calculating alarm offsets
01516     DateTime nextDateTime = mNextMainDateTime;
01517     if (mMainExpired)
01518     {
01519         if (checkRecur() == KARecurrence::NO_RECUR)
01520             nextDateTime = mStartDateTime;
01521         else if (!archived)
01522         {
01523             // It's a deferral of an expired recurrence.
01524             // Need to ensure that the alarm offset is to an occurrence
01525             // which isn't excluded by an exception - otherwise, it will
01526             // never be triggered. So choose the first recurrence which
01527             // isn't an exception.
01528             KDateTime dt = mRecurrence->getNextDateTime(mStartDateTime.addDays(-1).kDateTime());
01529             dt.setDateOnly(mStartDateTime.isDateOnly());
01530             nextDateTime = dt;
01531         }
01532     }
01533 
01534     if (mReminderMinutes  &&  (mReminderActive != NO_REMINDER || archived))
01535     {
01536         int startOffset;
01537         if (mReminderMinutes < 0  &&  mReminderActive != NO_REMINDER)
01538         {
01539             // A reminder AFTER the main alarm is active or disabled
01540             startOffset = nextDateTime.calendarKDateTime().secsTo(mReminderAfterTime.calendarKDateTime());
01541         }
01542         else
01543         {
01544             // A reminder BEFORE the main alarm is active
01545             startOffset = -mReminderMinutes * 60;
01546         }
01547         initKCalAlarm(ev, startOffset, QStringList(REMINDER_TYPE));
01548         // Don't set ancillary time if the reminder AFTER is hidden by a deferral
01549         if (!ancillaryType  &&  (mReminderActive == ACTIVE_REMINDER || archived))
01550         {
01551             ancillaryOffset = startOffset;
01552             ancillaryType = 2;
01553         }
01554     }
01555     if (mDeferral != NO_DEFERRAL)
01556     {
01557         int startOffset;
01558         QStringList list;
01559         if (mDeferralTime.isDateOnly())
01560         {
01561             startOffset = nextDateTime.secsTo(mDeferralTime.calendarKDateTime());
01562             list += DATE_DEFERRAL_TYPE;
01563         }
01564         else
01565         {
01566             startOffset = nextDateTime.calendarKDateTime().secsTo(mDeferralTime.calendarKDateTime());
01567             list += TIME_DEFERRAL_TYPE;
01568         }
01569         if (mDeferral == REMINDER_DEFERRAL)
01570             list += REMINDER_TYPE;
01571         initKCalAlarm(ev, startOffset, list);
01572         if (!ancillaryType  &&  mDeferralTime.isValid())
01573         {
01574             ancillaryOffset = startOffset;
01575             ancillaryType = 2;
01576         }
01577     }
01578     if (!mTemplateName.isEmpty())
01579         ev->setSummary(mTemplateName);
01580     else if (mDisplaying)
01581     {
01582         QStringList list(DISPLAYING_TYPE);
01583         if (mDisplayingFlags & REPEAT_AT_LOGIN)
01584             list += AT_LOGIN_TYPE;
01585         else if (mDisplayingFlags & DEFERRAL)
01586         {
01587             if (mDisplayingFlags & TIMED_FLAG)
01588                 list += TIME_DEFERRAL_TYPE;
01589             else
01590                 list += DATE_DEFERRAL_TYPE;
01591         }
01592         if (mDisplayingFlags & REMINDER)
01593             list += REMINDER_TYPE;
01594         initKCalAlarm(ev, mDisplayingTime, list);
01595         if (!ancillaryType  &&  mDisplayingTime.isValid())
01596         {
01597             ancillaryTime = mDisplayingTime;
01598             ancillaryType = 1;
01599         }
01600     }
01601     if ((mBeep  ||  mSpeak  ||  !mAudioFile.isEmpty())  &&  mActionSubType != AUDIO)
01602     {
01603         // A sound is specified
01604         if (ancillaryType == 2)
01605             initKCalAlarm(ev, ancillaryOffset, QStringList(), AUDIO_ALARM);
01606         else
01607             initKCalAlarm(ev, ancillaryTime, QStringList(), AUDIO_ALARM);
01608     }
01609     if (!mPreAction.isEmpty())
01610     {
01611         // A pre-display action is specified
01612         if (ancillaryType == 2)
01613             initKCalAlarm(ev, ancillaryOffset, QStringList(PRE_ACTION_TYPE), PRE_ACTION_ALARM);
01614         else
01615             initKCalAlarm(ev, ancillaryTime, QStringList(PRE_ACTION_TYPE), PRE_ACTION_ALARM);
01616     }
01617     if (!mPostAction.isEmpty())
01618     {
01619         // A post-display action is specified
01620         if (ancillaryType == 2)
01621             initKCalAlarm(ev, ancillaryOffset, QStringList(POST_ACTION_TYPE), POST_ACTION_ALARM);
01622         else
01623             initKCalAlarm(ev, ancillaryTime, QStringList(POST_ACTION_TYPE), POST_ACTION_ALARM);
01624     }
01625 
01626     if (mRecurrence)
01627         mRecurrence->writeRecurrence(*ev->recurrence());
01628     else
01629         ev->clearRecurrence();
01630     if (mCreatedDateTime.isValid())
01631         ev->setCreated(mCreatedDateTime);
01632     ev->setReadOnly(readOnly);
01633     ev->endUpdates();     // finally issue an update notification
01634     return true;
01635 }
01636 
01637 /******************************************************************************
01638 * Create a new alarm for a libkcal event, and initialise it according to the
01639 * alarm action. If 'types' is non-null, it is appended to the X-KDE-KALARM-TYPE
01640 * property value list.
01641 * NOTE: The variant taking a DateTime calculates the offset from mStartDateTime,
01642 *       which is not suitable for an alarm in a recurring event.
01643 */
01644 #ifndef USE_KRESOURCES
01645 Alarm::Ptr KAEvent::Private::initKCalAlarm(const Event::Ptr& event, const DateTime& dt, const QStringList& types, AlarmType type) const
01646 #else
01647 Alarm* KAEvent::Private::initKCalAlarm(Event* event, const DateTime& dt, const QStringList& types, AlarmType type) const
01648 #endif
01649 {
01650     const int startOffset = dt.isDateOnly() ? mStartDateTime.secsTo(dt)
01651                                       : mStartDateTime.calendarKDateTime().secsTo(dt.calendarKDateTime());
01652     return initKCalAlarm(event, startOffset, types, type);
01653 }
01654 
01655 #ifndef USE_KRESOURCES
01656 Alarm::Ptr KAEvent::Private::initKCalAlarm(const Event::Ptr& event, int startOffsetSecs, const QStringList& types, AlarmType type) const
01657 #else
01658 Alarm* KAEvent::Private::initKCalAlarm(Event* event, int startOffsetSecs, const QStringList& types, AlarmType type) const
01659 #endif
01660 {
01661     QStringList alltypes;
01662     QStringList flags;
01663 #ifndef USE_KRESOURCES
01664     Alarm::Ptr alarm = event->newAlarm();
01665 #else
01666     Alarm* alarm = event->newAlarm();
01667 #endif
01668     alarm->setEnabled(true);
01669     if (type != MAIN_ALARM)
01670     {
01671         // RFC2445 specifies that absolute alarm times must be stored as a UTC DATE-TIME value.
01672         // Set the alarm time as an offset to DTSTART for the reasons described in updateKCalEvent().
01673         alarm->setStartOffset(startOffsetSecs);
01674     }
01675 
01676     switch (type)
01677     {
01678         case AUDIO_ALARM:
01679             setAudioAlarm(alarm);
01680             if (mSpeak)
01681                 flags << Private::SPEAK_FLAG;
01682             if (mRepeatSoundPause >= 0)
01683             {
01684                 // Alarm::setSnoozeTime() sets 5 seconds if duration parameter is zero,
01685                 // so repeat count = -1 represents 0 pause, -2 represents non-zero pause.
01686                 alarm->setRepeatCount(mRepeatSoundPause ? -2 : -1);
01687                 alarm->setSnoozeTime(Duration(mRepeatSoundPause, Duration::Seconds));
01688             }
01689             break;
01690         case PRE_ACTION_ALARM:
01691             setProcedureAlarm(alarm, mPreAction);
01692             if (mCancelOnPreActErr)
01693                 flags << Private::CANCEL_ON_ERROR_FLAG;
01694             if (mDontShowPreActErr)
01695                 flags << Private::DONT_SHOW_ERROR_FLAG;
01696             break;
01697         case POST_ACTION_ALARM:
01698             setProcedureAlarm(alarm, mPostAction);
01699             break;
01700         case MAIN_ALARM:
01701             alarm->setSnoozeTime(mRepetition.interval());
01702             alarm->setRepeatCount(mRepetition.count());
01703             if (mRepetition)
01704                 alarm->setCustomProperty(KACalendar::APPNAME, NEXT_REPEAT_PROPERTY,
01705                                          QString::number(mNextRepeat));
01706             // fall through to INVALID_ALARM
01707         case REMINDER_ALARM:
01708         case INVALID_ALARM:
01709         {
01710             if (types == QStringList(REMINDER_TYPE)
01711             &&  mReminderMinutes < 0  &&  mReminderActive == HIDDEN_REMINDER)
01712             {
01713                 // It's a reminder AFTER the alarm which is currently disabled
01714                 // due to the main alarm being deferred past it.
01715                 flags << HIDDEN_REMINDER_FLAG;
01716             }
01717             bool display = false;
01718             switch (mActionSubType)
01719             {
01720                 case FILE:
01721                     alltypes += FILE_TYPE;
01722                     // fall through to MESSAGE
01723                 case MESSAGE:
01724                     alarm->setDisplayAlarm(AlarmText::toCalendarText(mText));
01725                     display = true;
01726                     break;
01727                 case COMMAND:
01728                     if (mCommandScript)
01729                         alarm->setProcedureAlarm("", mText);
01730                     else
01731                         setProcedureAlarm(alarm, mText);
01732                     display = mCommandDisplay;
01733                     break;
01734                 case EMAIL:
01735                     alarm->setEmailAlarm(mEmailSubject, mText, mEmailAddresses, mEmailAttachments);
01736                     if (mEmailFromIdentity)
01737                         flags << Private::EMAIL_ID_FLAG << QString::number(mEmailFromIdentity);
01738                     break;
01739                 case AUDIO:
01740                     setAudioAlarm(alarm);
01741                     if (mRepeatSoundPause >= 0)
01742                         alltypes += SOUND_REPEAT_TYPE;
01743                     break;
01744             }
01745             if (display)
01746                 alarm->setCustomProperty(KACalendar::APPNAME, FONT_COLOUR_PROPERTY,
01747                                          QString::fromLatin1("%1;%2;%3").arg(mBgColour.name())
01748                                                                         .arg(mFgColour.name())
01749                                                                         .arg(mUseDefaultFont ? QString() : mFont.toString()));
01750             break;
01751         }
01752         case DEFERRED_ALARM:
01753         case DEFERRED_REMINDER_ALARM:
01754         case AT_LOGIN_ALARM:
01755         case DISPLAYING_ALARM:
01756             break;
01757     }
01758     alltypes += types;
01759     if (!alltypes.isEmpty())
01760         alarm->setCustomProperty(KACalendar::APPNAME, TYPE_PROPERTY, alltypes.join(","));
01761     if (!flags.isEmpty())
01762         alarm->setCustomProperty(KACalendar::APPNAME, FLAGS_PROPERTY, flags.join(SC));
01763     return alarm;
01764 }
01765 
01766 bool KAEvent::isValid() const
01767 {
01768     return d->mAlarmCount  &&  (d->mAlarmCount != 1 || !d->mRepeatAtLogin);
01769 }
01770 
01771 void KAEvent::setEnabled(bool enable)
01772 {
01773     d->mEnabled = enable;
01774 }
01775 
01776 bool KAEvent::enabled() const
01777 {
01778     return d->mEnabled;
01779 }
01780 
01781 #ifndef USE_KRESOURCES
01782 void KAEvent::setReadOnly(bool ro)
01783 {
01784     d->mReadOnly = ro;
01785 }
01786 
01787 bool KAEvent::isReadOnly() const
01788 {
01789     return d->mReadOnly;
01790 }
01791 #endif
01792 
01793 void KAEvent::setArchive()
01794 {
01795     d->mArchive = true;
01796 }
01797 
01798 bool KAEvent::toBeArchived() const
01799 {
01800     return d->mArchive;
01801 }
01802 
01803 bool KAEvent::mainExpired() const
01804 {
01805     return d->mMainExpired;
01806 }
01807 
01808 bool KAEvent::expired() const
01809 {
01810     return (d->mDisplaying && d->mMainExpired)  ||  d->mCategory == CalEvent::ARCHIVED;
01811 }
01812 
01813 KAEvent::Flags KAEvent::flags() const
01814 {
01815     return d->flags();
01816 }
01817 
01818 KAEvent::Flags KAEvent::Private::flags() const
01819 {
01820     Flags result(0);
01821     if (mBeep)                       result |= BEEP;
01822     if (mRepeatSoundPause >= 0)      result |= REPEAT_SOUND;
01823     if (mEmailBcc)                   result |= EMAIL_BCC;
01824     if (mStartDateTime.isDateOnly()) result |= ANY_TIME;
01825     if (mSpeak)                      result |= SPEAK;
01826     if (mRepeatAtLogin)              result |= REPEAT_AT_LOGIN;
01827     if (mConfirmAck)                 result |= CONFIRM_ACK;
01828     if (mUseDefaultFont)             result |= DEFAULT_FONT;
01829     if (mCommandScript)              result |= SCRIPT;
01830     if (mCommandXterm)               result |= EXEC_IN_XTERM;
01831     if (mCommandDisplay)             result |= DISPLAY_COMMAND;
01832     if (mCopyToKOrganizer)           result |= COPY_KORGANIZER;
01833     if (mExcludeHolidays)            result |= EXCL_HOLIDAYS;
01834     if (mWorkTimeOnly)               result |= WORK_TIME_ONLY;
01835     if (mReminderOnceOnly)           result |= REMINDER_ONCE;
01836     if (mAutoClose)                  result |= AUTO_CLOSE;
01837     if (!mEnabled)                   result |= DISABLED;
01838     return result;
01839 }
01840 
01841 /******************************************************************************
01842 * Change the type of an event.
01843 * If it is being set to archived, set the archived indication in the event ID;
01844 * otherwise, remove the archived indication from the event ID.
01845 */
01846 void KAEvent::setCategory(CalEvent::Type s)
01847 {
01848     d->setCategory(s);
01849 }
01850 
01851 void KAEvent::Private::setCategory(CalEvent::Type s)
01852 {
01853     if (s == mCategory)
01854         return;
01855     mEventID = CalEvent::uid(mEventID, s);
01856     mCategory = s;
01857     mTriggerChanged = true;   // templates and archived don't have trigger times
01858 }
01859 
01860 CalEvent::Type KAEvent::category() const
01861 {
01862     return d->mCategory;
01863 }
01864 
01865 void KAEvent::setEventId(const QString& id)
01866 {
01867     d->mEventID = id;
01868 }
01869 
01870 QString KAEvent::id() const
01871 {
01872     return d->mEventID;
01873 }
01874 
01875 void KAEvent::incrementRevision()
01876 {
01877     ++d->mRevision;
01878 }
01879 
01880 int KAEvent::revision() const
01881 {
01882     return d->mRevision;
01883 }
01884 
01885 #ifndef USE_KRESOURCES
01886 void KAEvent::setItemId(Akonadi::Item::Id id)
01887 {
01888     d->mItemId = id;
01889 }
01890 
01891 Akonadi::Item::Id KAEvent::itemId() const
01892 {
01893     return d->mItemId;
01894 }
01895 
01896 /******************************************************************************
01897 * Initialise an Item with the event.
01898 * Note that the event is not updated with the Item ID.
01899 * Reply = true if successful,
01900 *         false if event's category does not match collection's mime types.
01901 */
01902 bool KAEvent::setItemPayload(Akonadi::Item& item, const QStringList& collectionMimeTypes) const
01903 {
01904     QString mimetype;
01905     switch (d->mCategory)
01906     {
01907         case CalEvent::ACTIVE:      mimetype = MIME_ACTIVE;    break;
01908         case CalEvent::ARCHIVED:    mimetype = MIME_ARCHIVED;  break;
01909         case CalEvent::TEMPLATE:    mimetype = MIME_TEMPLATE;  break;
01910         default:                            Q_ASSERT(0);  return false;
01911     }
01912     if (!collectionMimeTypes.contains(mimetype))
01913         return false;
01914     item.setMimeType(mimetype);
01915     item.setPayload<KAEvent>(*this);
01916     return true;
01917 }
01918 
01919 void KAEvent::setCompatibility(KACalendar::Compat c)
01920 {
01921     d->mCompatibility = c;
01922 }
01923 
01924 KACalendar::Compat KAEvent::compatibility() const
01925 {
01926     return d->mCompatibility;
01927 }
01928 
01929 QMap<QByteArray, QString> KAEvent::customProperties() const
01930 {
01931     return d->mCustomProperties;
01932 }
01933 
01934 #else
01935 void KAEvent::setResource(AlarmResource* r)
01936 {
01937     d->mResource = r;
01938 }
01939 
01940 AlarmResource* KAEvent::resource() const
01941 {
01942     return d->mResource;
01943 }
01944 #endif
01945 
01946 KAEvent::SubAction KAEvent::actionSubType() const
01947 {
01948     return d->mActionSubType;
01949 }
01950 
01951 KAEvent::Actions KAEvent::actionTypes() const
01952 {
01953     switch (d->mActionSubType)
01954     {
01955         case MESSAGE:
01956         case FILE:     return ACT_DISPLAY;
01957         case COMMAND:  return d->mCommandDisplay ? ACT_DISPLAY_COMMAND : ACT_COMMAND;
01958         case EMAIL:    return ACT_EMAIL;
01959         case AUDIO:    return ACT_AUDIO;
01960         default:       return ACT_NONE;
01961     }
01962 }
01963 
01964 void KAEvent::setLateCancel(int minutes)
01965 {
01966     if (d->mRepeatAtLogin)
01967         minutes = 0;
01968     d->mLateCancel = minutes;
01969     if (!minutes)
01970         d->mAutoClose = false;
01971 }
01972 
01973 int KAEvent::lateCancel() const
01974 {
01975     return d->mLateCancel;
01976 }
01977 
01978 void KAEvent::setAutoClose(bool ac)
01979 {
01980     d->mAutoClose = ac;
01981 }
01982 
01983 bool KAEvent::autoClose() const
01984 {
01985     return d->mAutoClose;
01986 }
01987 
01988 void KAEvent::setKMailSerialNumber(unsigned long n)
01989 {
01990     d->mKMailSerialNumber = n;
01991 }
01992 
01993 unsigned long KAEvent::kmailSerialNumber() const
01994 {
01995     return d->mKMailSerialNumber;
01996 }
01997 
01998 QString KAEvent::cleanText() const
01999 {
02000     return d->mText;
02001 }
02002 
02003 QString KAEvent::message() const
02004 {
02005     return (d->mActionSubType == MESSAGE
02006          || d->mActionSubType == EMAIL) ? d->mText : QString();
02007 }
02008 
02009 QString KAEvent::displayMessage() const
02010 {
02011     return (d->mActionSubType == MESSAGE) ? d->mText : QString();
02012 }
02013 
02014 QString KAEvent::fileName() const
02015 {
02016     return (d->mActionSubType == FILE) ? d->mText : QString();
02017 }
02018 
02019 QColor KAEvent::bgColour() const
02020 {
02021     return d->mBgColour;
02022 }
02023 
02024 QColor KAEvent::fgColour() const
02025 {
02026     return d->mFgColour;
02027 }
02028 
02029 void KAEvent::setDefaultFont(const QFont& f)
02030 {
02031     Private::mDefaultFont = f;
02032 }
02033 
02034 bool KAEvent::useDefaultFont() const
02035 {
02036     return d->mUseDefaultFont;
02037 }
02038 
02039 QFont KAEvent::font() const
02040 {
02041     return d->mUseDefaultFont ? Private::mDefaultFont : d->mFont;
02042 }
02043 
02044 QString KAEvent::command() const
02045 {
02046     return (d->mActionSubType == COMMAND) ? d->mText : QString();
02047 }
02048 
02049 bool KAEvent::commandScript() const
02050 {
02051     return d->mCommandScript;
02052 }
02053 
02054 bool KAEvent::commandXterm() const
02055 {
02056     return d->mCommandXterm;
02057 }
02058 
02059 bool KAEvent::commandDisplay() const
02060 {
02061     return d->mCommandDisplay;
02062 }
02063 
02064 #ifndef USE_KRESOURCES
02065 void KAEvent::setCommandError(CmdErrType t) const
02066 {
02067     d->mCommandError = t;
02068 }
02069 
02070 #else
02071 /******************************************************************************
02072 * Set the command last error status.
02073 * If 'writeConfig' is true, the status is written to the config file.
02074 */
02075 void KAEvent::setCommandError(CmdErrType t, bool writeConfig) const
02076 {
02077     d->setCommandError(t, writeConfig);
02078 }
02079 
02080 void KAEvent::Private::setCommandError(CmdErrType error, bool writeConfig) const
02081 {
02082     kDebug() << mEventID << "," << error;
02083     if (error == mCommandError)
02084         return;
02085     mCommandError = error;
02086     if (writeConfig)
02087     {
02088         KConfigGroup config(KGlobal::config(), mCmdErrConfigGroup);
02089         if (mCommandError == CMD_NO_ERROR)
02090             config.deleteEntry(mEventID);
02091         else
02092         {
02093             QString errtext;
02094             switch (mCommandError)
02095             {
02096                 case CMD_ERROR:       errtext = CMD_ERROR_VALUE;  break;
02097                 case CMD_ERROR_PRE:   errtext = CMD_ERROR_PRE_VALUE;  break;
02098                 case CMD_ERROR_POST:  errtext = CMD_ERROR_POST_VALUE;  break;
02099                 case CMD_ERROR_PRE_POST:
02100                     errtext = CMD_ERROR_PRE_VALUE + ',' + CMD_ERROR_POST_VALUE;
02101                     break;
02102                 default:
02103                     break;
02104             }
02105             config.writeEntry(mEventID, errtext);
02106         }
02107         config.sync();
02108     }
02109 }
02110 
02111 /******************************************************************************
02112 * Initialise the command last error status of the alarm from the config file.
02113 */
02114 void KAEvent::setCommandError(const QString& configString)
02115 {
02116     d->setCommandError(configString);
02117 }
02118 
02119 void KAEvent::Private::setCommandError(const QString& configString)
02120 {
02121     mCommandError = CMD_NO_ERROR;
02122     const QStringList errs = configString.split(',');
02123     if (errs.indexOf(CMD_ERROR_VALUE) >= 0)
02124         mCommandError = CMD_ERROR;
02125     else
02126     {
02127         if (errs.indexOf(CMD_ERROR_PRE_VALUE) >= 0)
02128             mCommandError = CMD_ERROR_PRE;
02129         if (errs.indexOf(CMD_ERROR_POST_VALUE) >= 0)
02130             mCommandError = static_cast<CmdErrType>(mCommandError | CMD_ERROR_POST);
02131     }
02132 }
02133 
02134 QString KAEvent::commandErrorConfigGroup()
02135 {
02136     return Private::mCmdErrConfigGroup;
02137 }
02138 #endif
02139 
02140 KAEvent::CmdErrType KAEvent::commandError() const
02141 {
02142     return d->mCommandError;
02143 }
02144 
02145 void KAEvent::setLogFile(const QString& logfile)
02146 {
02147     d->mLogFile = logfile;
02148     if (!logfile.isEmpty())
02149         d->mCommandDisplay = d->mCommandXterm = false;
02150 }
02151 
02152 QString KAEvent::logFile() const
02153 {
02154     return d->mLogFile;
02155 }
02156 
02157 bool KAEvent::confirmAck() const
02158 {
02159     return d->mConfirmAck;
02160 }
02161 
02162 bool KAEvent::copyToKOrganizer() const
02163 {
02164     return d->mCopyToKOrganizer;
02165 }
02166 
02167 #ifndef USE_KRESOURCES
02168 void KAEvent::setEmail(uint from, const KCalCore::Person::List& addresses, const QString& subject,
02169                       const QStringList& attachments)
02170 #else
02171 void KAEvent::setEmail(uint from, const QList<KCal::Person>& addresses, const QString& subject,
02172                       const QStringList& attachments)
02173 #endif
02174 {
02175     d->mEmailFromIdentity = from;
02176     d->mEmailAddresses    = addresses;
02177     d->mEmailSubject      = subject;
02178     d->mEmailAttachments  = attachments;
02179 }
02180 
02181 QString KAEvent::emailMessage() const
02182 {
02183     return (d->mActionSubType == EMAIL) ? d->mText : QString();
02184 }
02185 
02186 uint KAEvent::emailFromId() const
02187 {
02188     return d->mEmailFromIdentity;
02189 }
02190 
02191 #ifndef USE_KRESOURCES
02192 KCalCore::Person::List KAEvent::emailAddressees() const
02193 #else
02194 QList<KCal::Person> KAEvent::emailAddressees() const
02195 #endif
02196 {
02197     return d->mEmailAddresses;
02198 }
02199 
02200 QStringList KAEvent::emailAddresses() const
02201 {
02202     return static_cast<QStringList>(d->mEmailAddresses);
02203 }
02204 
02205 QString KAEvent::emailAddresses(const QString& sep) const
02206 {
02207     return d->mEmailAddresses.join(sep);
02208 }
02209 
02210 #ifndef USE_KRESOURCES
02211 QString KAEvent::joinEmailAddresses(const KCalCore::Person::List& addresses, const QString& separator)
02212 #else
02213 QString KAEvent::joinEmailAddresses(const QList<KCal::Person>& addresses, const QString& separator)
02214 #endif
02215 {
02216     return EmailAddressList(addresses).join(separator);
02217 }
02218 
02219 QStringList KAEvent::emailPureAddresses() const
02220 {
02221     return d->mEmailAddresses.pureAddresses();
02222 }
02223 
02224 QString KAEvent::emailPureAddresses(const QString& sep) const
02225 {
02226     return d->mEmailAddresses.pureAddresses(sep);
02227 }
02228 
02229 QString KAEvent::emailSubject() const
02230 {
02231     return d->mEmailSubject;
02232 }
02233 
02234 QStringList KAEvent::emailAttachments() const
02235 {
02236     return d->mEmailAttachments;
02237 }
02238 
02239 QString KAEvent::emailAttachments(const QString& sep) const
02240 {
02241     return d->mEmailAttachments.join(sep);
02242 }
02243 
02244 bool KAEvent::emailBcc() const
02245 {
02246     return d->mEmailBcc;
02247 }
02248 
02249 void KAEvent::setAudioFile(const QString& filename, float volume, float fadeVolume, int fadeSeconds, int repeatPause, bool allowEmptyFile)
02250 {
02251     d->setAudioFile(filename, volume, fadeVolume, fadeSeconds, repeatPause, allowEmptyFile);
02252 }
02253 
02254 void KAEvent::Private::setAudioFile(const QString& filename, float volume, float fadeVolume, int fadeSeconds, int repeatPause, bool allowEmptyFile)
02255 {
02256     mAudioFile = filename;
02257     mSoundVolume = (!allowEmptyFile && filename.isEmpty()) ? -1 : volume;
02258     if (mSoundVolume >= 0)
02259     {
02260         mFadeVolume  = (fadeSeconds > 0) ? fadeVolume : -1;
02261         mFadeSeconds = (mFadeVolume >= 0) ? fadeSeconds : 0;
02262     }
02263     else
02264     {
02265         mFadeVolume  = -1;
02266         mFadeSeconds = 0;
02267     }
02268     mRepeatSoundPause = repeatPause;
02269 }
02270 
02271 QString KAEvent::audioFile() const
02272 {
02273     return d->mAudioFile;
02274 }
02275 
02276 float KAEvent::soundVolume() const
02277 {
02278     return d->mSoundVolume;
02279 }
02280 
02281 float KAEvent::fadeVolume() const
02282 {
02283     return d->mSoundVolume >= 0 && d->mFadeSeconds ? d->mFadeVolume : -1;
02284 }
02285 
02286 int KAEvent::fadeSeconds() const
02287 {
02288     return d->mSoundVolume >= 0 && d->mFadeVolume >= 0 ? d->mFadeSeconds : 0;
02289 }
02290 
02291 bool KAEvent::repeatSound() const
02292 {
02293     return d->mRepeatSoundPause >= 0;
02294 }
02295  
02296 int KAEvent::repeatSoundPause() const
02297 {
02298     return d->mRepeatSoundPause;
02299 }
02300 
02301 bool KAEvent::beep() const
02302 {
02303     return d->mBeep;
02304 }
02305 
02306 bool KAEvent::speak() const
02307 {
02308     return (d->mActionSubType == MESSAGE
02309             ||  (d->mActionSubType == COMMAND && d->mCommandDisplay))
02310         && d->mSpeak;
02311 }
02312 
02313 /******************************************************************************
02314 * Set the event to be an alarm template.
02315 */
02316 void KAEvent::setTemplate(const QString& name, int afterTime)
02317 {
02318     d->setCategory(CalEvent::TEMPLATE);
02319     d->mTemplateName = name;
02320     d->mTemplateAfterTime = afterTime;
02321     d->mTriggerChanged = true;   // templates and archived don't have trigger times
02322 }
02323 
02324 bool KAEvent::isTemplate() const
02325 {
02326     return !d->mTemplateName.isEmpty();
02327 }
02328 
02329 QString KAEvent::templateName() const
02330 {
02331     return d->mTemplateName;
02332 }
02333 
02334 bool KAEvent::usingDefaultTime() const
02335 {
02336     return d->mTemplateAfterTime == 0;
02337 }
02338 
02339 int KAEvent::templateAfterTime() const
02340 {
02341     return d->mTemplateAfterTime;
02342 }
02343 
02344 void KAEvent::setActions(const QString& pre, const QString& post, bool cancelOnError, bool dontShowError)
02345 {
02346     d->mPreAction = pre;
02347     d->mPostAction = post;
02348     d->mCancelOnPreActErr = cancelOnError;
02349     d->mDontShowPreActErr = dontShowError;
02350 }
02351 
02352 QString KAEvent::preAction() const
02353 {
02354     return d->mPreAction;
02355 }
02356 
02357 QString KAEvent::postAction() const
02358 {
02359     return d->mPostAction;
02360 }
02361 
02362 bool KAEvent::cancelOnPreActionError() const
02363 {
02364     return d->mCancelOnPreActErr;
02365 }
02366 
02367 bool KAEvent::dontShowPreActionError() const
02368 {
02369     return d->mDontShowPreActErr;
02370 }
02371 
02372 /******************************************************************************
02373 * Set a reminder.
02374 * 'minutes' = number of minutes BEFORE the main alarm.
02375 */
02376 void KAEvent::setReminder(int minutes, bool onceOnly)
02377 {
02378     d->setReminder(minutes, onceOnly);
02379 }
02380 
02381 void KAEvent::Private::setReminder(int minutes, bool onceOnly)
02382 {
02383     if (minutes > 0  &&  mRepeatAtLogin)
02384         minutes = 0;
02385     if (minutes != mReminderMinutes  ||  (minutes && mReminderActive != ACTIVE_REMINDER))
02386     {
02387         if (minutes  &&  mReminderActive == NO_REMINDER)
02388             ++mAlarmCount;
02389         else if (!minutes  &&  mReminderActive != NO_REMINDER)
02390             --mAlarmCount;
02391         mReminderMinutes   = minutes;
02392         mReminderActive    = minutes ? ACTIVE_REMINDER : NO_REMINDER;
02393         mReminderOnceOnly  = onceOnly;
02394         mReminderAfterTime = DateTime();
02395         mTriggerChanged = true;
02396     }
02397 }
02398 
02399 /******************************************************************************
02400 * Activate the event's reminder which occurs AFTER the given main alarm time.
02401 * Reply = true if successful (i.e. reminder falls before the next main alarm).
02402 */
02403 void KAEvent::activateReminderAfter(const DateTime& mainAlarmTime)
02404 {
02405     d->activateReminderAfter(mainAlarmTime);
02406 }
02407 
02408 void KAEvent::Private::activateReminderAfter(const DateTime& mainAlarmTime)
02409 {
02410     if (mReminderMinutes >= 0  ||  mReminderActive == ACTIVE_REMINDER  ||  !mainAlarmTime.isValid())
02411         return;
02412     // There is a reminder AFTER the main alarm.
02413     if (checkRecur() != KARecurrence::NO_RECUR)
02414     {
02415         // For a recurring alarm, the given alarm time must be a recurrence, not a sub-repetition.
02416         DateTime next;
02417         //???? For some unknown reason, addSecs(-1) returns the recurrence after the next,
02418         //???? so addSecs(-60) is used instead.
02419         if (nextRecurrence(mainAlarmTime.addSecs(-60).effectiveKDateTime(), next) == NO_OCCURRENCE
02420         ||  mainAlarmTime != next)
02421             return;
02422     }
02423     else if (!mRepeatAtLogin)
02424     {
02425         // For a non-recurring alarm, the given alarm time must be the main alarm time.
02426         if (mainAlarmTime != mStartDateTime)
02427             return;
02428     }
02429 
02430     const DateTime reminderTime = mainAlarmTime.addMins(-mReminderMinutes);
02431     DateTime next;
02432     if (nextOccurrence(mainAlarmTime.effectiveKDateTime(), next, RETURN_REPETITION) != NO_OCCURRENCE
02433     &&  reminderTime >= next)
02434         return;    // the reminder time is after the next occurrence of the main alarm
02435 
02436     kDebug() << "Setting reminder at" << reminderTime.effectiveKDateTime().dateTime();
02437     activate_reminder(true);
02438     mReminderAfterTime = reminderTime;
02439 }
02440 
02441 int KAEvent::reminderMinutes() const
02442 {
02443     return d->mReminderMinutes;
02444 }
02445 
02446 bool KAEvent::reminderActive() const
02447 {
02448     return d->mReminderActive == Private::ACTIVE_REMINDER;
02449 }
02450 
02451 bool KAEvent::reminderOnceOnly() const
02452 {
02453     return d->mReminderOnceOnly;
02454 }
02455 
02456 bool KAEvent::reminderDeferral() const
02457 {
02458     return d->mDeferral == Private::REMINDER_DEFERRAL;
02459 }
02460 
02461 /******************************************************************************
02462 * Defer the event to the specified time.
02463 * If the main alarm time has passed, the main alarm is marked as expired.
02464 * If 'adjustRecurrence' is true, ensure that the next scheduled recurrence is
02465 * after the current time.
02466 */
02467 void KAEvent::defer(const DateTime& dt, bool reminder, bool adjustRecurrence)
02468 {
02469     return d->defer(dt, reminder, adjustRecurrence);
02470 }
02471 
02472 void KAEvent::Private::defer(const DateTime& dateTime, bool reminder, bool adjustRecurrence)
02473 {
02474     startChanges();   // prevent multiple trigger time evaluation here
02475     bool setNextRepetition = false;
02476     bool checkRepetition = false;
02477     bool checkReminderAfter = false;
02478     if (checkRecur() == KARecurrence::NO_RECUR)
02479     {
02480         // Deferring a non-recurring alarm
02481         if (mReminderMinutes)
02482         {
02483             bool deferReminder = false;
02484             if (mReminderMinutes > 0)
02485             {
02486                 // There's a reminder BEFORE the main alarm
02487                 if (dateTime < mNextMainDateTime.effectiveKDateTime())
02488                     deferReminder = true;
02489                 else if (mReminderActive == ACTIVE_REMINDER  ||  mDeferral == REMINDER_DEFERRAL)
02490                 {
02491                     // Deferring past the main alarm time, so adjust any existing deferral
02492                     set_deferral(NO_DEFERRAL);
02493                     mTriggerChanged = true;
02494                 }
02495             }
02496             else if (mReminderMinutes < 0  &&  reminder)
02497                 deferReminder = true;    // deferring a reminder AFTER the main alarm
02498             if (deferReminder)
02499             {
02500                 set_deferral(REMINDER_DEFERRAL);   // defer reminder alarm
02501                 mDeferralTime = dateTime;
02502                 mTriggerChanged = true;
02503             }
02504             if (mReminderActive == ACTIVE_REMINDER)
02505             {
02506                 activate_reminder(false);
02507                 mTriggerChanged = true;
02508             }
02509         }
02510         if (mDeferral != REMINDER_DEFERRAL)
02511         {
02512             // We're deferring the main alarm.
02513             // Main alarm has now expired.
02514             mNextMainDateTime = mDeferralTime = dateTime;
02515             set_deferral(NORMAL_DEFERRAL);
02516             mTriggerChanged = true;
02517             checkReminderAfter = true;
02518             if (!mMainExpired)
02519             {
02520                 // Mark the alarm as expired now
02521                 mMainExpired = true;
02522                 --mAlarmCount;
02523                 if (mRepeatAtLogin)
02524                 {
02525                     // Remove the repeat-at-login alarm, but keep a note of it for archiving purposes
02526                     mArchiveRepeatAtLogin = true;
02527                     mRepeatAtLogin = false;
02528                     --mAlarmCount;
02529                 }
02530             }
02531         }
02532     }
02533     else if (reminder)
02534     {
02535         // Deferring a reminder for a recurring alarm
02536         if (dateTime >= mNextMainDateTime.effectiveKDateTime())
02537         {
02538             // Trying to defer it past the next main alarm (regardless of whether
02539             // the reminder triggered before or after the main alarm).
02540             set_deferral(NO_DEFERRAL);    // (error)
02541         }
02542         else
02543         {
02544             set_deferral(REMINDER_DEFERRAL);
02545             mDeferralTime = dateTime;
02546             checkRepetition = true;
02547         }
02548         mTriggerChanged = true;
02549     }
02550     else
02551     {
02552         // Deferring a recurring alarm
02553         mDeferralTime = dateTime;
02554         if (mDeferral == NO_DEFERRAL)
02555             set_deferral(NORMAL_DEFERRAL);
02556         mTriggerChanged = true;
02557         checkReminderAfter = true;
02558         if (adjustRecurrence)
02559         {
02560             const KDateTime now = KDateTime::currentUtcDateTime();
02561             if (mainEndRepeatTime() < now)
02562             {
02563                 // The last repetition (if any) of the current recurrence has already passed.
02564                 // Adjust to the next scheduled recurrence after now.
02565                 if (!mMainExpired  &&  setNextOccurrence(now) == NO_OCCURRENCE)
02566                 {
02567                     mMainExpired = true;
02568                     --mAlarmCount;
02569                 }
02570             }
02571             else
02572                 setNextRepetition = mRepetition;
02573         }
02574         else
02575             checkRepetition = true;
02576     }
02577     if (checkReminderAfter  &&  mReminderMinutes < 0  &&  mReminderActive != NO_REMINDER)
02578     {
02579         // Enable/disable the active reminder AFTER the main alarm,
02580         // depending on whether the deferral is before or after the reminder.
02581         mReminderActive = (mDeferralTime < mReminderAfterTime) ? ACTIVE_REMINDER : HIDDEN_REMINDER;
02582     }
02583     if (checkRepetition)
02584         setNextRepetition = (mRepetition  &&  mDeferralTime < mainEndRepeatTime());
02585     if (setNextRepetition)
02586     {
02587         // The alarm is repeated, and we're deferring to a time before the last repetition.
02588         // Set the next scheduled repetition to the one after the deferral.
02589         if (mNextMainDateTime >= mDeferralTime)
02590             mNextRepeat = 0;
02591         else
02592             mNextRepeat = mRepetition.nextRepeatCount(mNextMainDateTime.kDateTime(), mDeferralTime.kDateTime());
02593         mTriggerChanged = true;
02594     }
02595     endChanges();
02596 }
02597 
02598 /******************************************************************************
02599 * Cancel any deferral alarm.
02600 */
02601 void KAEvent::cancelDefer()
02602 {
02603     d->cancelDefer();
02604 }
02605 
02606 void KAEvent::Private::cancelDefer()
02607 {
02608     if (mDeferral != NO_DEFERRAL)
02609     {
02610         mDeferralTime = DateTime();
02611         set_deferral(NO_DEFERRAL);
02612         mTriggerChanged = true;
02613     }
02614 }
02615 
02616 void KAEvent::setDeferDefaultMinutes(int minutes, bool dateOnly)
02617 {
02618     d->mDeferDefaultMinutes = minutes;
02619     d->mDeferDefaultDateOnly = dateOnly;
02620 }
02621 
02622 bool KAEvent::deferred() const
02623 {
02624     return d->mDeferral > 0;
02625 }
02626 
02627 DateTime KAEvent::deferDateTime() const
02628 {
02629     return d->mDeferralTime;
02630 }
02631 
02632 /******************************************************************************
02633 * Find the latest time which the alarm can currently be deferred to.
02634 */
02635 DateTime KAEvent::deferralLimit(DeferLimitType* limitType) const
02636 {
02637     return d->deferralLimit(limitType);
02638 }
02639 
02640 DateTime KAEvent::Private::deferralLimit(DeferLimitType* limitType) const
02641 {
02642     DeferLimitType ltype = LIMIT_NONE;
02643     DateTime endTime;
02644     if (checkRecur() != KARecurrence::NO_RECUR)
02645     {
02646         // It's a recurring alarm. Find the latest time it can be deferred to:
02647         // it cannot be deferred past its next occurrence or sub-repetition,
02648         // or any advance reminder before that.
02649         DateTime reminderTime;
02650         const KDateTime now = KDateTime::currentUtcDateTime();
02651         const OccurType type = nextOccurrence(now, endTime, RETURN_REPETITION);
02652         if (type & OCCURRENCE_REPEAT)
02653             ltype = LIMIT_REPETITION;
02654         else if (type == NO_OCCURRENCE)
02655             ltype = LIMIT_NONE;
02656         else if (mReminderActive == ACTIVE_REMINDER  &&  mReminderMinutes > 0
02657              &&  (now < (reminderTime = endTime.addMins(-mReminderMinutes))))
02658         {
02659             endTime = reminderTime;
02660             ltype = LIMIT_REMINDER;
02661         }
02662         else
02663             ltype = LIMIT_RECURRENCE;
02664     }
02665     else if (mReminderMinutes < 0)
02666     {
02667         // There is a reminder alarm which occurs AFTER the main alarm.
02668         // Don't allow the reminder to be deferred past the next main alarm time.
02669         if (KDateTime::currentUtcDateTime() < mNextMainDateTime.effectiveKDateTime())
02670         {
02671             endTime = mNextMainDateTime;
02672             ltype = LIMIT_MAIN;
02673         }
02674     }
02675     else if (mReminderMinutes > 0
02676          &&  KDateTime::currentUtcDateTime() < mNextMainDateTime.effectiveKDateTime())
02677     {
02678         // It's a reminder BEFORE the main alarm.
02679         // Don't allow it to be deferred past its main alarm time.
02680         endTime = mNextMainDateTime;
02681         ltype = LIMIT_MAIN;
02682     }
02683     if (ltype != LIMIT_NONE)
02684         endTime = endTime.addMins(-1);
02685     if (limitType)
02686         *limitType = ltype;
02687     return endTime;
02688 }
02689 
02690 int KAEvent::deferDefaultMinutes() const
02691 {
02692     return d->mDeferDefaultMinutes;
02693 }
02694 
02695 bool KAEvent::deferDefaultDateOnly() const
02696 {
02697     return d->mDeferDefaultDateOnly;
02698 }
02699 
02700 DateTime KAEvent::startDateTime() const
02701 {
02702     return d->mStartDateTime;
02703 }
02704 
02705 void KAEvent::setTime(const KDateTime& dt)
02706 {
02707     d->mNextMainDateTime = dt;
02708     d->mTriggerChanged = true;
02709 }
02710 
02711 DateTime KAEvent::mainDateTime(bool withRepeats) const
02712 {
02713     return d->mainDateTime(withRepeats);
02714 }
02715 
02716 QTime KAEvent::mainTime() const
02717 {
02718     return d->mNextMainDateTime.effectiveTime();
02719 }
02720 
02721 DateTime KAEvent::mainEndRepeatTime() const
02722 {
02723     return d->mainEndRepeatTime();
02724 }
02725 
02726 /******************************************************************************
02727 * Set the start-of-day time for date-only alarms.
02728 */
02729 void KAEvent::setStartOfDay(const QTime& startOfDay)
02730 {
02731     DateTime::setStartOfDay(startOfDay);
02732 #ifdef __GNUC__
02733 #warning Does this need all trigger times for date-only alarms to be recalculated?
02734 #endif
02735 }
02736 
02737 /******************************************************************************
02738 * Called when the user changes the start-of-day time.
02739 * Adjust the start time of the recurrence to match, for each date-only event in
02740 * a list.
02741 */
02742 void KAEvent::adjustStartOfDay(const KAEvent::List& events)
02743 {
02744     for (int i = 0, end = events.count();  i < end;  ++i)
02745     {
02746         Private* const p = events[i]->d;
02747         if (p->mStartDateTime.isDateOnly()  &&  p->checkRecur() != KARecurrence::NO_RECUR)
02748             p->mRecurrence->setStartDateTime(p->mStartDateTime.effectiveKDateTime(), true);
02749     }
02750 }
02751 
02752 DateTime KAEvent::nextTrigger(TriggerType type) const
02753 {
02754     d->calcTriggerTimes();
02755     switch (type)
02756     {
02757         case ALL_TRIGGER:       return d->mAllTrigger;
02758         case MAIN_TRIGGER:      return d->mMainTrigger;
02759         case ALL_WORK_TRIGGER:  return d->mAllWorkTrigger;
02760         case WORK_TRIGGER:      return d->mMainWorkTrigger;
02761         case DISPLAY_TRIGGER:
02762         {
02763             const bool reminderAfter = d->mMainExpired && d->mReminderActive && d->mReminderMinutes < 0;
02764             return d->checkRecur() != KARecurrence::NO_RECUR  &&  (d->mWorkTimeOnly || d->mExcludeHolidays)
02765                    ? (reminderAfter ? d->mAllWorkTrigger : d->mMainWorkTrigger)
02766                    : (reminderAfter ? d->mAllTrigger : d->mMainTrigger);
02767         }
02768         default:                return DateTime();
02769     }
02770 }
02771 
02772 void KAEvent::setCreatedDateTime(const KDateTime& dt)
02773 {
02774     d->mCreatedDateTime = dt;
02775 }
02776 
02777 KDateTime KAEvent::createdDateTime() const
02778 {
02779     return d->mCreatedDateTime;
02780 }
02781 
02782 /******************************************************************************
02783 * Set or clear repeat-at-login.
02784 */
02785 void KAEvent::setRepeatAtLogin(bool rl)
02786 {
02787     d->setRepeatAtLogin(rl);
02788 }
02789 
02790 void KAEvent::Private::setRepeatAtLogin(bool rl)
02791 {
02792     if (rl  &&  !mRepeatAtLogin)
02793     {
02794         setRepeatAtLoginTrue(true);   // clear incompatible statuses
02795         ++mAlarmCount;
02796     }
02797     else if (!rl  &&  mRepeatAtLogin)
02798         --mAlarmCount;
02799     mRepeatAtLogin = rl;
02800     mTriggerChanged = true;
02801 }
02802 
02803 /******************************************************************************
02804 * Clear incompatible statuses when repeat-at-login is set.
02805 */
02806 void KAEvent::Private::setRepeatAtLoginTrue(bool clearReminder)
02807 {
02808     clearRecur();                // clear recurrences
02809     if (mReminderMinutes >= 0 && clearReminder)
02810         setReminder(0, false);   // clear pre-alarm reminder
02811     mLateCancel = 0;
02812     mAutoClose = false;
02813     mCopyToKOrganizer = false;
02814 }
02815 
02816 bool KAEvent::repeatAtLogin(bool includeArchived) const
02817 {
02818     return d->mRepeatAtLogin || (includeArchived && d->mArchiveRepeatAtLogin);
02819 }
02820 
02821 void KAEvent::setExcludeHolidays(bool ex)
02822 {
02823     d->mExcludeHolidays = ex ? Private::mHolidays : 0;
02824     // Option only affects recurring alarms
02825     d->mTriggerChanged = (d->checkRecur() != KARecurrence::NO_RECUR);
02826 }
02827 
02828 bool KAEvent::holidaysExcluded() const
02829 {
02830     return d->mExcludeHolidays;
02831 }
02832 
02833 /******************************************************************************
02834 * Set a new holiday region.
02835 * Alarms which exclude holidays record the pointer to the holiday definition
02836 * at the time their next trigger times were last calculated. The change in
02837 * holiday definition pointer will cause their next trigger times to be
02838 * recalculated.
02839 */
02840 void KAEvent::setHolidays(const HolidayRegion& h)
02841 {
02842     Private::mHolidays = &h;
02843 }
02844 
02845 void KAEvent::setWorkTimeOnly(bool wto)
02846 {
02847     d->mWorkTimeOnly = wto;
02848     // Option only affects recurring alarms
02849     d->mTriggerChanged = (d->checkRecur() != KARecurrence::NO_RECUR);
02850 }
02851 
02852 bool KAEvent::workTimeOnly() const
02853 {
02854     return d->mWorkTimeOnly;
02855 }
02856 
02857 /******************************************************************************
02858 * Check whether a date/time is during working hours and/or holidays, depending
02859 * on the flags set for the specified event.
02860 */
02861 bool KAEvent::isWorkingTime(const KDateTime& dt) const
02862 {
02863     return d->isWorkingTime(dt);
02864 }
02865 
02866 bool KAEvent::Private::isWorkingTime(const KDateTime& dt) const
02867 {
02868     if ((mWorkTimeOnly  &&  !mWorkDays.testBit(dt.date().dayOfWeek() - 1))
02869     ||  (mExcludeHolidays  &&  mHolidays  &&  mHolidays->isHoliday(dt.date())))
02870         return false;
02871     if (!mWorkTimeOnly)
02872         return true;
02873     return dt.isDateOnly()
02874        ||  (dt.time() >= mWorkDayStart  &&  dt.time() < mWorkDayEnd);
02875 }
02876 
02877 /******************************************************************************
02878 * Set new working days and times.
02879 * Increment a counter so that working-time-only alarms can detect that they
02880 * need to update their next trigger time.
02881 */
02882 void KAEvent::setWorkTime(const QBitArray& days, const QTime& start, const QTime& end)
02883 {
02884     if (days != Private::mWorkDays  ||  start != Private::mWorkDayStart  ||  end != Private::mWorkDayEnd)
02885     {
02886         Private::mWorkDays     = days;
02887         Private::mWorkDayStart = start;
02888         Private::mWorkDayEnd   = end;
02889         if (!++Private::mWorkTimeIndex)
02890             ++Private::mWorkTimeIndex;
02891     }
02892 }
02893 
02894 /******************************************************************************
02895 * Clear the event's recurrence and alarm repetition data.
02896 */
02897 void KAEvent::setNoRecur()
02898 {
02899     d->clearRecur();
02900 }
02901 
02902 void KAEvent::Private::clearRecur()
02903 {
02904     if (mRecurrence || mRepetition)
02905     {
02906         delete mRecurrence;
02907         mRecurrence = 0;
02908         mRepetition.set(0, 0);
02909         mTriggerChanged = true;
02910     }
02911     mNextRepeat = 0;
02912 }
02913 
02914 /******************************************************************************
02915 * Initialise the event's recurrence from a KCal::Recurrence.
02916 * The event's start date/time is not changed.
02917 */
02918 void KAEvent::setRecurrence(const KARecurrence& recurrence)
02919 {
02920     d->setRecurrence(recurrence);
02921 }
02922 
02923 void KAEvent::Private::setRecurrence(const KARecurrence& recurrence)
02924 {
02925     startChanges();   // prevent multiple trigger time evaluation here
02926     delete mRecurrence;
02927     if (recurrence.recurs())
02928     {
02929         mRecurrence = new KARecurrence(recurrence);
02930         mRecurrence->setStartDateTime(mStartDateTime.effectiveKDateTime(), mStartDateTime.isDateOnly());
02931         mTriggerChanged = true;
02932     }
02933     else
02934     {
02935         if (mRecurrence)
02936             mTriggerChanged = true;
02937         mRecurrence = 0;
02938     }
02939 
02940     // Adjust sub-repetition values to fit the recurrence.
02941     setRepetition(mRepetition);
02942 
02943     endChanges();
02944 }
02945 
02946 /******************************************************************************
02947 * Set the recurrence to recur at a minutes interval.
02948 * Parameters:
02949 *    freq  = how many minutes between recurrences.
02950 *    count = number of occurrences, including first and last.
02951 *          = -1 to recur indefinitely.
02952 *          = 0 to use 'end' instead.
02953 *    end   = end date/time (invalid to use 'count' instead).
02954 * Reply = false if no recurrence was set up.
02955 */
02956 bool KAEvent::setRecurMinutely(int freq, int count, const KDateTime& end)
02957 {
02958     const bool success = d->setRecur(RecurrenceRule::rMinutely, freq, count, end);
02959     d->mTriggerChanged = true;
02960     return success;
02961 }
02962 
02963 /******************************************************************************
02964 * Set the recurrence to recur daily.
02965 * Parameters:
02966 *    freq  = how many days between recurrences.
02967 *    days  = which days of the week alarms are allowed to occur on.
02968 *    count = number of occurrences, including first and last.
02969 *          = -1 to recur indefinitely.
02970 *          = 0 to use 'end' instead.
02971 *    end   = end date (invalid to use 'count' instead).
02972 * Reply = false if no recurrence was set up.
02973 */
02974 bool KAEvent::setRecurDaily(int freq, const QBitArray& days, int count, const QDate& end)
02975 {
02976     const bool success = d->setRecur(RecurrenceRule::rDaily, freq, count, end);
02977     if (success)
02978     {
02979         int n = 0;
02980         for (int i = 0;  i < 7;  ++i)
02981         {
02982             if (days.testBit(i))
02983                 ++n;
02984         }
02985         if (n < 7)
02986             d->mRecurrence->addWeeklyDays(days);
02987     }
02988     d->mTriggerChanged = true;
02989     return success;
02990 }
02991 
02992 /******************************************************************************
02993 * Set the recurrence to recur weekly, on the specified weekdays.
02994 * Parameters:
02995 *    freq  = how many weeks between recurrences.
02996 *    days  = which days of the week alarms should occur on.
02997 *    count = number of occurrences, including first and last.
02998 *          = -1 to recur indefinitely.
02999 *          = 0 to use 'end' instead.
03000 *    end   = end date (invalid to use 'count' instead).
03001 * Reply = false if no recurrence was set up.
03002 */
03003 bool KAEvent::setRecurWeekly(int freq, const QBitArray& days, int count, const QDate& end)
03004 {
03005     const bool success = d->setRecur(RecurrenceRule::rWeekly, freq, count, end);
03006     if (success)
03007         d->mRecurrence->addWeeklyDays(days);
03008     d->mTriggerChanged = true;
03009     return success;
03010 }
03011 
03012 /******************************************************************************
03013 * Set the recurrence to recur monthly, on the specified days within the month.
03014 * Parameters:
03015 *    freq  = how many months between recurrences.
03016 *    days  = which days of the month alarms should occur on.
03017 *    count = number of occurrences, including first and last.
03018 *          = -1 to recur indefinitely.
03019 *          = 0 to use 'end' instead.
03020 *    end   = end date (invalid to use 'count' instead).
03021 * Reply = false if no recurrence was set up.
03022 */
03023 bool KAEvent::setRecurMonthlyByDate(int freq, const QVector<int>& days, int count, const QDate& end)
03024 {
03025     const bool success = d->setRecur(RecurrenceRule::rMonthly, freq, count, end);
03026     if (success)
03027     {
03028         for (int i = 0, end = days.count();  i < end;  ++i)
03029             d->mRecurrence->addMonthlyDate(days[i]);
03030     }
03031     d->mTriggerChanged = true;
03032     return success;
03033 }
03034 
03035 /******************************************************************************
03036 * Set the recurrence to recur monthly, on the specified weekdays in the
03037 * specified weeks of the month.
03038 * Parameters:
03039 *    freq  = how many months between recurrences.
03040 *    posns = which days of the week/weeks of the month alarms should occur on.
03041 *    count = number of occurrences, including first and last.
03042 *          = -1 to recur indefinitely.
03043 *          = 0 to use 'end' instead.
03044 *    end   = end date (invalid to use 'count' instead).
03045 * Reply = false if no recurrence was set up.
03046 */
03047 bool KAEvent::setRecurMonthlyByPos(int freq, const QVector<MonthPos>& posns, int count, const QDate& end)
03048 {
03049     const bool success = d->setRecur(RecurrenceRule::rMonthly, freq, count, end);
03050     if (success)
03051     {
03052         for (int i = 0, end = posns.count();  i < end;  ++i)
03053             d->mRecurrence->addMonthlyPos(posns[i].weeknum, posns[i].days);
03054     }
03055     d->mTriggerChanged = true;
03056     return success;
03057 }
03058 
03059 /******************************************************************************
03060 * Set the recurrence to recur annually, on the specified start date in each
03061 * of the specified months.
03062 * Parameters:
03063 *    freq   = how many years between recurrences.
03064 *    months = which months of the year alarms should occur on.
03065 *    day    = day of month, or 0 to use start date
03066 *    feb29  = when February 29th should recur in non-leap years.
03067 *    count  = number of occurrences, including first and last.
03068 *           = -1 to recur indefinitely.
03069 *           = 0 to use 'end' instead.
03070 *    end    = end date (invalid to use 'count' instead).
03071 * Reply = false if no recurrence was set up.
03072 */
03073 bool KAEvent::setRecurAnnualByDate(int freq, const QVector<int>& months, int day, KARecurrence::Feb29Type feb29, int count, const QDate& end)
03074 {
03075     const bool success = d->setRecur(RecurrenceRule::rYearly, freq, count, end, feb29);
03076     if (success)
03077     {
03078         for (int i = 0, end = months.count();  i < end;  ++i)
03079             d->mRecurrence->addYearlyMonth(months[i]);
03080         if (day)
03081             d->mRecurrence->addMonthlyDate(day);
03082     }
03083     d->mTriggerChanged = true;
03084     return success;
03085 }
03086 
03087 /******************************************************************************
03088 * Set the recurrence to recur annually, on the specified weekdays in the
03089 * specified weeks of the specified months.
03090 * Parameters:
03091 *    freq   = how many years between recurrences.
03092 *    posns  = which days of the week/weeks of the month alarms should occur on.
03093 *    months = which months of the year alarms should occur on.
03094 *    count  = number of occurrences, including first and last.
03095 *           = -1 to recur indefinitely.
03096 *           = 0 to use 'end' instead.
03097 *    end    = end date (invalid to use 'count' instead).
03098 * Reply = false if no recurrence was set up.
03099 */
03100 bool KAEvent::setRecurAnnualByPos(int freq, const QVector<MonthPos>& posns, const QVector<int>& months, int count, const QDate& end)
03101 {
03102     const bool success = d->setRecur(RecurrenceRule::rYearly, freq, count, end);
03103     if (success)
03104     {
03105         int i = 0;
03106         int iend;
03107         for (iend = months.count();  i < iend;  ++i)
03108             d->mRecurrence->addYearlyMonth(months[i]);
03109         for (i = 0, iend = posns.count();  i < iend;  ++i)
03110             d->mRecurrence->addYearlyPos(posns[i].weeknum, posns[i].days);
03111     }
03112     d->mTriggerChanged = true;
03113     return success;
03114 }
03115 
03116 /******************************************************************************
03117 * Initialise the event's recurrence data.
03118 * Parameters:
03119 *    freq  = how many intervals between recurrences.
03120 *    count = number of occurrences, including first and last.
03121 *          = -1 to recur indefinitely.
03122 *          = 0 to use 'end' instead.
03123 *    end   = end date/time (invalid to use 'count' instead).
03124 * Reply = false if no recurrence was set up.
03125 */
03126 bool KAEvent::Private::setRecur(RecurrenceRule::PeriodType recurType, int freq, int count, const QDate& end, KARecurrence::Feb29Type feb29)
03127 {
03128     KDateTime edt = mNextMainDateTime.kDateTime();
03129     edt.setDate(end);
03130     return setRecur(recurType, freq, count, edt, feb29);
03131 }
03132 bool KAEvent::Private::setRecur(RecurrenceRule::PeriodType recurType, int freq, int count, const KDateTime& end, KARecurrence::Feb29Type feb29)
03133 {
03134     if (count >= -1  &&  (count || end.date().isValid()))
03135     {
03136         if (!mRecurrence)
03137             mRecurrence = new KARecurrence;
03138         if (mRecurrence->init(recurType, freq, count, mNextMainDateTime.kDateTime(), end, feb29))
03139             return true;
03140     }
03141     clearRecur();
03142     return false;
03143 }
03144 
03145 bool KAEvent::recurs() const
03146 {
03147     return d->checkRecur() != KARecurrence::NO_RECUR;
03148 }
03149 
03150 KARecurrence::Type KAEvent::recurType() const
03151 {
03152     return d->checkRecur();
03153 }
03154 
03155 KARecurrence* KAEvent::recurrence() const
03156 {
03157     return d->mRecurrence;
03158 }
03159 
03160 /******************************************************************************
03161 * Return the recurrence interval in units of the recurrence period type.
03162 */
03163 int KAEvent::recurInterval() const
03164 {
03165     if (d->mRecurrence)
03166     {
03167         switch (d->mRecurrence->type())
03168         {
03169             case KARecurrence::MINUTELY:
03170             case KARecurrence::DAILY:
03171             case KARecurrence::WEEKLY:
03172             case KARecurrence::MONTHLY_DAY:
03173             case KARecurrence::MONTHLY_POS:
03174             case KARecurrence::ANNUAL_DATE:
03175             case KARecurrence::ANNUAL_POS:
03176                 return d->mRecurrence->frequency();
03177             default:
03178                 break;
03179         }
03180     }
03181     return 0;
03182 }
03183 
03184 Duration KAEvent::longestRecurrenceInterval() const
03185 {
03186     return d->mRecurrence ? d->mRecurrence->longestInterval() : Duration(0);
03187 }
03188 
03189 /******************************************************************************
03190 * Adjust the event date/time to the first recurrence of the event, on or after
03191 * start date/time. The event start date may not be a recurrence date, in which
03192 * case a later date will be set.
03193 */
03194 void KAEvent::setFirstRecurrence()
03195 {
03196     d->setFirstRecurrence();
03197 }
03198 
03199 void KAEvent::Private::setFirstRecurrence()
03200 {
03201     switch (checkRecur())
03202     {
03203         case KARecurrence::NO_RECUR:
03204         case KARecurrence::MINUTELY:
03205             return;
03206         case KARecurrence::ANNUAL_DATE:
03207         case KARecurrence::ANNUAL_POS:
03208             if (mRecurrence->yearMonths().isEmpty())
03209                 return;    // (presumably it's a template)
03210             break;
03211         case KARecurrence::DAILY:
03212         case KARecurrence::WEEKLY:
03213         case KARecurrence::MONTHLY_POS:
03214         case KARecurrence::MONTHLY_DAY:
03215             break;
03216     }
03217     const KDateTime recurStart = mRecurrence->startDateTime();
03218     if (mRecurrence->recursOn(recurStart.date(), recurStart.timeSpec()))
03219         return;           // it already recurs on the start date
03220 
03221     // Set the frequency to 1 to find the first possible occurrence
03222     const int frequency = mRecurrence->frequency();
03223     mRecurrence->setFrequency(1);
03224     DateTime next;
03225     nextRecurrence(mNextMainDateTime.effectiveKDateTime(), next);
03226     if (!next.isValid())
03227         mRecurrence->setStartDateTime(recurStart, mStartDateTime.isDateOnly());   // reinstate the old value
03228     else
03229     {
03230         mRecurrence->setStartDateTime(next.effectiveKDateTime(), next.isDateOnly());
03231         mStartDateTime = mNextMainDateTime = next;
03232         mTriggerChanged = true;
03233     }
03234     mRecurrence->setFrequency(frequency);    // restore the frequency
03235 }
03236 
03237 /******************************************************************************
03238 * Return the recurrence interval as text suitable for display.
03239 */
03240 QString KAEvent::recurrenceText(bool brief) const
03241 {
03242     if (d->mRepeatAtLogin)
03243         return brief ? i18nc("@info/plain Brief form of 'At Login'", "Login") : i18nc("@info/plain", "At login");
03244     if (d->mRecurrence)
03245     {
03246         const int frequency = d->mRecurrence->frequency();
03247         switch (d->mRecurrence->defaultRRuleConst()->recurrenceType())
03248         {
03249             case RecurrenceRule::rMinutely:
03250                 if (frequency < 60)
03251                     return i18ncp("@info/plain", "1 Minute", "%1 Minutes", frequency);
03252                 else if (frequency % 60 == 0)
03253                     return i18ncp("@info/plain", "1 Hour", "%1 Hours", frequency/60);
03254                 else
03255                 {
03256                     QString mins;
03257                     return i18nc("@info/plain Hours and minutes", "%1h %2m", frequency/60, mins.sprintf("%02d", frequency%60));
03258                 }
03259             case RecurrenceRule::rDaily:
03260                 return i18ncp("@info/plain", "1 Day", "%1 Days", frequency);
03261             case RecurrenceRule::rWeekly:
03262                 return i18ncp("@info/plain", "1 Week", "%1 Weeks", frequency);
03263             case RecurrenceRule::rMonthly:
03264                 return i18ncp("@info/plain", "1 Month", "%1 Months", frequency);
03265             case RecurrenceRule::rYearly:
03266                 return i18ncp("@info/plain", "1 Year", "%1 Years", frequency);
03267             case RecurrenceRule::rNone:
03268             default:
03269                 break;
03270         }
03271     }
03272     return brief ? QString() : i18nc("@info/plain No recurrence", "None");
03273 }
03274 
03275 /******************************************************************************
03276 * Initialise the event's sub-repetition.
03277 * The repetition length is adjusted if necessary to fit the recurrence interval.
03278 * If the event doesn't recur, the sub-repetition is cleared.
03279 * Reply = false if a non-daily interval was specified for a date-only recurrence.
03280 */
03281 bool KAEvent::setRepetition(const Repetition& r)
03282 {
03283     return d->setRepetition(r);
03284 }
03285 
03286 bool KAEvent::Private::setRepetition(const Repetition& repetition)
03287 {
03288     // Don't set mRepetition to zero at the start of this function, in case the
03289     // 'repetition' parameter passed in is a reference to mRepetition.
03290     mNextRepeat = 0;
03291     if (repetition  &&  !mRepeatAtLogin)
03292     {
03293         Q_ASSERT(checkRecur() != KARecurrence::NO_RECUR);
03294         if (!repetition.isDaily()  &&  mStartDateTime.isDateOnly())
03295         {
03296             mRepetition.set(0, 0);
03297             return false;    // interval must be in units of days for date-only alarms
03298         }
03299         Duration longestInterval = mRecurrence->longestInterval();
03300         if (repetition.duration() >= longestInterval)
03301         {
03302             const int count = mStartDateTime.isDateOnly()
03303                               ? (longestInterval.asDays() - 1) / repetition.intervalDays()
03304                               : (longestInterval.asSeconds() - 1) / repetition.intervalSeconds();
03305             mRepetition.set(repetition.interval(), count);
03306         }
03307         else
03308             mRepetition = repetition;
03309         mTriggerChanged = true;
03310     }
03311     else if (mRepetition)
03312     {
03313         mRepetition.set(0, 0);
03314         mTriggerChanged = true;
03315     }
03316     return true;
03317 }
03318 
03319 Repetition KAEvent::repetition() const
03320 {
03321     return d->mRepetition;
03322 }
03323 
03324 int KAEvent::nextRepetition() const
03325 {
03326     return d->mNextRepeat;
03327 }
03328 
03329 /******************************************************************************
03330 * Return the repetition interval as text suitable for display.
03331 */
03332 QString KAEvent::repetitionText(bool brief) const
03333 {
03334     if (d->mRepetition)
03335     {
03336         if (!d->mRepetition.isDaily())
03337         {
03338             const int minutes = d->mRepetition.intervalMinutes();
03339             if (minutes < 60)
03340                 return i18ncp("@info/plain", "1 Minute", "%1 Minutes", minutes);
03341             if (minutes % 60 == 0)
03342                 return i18ncp("@info/plain", "1 Hour", "%1 Hours", minutes/60);
03343             QString mins;
03344             return i18nc("@info/plain Hours and minutes", "%1h %2m", minutes/60, mins.sprintf("%02d", minutes%60));
03345         }
03346         const int days = d->mRepetition.intervalDays();
03347         if (days % 7)
03348             return i18ncp("@info/plain", "1 Day", "%1 Days", days);
03349         return i18ncp("@info/plain", "1 Week", "%1 Weeks", days / 7);
03350     }
03351     return brief ? QString() : i18nc("@info/plain No repetition", "None");
03352 }
03353 
03354 /******************************************************************************
03355 * Determine whether the event will occur after the specified date/time.
03356 * If 'includeRepetitions' is true and the alarm has a sub-repetition, it
03357 * returns true if any repetitions occur after the specified date/time.
03358 */
03359 bool KAEvent::occursAfter(const KDateTime& preDateTime, bool includeRepetitions) const
03360 {
03361     return d->occursAfter(preDateTime, includeRepetitions);
03362 }
03363 
03364 bool KAEvent::Private::occursAfter(const KDateTime& preDateTime, bool includeRepetitions) const
03365 {
03366     KDateTime dt;
03367     if (checkRecur() != KARecurrence::NO_RECUR)
03368     {
03369         if (mRecurrence->duration() < 0)
03370             return true;    // infinite recurrence
03371         dt = mRecurrence->endDateTime();
03372     }
03373     else
03374         dt = mNextMainDateTime.effectiveKDateTime();
03375     if (mStartDateTime.isDateOnly())
03376     {
03377         QDate pre = preDateTime.date();
03378         if (preDateTime.toTimeSpec(mStartDateTime.timeSpec()).time() < DateTime::startOfDay())
03379             pre = pre.addDays(-1);    // today's recurrence (if today recurs) is still to come
03380         if (pre < dt.date())
03381             return true;
03382     }
03383     else if (preDateTime < dt)
03384         return true;
03385 
03386     if (includeRepetitions  &&  mRepetition)
03387     {
03388         if (preDateTime < mRepetition.duration().end(dt))
03389             return true;
03390     }
03391     return false;
03392 }
03393 
03394 /******************************************************************************
03395 * Set the date/time of the event to the next scheduled occurrence after the
03396 * specified date/time, provided that this is later than its current date/time.
03397 * Any reminder alarm is adjusted accordingly.
03398 * If the alarm has a sub-repetition, and a repetition of a previous recurrence
03399 * occurs after the specified date/time, that repetition is set as the next
03400 * occurrence.
03401 */
03402 KAEvent::OccurType KAEvent::setNextOccurrence(const KDateTime& preDateTime)
03403 {
03404     return d->setNextOccurrence(preDateTime);
03405 }
03406 
03407 KAEvent::OccurType KAEvent::Private::setNextOccurrence(const KDateTime& preDateTime)
03408 {
03409     if (preDateTime < mNextMainDateTime.effectiveKDateTime())
03410         return FIRST_OR_ONLY_OCCURRENCE;    // it might not be the first recurrence - tant pis
03411     KDateTime pre = preDateTime;
03412     // If there are repetitions, adjust the comparison date/time so that
03413     // we find the earliest recurrence which has a repetition falling after
03414     // the specified preDateTime.
03415     if (mRepetition)
03416         pre = mRepetition.duration(-mRepetition.count()).end(preDateTime);
03417 
03418     DateTime afterPre;          // next recurrence after 'pre'
03419     OccurType type;
03420     if (pre < mNextMainDateTime.effectiveKDateTime())
03421     {
03422         afterPre = mNextMainDateTime;
03423         type = FIRST_OR_ONLY_OCCURRENCE;   // may not actually be the first occurrence
03424     }
03425     else if (checkRecur() != KARecurrence::NO_RECUR)
03426     {
03427         type = nextRecurrence(pre, afterPre);
03428         if (type == NO_OCCURRENCE)
03429             return NO_OCCURRENCE;
03430         if (type != FIRST_OR_ONLY_OCCURRENCE  &&  afterPre != mNextMainDateTime)
03431         {
03432             // Need to reschedule the next trigger date/time
03433             mNextMainDateTime = afterPre;
03434             if (mReminderMinutes > 0  &&  (mDeferral == REMINDER_DEFERRAL || mReminderActive != ACTIVE_REMINDER))
03435             {
03436                 // Reinstate the advance reminder for the rescheduled recurrence.
03437                 // Note that a reminder AFTER the main alarm will be left active.
03438                 activate_reminder(!mReminderOnceOnly);
03439             }
03440             if (mDeferral == REMINDER_DEFERRAL)
03441                 set_deferral(NO_DEFERRAL);
03442             mTriggerChanged = true;
03443         }
03444     }
03445     else
03446         return NO_OCCURRENCE;
03447 
03448     if (mRepetition)
03449     {
03450         if (afterPre <= preDateTime)
03451         {
03452             // The next occurrence is a sub-repetition.
03453             type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
03454             mNextRepeat = mRepetition.nextRepeatCount(afterPre.effectiveKDateTime(), preDateTime);
03455             // Repetitions can't have a reminder, so remove any.
03456             activate_reminder(false);
03457             if (mDeferral == REMINDER_DEFERRAL)
03458                 set_deferral(NO_DEFERRAL);
03459             mTriggerChanged = true;
03460         }
03461         else if (mNextRepeat)
03462         {
03463             // The next occurrence is the main occurrence, not a repetition
03464             mNextRepeat = 0;
03465             mTriggerChanged = true;
03466         }
03467     }
03468     return type;
03469 }
03470 
03471 /******************************************************************************
03472 * Get the date/time of the next occurrence of the event, after the specified
03473 * date/time.
03474 * 'result' = date/time of next occurrence, or invalid date/time if none.
03475 */
03476 KAEvent::OccurType KAEvent::nextOccurrence(const KDateTime& preDateTime, DateTime& result, OccurOption o) const
03477 {
03478     return d->nextOccurrence(preDateTime, result, o);
03479 }
03480 
03481 KAEvent::OccurType KAEvent::Private::nextOccurrence(const KDateTime& preDateTime, DateTime& result,
03482                                                     OccurOption includeRepetitions) const
03483 {
03484     KDateTime pre = preDateTime;
03485     if (includeRepetitions != IGNORE_REPETITION)
03486     {                   // RETURN_REPETITION or ALLOW_FOR_REPETITION
03487         if (!mRepetition)
03488             includeRepetitions = IGNORE_REPETITION;
03489         else
03490             pre = mRepetition.duration(-mRepetition.count()).end(preDateTime);
03491     }
03492 
03493     OccurType type;
03494     const bool recurs = (checkRecur() != KARecurrence::NO_RECUR);
03495     if (recurs)
03496         type = nextRecurrence(pre, result);
03497     else if (pre < mNextMainDateTime.effectiveKDateTime())
03498     {
03499         result = mNextMainDateTime;
03500         type = FIRST_OR_ONLY_OCCURRENCE;
03501     }
03502     else
03503     {
03504         result = DateTime();
03505         type = NO_OCCURRENCE;
03506     }
03507 
03508     if (type != NO_OCCURRENCE  &&  result <= preDateTime  &&  includeRepetitions != IGNORE_REPETITION)
03509     {                   // RETURN_REPETITION or ALLOW_FOR_REPETITION
03510         // The next occurrence is a sub-repetition
03511         int repetition = mRepetition.nextRepeatCount(result.kDateTime(), preDateTime);
03512         const DateTime repeatDT = mRepetition.duration(repetition).end(result.kDateTime());
03513         if (recurs)
03514         {
03515             // We've found a recurrence before the specified date/time, which has
03516             // a sub-repetition after the date/time.
03517             // However, if the intervals between recurrences vary, we could possibly
03518             // have missed a later recurrence which fits the criterion, so check again.
03519             DateTime dt;
03520             const OccurType newType = previousOccurrence(repeatDT.effectiveKDateTime(), dt, false);
03521             if (dt > result)
03522             {
03523                 type = newType;
03524                 result = dt;
03525                 if (includeRepetitions == RETURN_REPETITION  &&  result <= preDateTime)
03526                 {
03527                     // The next occurrence is a sub-repetition
03528                     repetition = mRepetition.nextRepeatCount(result.kDateTime(), preDateTime);
03529                     result = mRepetition.duration(repetition).end(result.kDateTime());
03530                     type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
03531                 }
03532                 return type;
03533             }
03534         }
03535         if (includeRepetitions == RETURN_REPETITION)
03536         {
03537             // The next occurrence is a sub-repetition
03538             result = repeatDT;
03539             type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
03540         }
03541     }
03542     return type;
03543 }
03544 
03545 /******************************************************************************
03546 * Get the date/time of the last previous occurrence of the event, before the
03547 * specified date/time.
03548 * If 'includeRepetitions' is true and the alarm has a sub-repetition, the
03549 * last previous repetition is returned if appropriate.
03550 * 'result' = date/time of previous occurrence, or invalid date/time if none.
03551 */
03552 KAEvent::OccurType KAEvent::previousOccurrence(const KDateTime& afterDateTime, DateTime& result, bool includeRepetitions) const
03553 {
03554     return d->previousOccurrence(afterDateTime, result, includeRepetitions);
03555 }
03556 
03557 KAEvent::OccurType KAEvent::Private::previousOccurrence(const KDateTime& afterDateTime, DateTime& result,
03558                                                         bool includeRepetitions) const
03559 {
03560     Q_ASSERT(!afterDateTime.isDateOnly());
03561     if (mStartDateTime >= afterDateTime)
03562     {
03563         result = KDateTime();
03564         return NO_OCCURRENCE;     // the event starts after the specified date/time
03565     }
03566 
03567     // Find the latest recurrence of the event
03568     OccurType type;
03569     if (checkRecur() == KARecurrence::NO_RECUR)
03570     {
03571         result = mStartDateTime;
03572         type = FIRST_OR_ONLY_OCCURRENCE;
03573     }
03574     else
03575     {
03576         const KDateTime recurStart = mRecurrence->startDateTime();
03577         KDateTime after = afterDateTime.toTimeSpec(mStartDateTime.timeSpec());
03578         if (mStartDateTime.isDateOnly()  &&  afterDateTime.time() > DateTime::startOfDay())
03579             after = after.addDays(1);    // today's recurrence (if today recurs) has passed
03580         const KDateTime dt = mRecurrence->getPreviousDateTime(after);
03581         result = dt;
03582         result.setDateOnly(mStartDateTime.isDateOnly());
03583         if (!dt.isValid())
03584             return NO_OCCURRENCE;
03585         if (dt == recurStart)
03586             type = FIRST_OR_ONLY_OCCURRENCE;
03587         else if (mRecurrence->getNextDateTime(dt).isValid())
03588             type = result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
03589         else
03590             type = LAST_RECURRENCE;
03591     }
03592 
03593     if (includeRepetitions  &&  mRepetition)
03594     {
03595         // Find the latest repetition which is before the specified time.
03596         const int repetition = mRepetition.previousRepeatCount(result.effectiveKDateTime(), afterDateTime);
03597         if (repetition > 0)
03598         {
03599             result = mRepetition.duration(qMin(repetition, mRepetition.count())).end(result.kDateTime());
03600             return static_cast<OccurType>(type | OCCURRENCE_REPEAT);
03601         }
03602     }
03603     return type;
03604 }
03605 
03606 /******************************************************************************
03607 * Set the event to be a copy of the specified event, making the specified
03608 * alarm the 'displaying' alarm.
03609 * The purpose of setting up a 'displaying' alarm is to be able to reinstate
03610 * the alarm message in case of a crash, or to reinstate it should the user
03611 * choose to defer the alarm. Note that even repeat-at-login alarms need to be
03612 * saved in case their end time expires before the next login.
03613 * Reply = true if successful, false if alarm was not copied.
03614 */
03615 #ifndef USE_KRESOURCES
03616 bool KAEvent::setDisplaying(const KAEvent& e, KAAlarm::Type t, Akonadi::Collection::Id id, const KDateTime& dt, bool showEdit, bool showDefer)
03617 #else
03618 bool KAEvent::setDisplaying(const KAEvent& e, KAAlarm::Type t, const QString& id, const KDateTime& dt, bool showEdit, bool showDefer)
03619 #endif
03620 {
03621     return d->setDisplaying(*e.d, t, id, dt, showEdit, showDefer);
03622 }
03623 
03624 #ifndef USE_KRESOURCES
03625 bool KAEvent::Private::setDisplaying(const KAEvent::Private& event, KAAlarm::Type alarmType, Akonadi::Collection::Id collectionId,
03626                                      const KDateTime& repeatAtLoginTime, bool showEdit, bool showDefer)
03627 #else
03628 bool KAEvent::Private::setDisplaying(const KAEvent::Private& event, KAAlarm::Type alarmType, const QString& resourceID,
03629                                      const KDateTime& repeatAtLoginTime, bool showEdit, bool showDefer)
03630 #endif
03631 {
03632     if (!mDisplaying
03633     &&  (alarmType == KAAlarm::MAIN_ALARM
03634       || alarmType == KAAlarm::REMINDER_ALARM
03635       || alarmType == KAAlarm::DEFERRED_REMINDER_ALARM
03636       || alarmType == KAAlarm::DEFERRED_ALARM
03637       || alarmType == KAAlarm::AT_LOGIN_ALARM))
03638     {
03639 //kDebug()<<event.id()<<","<<(alarmType==KAAlarm::MAIN_ALARM?"MAIN":alarmType==KAAlarm::REMINDER_ALARM?"REMINDER":alarmType==KAAlarm::DEFERRED_REMINDER_ALARM?"REMINDER_DEFERRAL":alarmType==KAAlarm::DEFERRED_ALARM?"DEFERRAL":"LOGIN")<<"): time="<<repeatAtLoginTime.toString();
03640         KAAlarm al = event.alarm(alarmType);
03641         if (al.isValid())
03642         {
03643             *this = event;
03644             // Change the event ID to avoid duplicating the same unique ID as the original event
03645             setCategory(CalEvent::DISPLAYING);
03646 #ifndef USE_KRESOURCES
03647             mItemId               = -1;    // the display event doesn't have an associated Item
03648             mOriginalCollectionId = collectionId;;
03649 #else
03650             mOriginalResourceId   = resourceID;
03651 #endif
03652             mDisplayingDefer      = showDefer;
03653             mDisplayingEdit       = showEdit;
03654             mDisplaying           = true;
03655             mDisplayingTime       = (alarmType == KAAlarm::AT_LOGIN_ALARM) ? repeatAtLoginTime : al.dateTime().kDateTime();
03656             switch (al.type())
03657             {
03658                 case KAAlarm::AT_LOGIN_ALARM:           mDisplayingFlags = REPEAT_AT_LOGIN;  break;
03659                 case KAAlarm::REMINDER_ALARM:           mDisplayingFlags = REMINDER;  break;
03660                 case KAAlarm::DEFERRED_REMINDER_ALARM:  mDisplayingFlags = al.timedDeferral() ? (REMINDER | TIME_DEFERRAL) : (REMINDER | DATE_DEFERRAL);  break;
03661                 case KAAlarm::DEFERRED_ALARM:           mDisplayingFlags = al.timedDeferral() ? TIME_DEFERRAL : DATE_DEFERRAL;  break;
03662                 default:                                mDisplayingFlags = 0;  break;
03663             }
03664             ++mAlarmCount;
03665             return true;
03666         }
03667     }
03668     return false;
03669 }
03670 
03671 /******************************************************************************
03672 * Reinstate the original event from the 'displaying' event.
03673 */
03674 #ifndef USE_KRESOURCES
03675 void KAEvent::reinstateFromDisplaying(const KCalCore::Event::Ptr& e, Akonadi::Collection::Id& id, bool& showEdit, bool& showDefer)
03676 #else
03677 void KAEvent::reinstateFromDisplaying(const KCal::Event* e, QString& id, bool& showEdit, bool& showDefer)
03678 #endif
03679 {
03680     d->reinstateFromDisplaying(e, id, showEdit, showDefer);
03681 }
03682 
03683 #ifndef USE_KRESOURCES
03684 void KAEvent::Private::reinstateFromDisplaying(const Event::Ptr& kcalEvent, Akonadi::Collection::Id& collectionId, bool& showEdit, bool& showDefer)
03685 #else
03686 void KAEvent::Private::reinstateFromDisplaying(const Event* kcalEvent, QString& resourceID, bool& showEdit, bool& showDefer)
03687 #endif
03688 {
03689     set(kcalEvent);
03690     if (mDisplaying)
03691     {
03692         // Retrieve the original event's unique ID
03693         setCategory(CalEvent::ACTIVE);
03694 #ifndef USE_KRESOURCES
03695         collectionId = mOriginalCollectionId;
03696         mOriginalCollectionId = -1;
03697 #else
03698         resourceID   = mOriginalResourceId;
03699         mOriginalResourceId.clear();
03700 #endif
03701         showDefer    = mDisplayingDefer;
03702         showEdit     = mDisplayingEdit;
03703         mDisplaying  = false;
03704         --mAlarmCount;
03705     }
03706 }
03707 
03708 /******************************************************************************
03709 * Return the original alarm which the displaying alarm refers to.
03710 * Note that the caller is responsible for ensuring that the event was a
03711 * displaying event, since this is normally called after
03712 * reinstateFromDisplaying(), which clears mDisplaying.
03713 */
03714 KAAlarm KAEvent::convertDisplayingAlarm() const
03715 {
03716     KAAlarm al = alarm(KAAlarm::DISPLAYING_ALARM);
03717     KAAlarm::Private* const al_d = al.d;
03718     const int displayingFlags = d->mDisplayingFlags;
03719     if (displayingFlags & REPEAT_AT_LOGIN)
03720     {
03721         al_d->mRepeatAtLogin = true;
03722         al_d->mType = KAAlarm::AT_LOGIN_ALARM;
03723     }
03724     else if (displayingFlags & Private::DEFERRAL)
03725     {
03726         al_d->mDeferred = true;
03727         al_d->mTimedDeferral = (displayingFlags & Private::TIMED_FLAG);
03728         al_d->mType = (displayingFlags & Private::REMINDER) ? KAAlarm::DEFERRED_REMINDER_ALARM : KAAlarm::DEFERRED_ALARM;
03729     }
03730     else if (displayingFlags & Private::REMINDER)
03731         al_d->mType = KAAlarm::REMINDER_ALARM;
03732     else
03733         al_d->mType = KAAlarm::MAIN_ALARM;
03734     return al;
03735 }
03736 
03737 bool KAEvent::displaying() const
03738 {
03739     return d->mDisplaying;
03740 }
03741 
03742 /******************************************************************************
03743 * Return the alarm of the specified type.
03744 */
03745 KAAlarm KAEvent::alarm(KAAlarm::Type t) const
03746 {
03747     return d->alarm(t);
03748 }
03749 
03750 KAAlarm KAEvent::Private::alarm(KAAlarm::Type type) const
03751 {
03752     checkRecur();     // ensure recurrence/repetition data is consistent
03753     KAAlarm al;       // this sets type to INVALID_ALARM
03754     KAAlarm::Private* const al_d = al.d;
03755     if (mAlarmCount)
03756     {
03757         al_d->mActionType    = (KAAlarm::Action)mActionSubType;
03758         al_d->mRepeatAtLogin = false;
03759         al_d->mDeferred      = false;
03760         switch (type)
03761         {
03762             case KAAlarm::MAIN_ALARM:
03763                 if (!mMainExpired)
03764                 {
03765                     al_d->mType             = KAAlarm::MAIN_ALARM;
03766                     al_d->mNextMainDateTime = mNextMainDateTime;
03767                     al_d->mRepetition       = mRepetition;
03768                     al_d->mNextRepeat       = mNextRepeat;
03769                 }
03770                 break;
03771             case KAAlarm::REMINDER_ALARM:
03772                 if (mReminderActive == ACTIVE_REMINDER)
03773                 {
03774                     al_d->mType = KAAlarm::REMINDER_ALARM;
03775                     if (mReminderMinutes < 0)
03776                         al_d->mNextMainDateTime = mReminderAfterTime;
03777                     else if (mReminderOnceOnly)
03778                         al_d->mNextMainDateTime = mStartDateTime.addMins(-mReminderMinutes);
03779                     else
03780                         al_d->mNextMainDateTime = mNextMainDateTime.addMins(-mReminderMinutes);
03781                 }
03782                 break;
03783             case KAAlarm::DEFERRED_REMINDER_ALARM:
03784                 if (mDeferral != REMINDER_DEFERRAL)
03785                     break;
03786                 // fall through to DEFERRED_ALARM
03787             case KAAlarm::DEFERRED_ALARM:
03788                 if (mDeferral != NO_DEFERRAL)
03789                 {
03790                     al_d->mType             = (mDeferral == REMINDER_DEFERRAL) ? KAAlarm::DEFERRED_REMINDER_ALARM : KAAlarm::DEFERRED_ALARM;
03791                     al_d->mNextMainDateTime = mDeferralTime;
03792                     al_d->mDeferred         = true;
03793                     al_d->mTimedDeferral    = !mDeferralTime.isDateOnly();
03794                 }
03795                 break;
03796             case KAAlarm::AT_LOGIN_ALARM:
03797                 if (mRepeatAtLogin)
03798                 {
03799                     al_d->mType             = KAAlarm::AT_LOGIN_ALARM;
03800                     al_d->mNextMainDateTime = mAtLoginDateTime;
03801                     al_d->mRepeatAtLogin    = true;
03802                 }
03803                 break;
03804             case KAAlarm::DISPLAYING_ALARM:
03805                 if (mDisplaying)
03806                 {
03807                     al_d->mType             = KAAlarm::DISPLAYING_ALARM;
03808                     al_d->mNextMainDateTime = mDisplayingTime;
03809                 }
03810                 break;
03811             case KAAlarm::INVALID_ALARM:
03812             default:
03813                 break;
03814         }
03815     }
03816     return al;
03817 }
03818 
03819 /******************************************************************************
03820 * Return the main alarm for the event.
03821 * If the main alarm does not exist, one of the subsidiary ones is returned if
03822 * possible.
03823 * N.B. a repeat-at-login alarm can only be returned if it has been read from/
03824 * written to the calendar file.
03825 */
03826 KAAlarm KAEvent::firstAlarm() const
03827 {
03828     return d->firstAlarm();
03829 }
03830 
03831 KAAlarm KAEvent::Private::firstAlarm() const
03832 {
03833     if (mAlarmCount)
03834     {
03835         if (!mMainExpired)
03836             return alarm(KAAlarm::MAIN_ALARM);
03837         return nextAlarm(KAAlarm::MAIN_ALARM);
03838     }
03839     return KAAlarm();
03840 }
03841 
03842 /******************************************************************************
03843 * Return the next alarm for the event, after the specified alarm.
03844 * N.B. a repeat-at-login alarm can only be returned if it has been read from/
03845 * written to the calendar file.
03846 */
03847 KAAlarm KAEvent::nextAlarm(const KAAlarm& previousAlarm) const
03848 {
03849     return d->nextAlarm(previousAlarm.type());
03850 }
03851 
03852 KAAlarm KAEvent::nextAlarm(KAAlarm::Type previousType) const
03853 {
03854     return d->nextAlarm(previousType);
03855 }
03856 
03857 KAAlarm KAEvent::Private::nextAlarm(KAAlarm::Type previousType) const
03858 {
03859     switch (previousType)
03860     {
03861         case KAAlarm::MAIN_ALARM:
03862             if (mReminderActive == ACTIVE_REMINDER)
03863                 return alarm(KAAlarm::REMINDER_ALARM);
03864             // fall through to REMINDER_ALARM
03865         case KAAlarm::REMINDER_ALARM:
03866             // There can only be one deferral alarm
03867             if (mDeferral == REMINDER_DEFERRAL)
03868                 return alarm(KAAlarm::DEFERRED_REMINDER_ALARM);
03869             if (mDeferral == NORMAL_DEFERRAL)
03870                 return alarm(KAAlarm::DEFERRED_ALARM);
03871             // fall through to DEFERRED_ALARM
03872         case KAAlarm::DEFERRED_REMINDER_ALARM:
03873         case KAAlarm::DEFERRED_ALARM:
03874             if (mRepeatAtLogin)
03875                 return alarm(KAAlarm::AT_LOGIN_ALARM);
03876             // fall through to AT_LOGIN_ALARM
03877         case KAAlarm::AT_LOGIN_ALARM:
03878             if (mDisplaying)
03879                 return alarm(KAAlarm::DISPLAYING_ALARM);
03880             // fall through to DISPLAYING_ALARM
03881         case KAAlarm::DISPLAYING_ALARM:
03882             // fall through to default
03883         case KAAlarm::INVALID_ALARM:
03884         default:
03885             break;
03886     }
03887     return KAAlarm();
03888 }
03889 
03890 int KAEvent::alarmCount() const
03891 {
03892     return d->mAlarmCount;
03893 }
03894 
03895 /******************************************************************************
03896 * Remove the alarm of the specified type from the event.
03897 * This must only be called to remove an alarm which has expired, not to
03898 * reconfigure the event.
03899 */
03900 void KAEvent::removeExpiredAlarm(KAAlarm::Type type)
03901 {
03902     d->removeExpiredAlarm(type);
03903 }
03904 
03905 void KAEvent::Private::removeExpiredAlarm(KAAlarm::Type type)
03906 {
03907     const int count = mAlarmCount;
03908     switch (type)
03909     {
03910         case KAAlarm::MAIN_ALARM:
03911             if (!mReminderActive  ||  mReminderMinutes > 0)
03912             {
03913                 mAlarmCount = 0;    // removing main alarm - also remove subsidiary alarms
03914                 break;
03915             }
03916             // There is a reminder after the main alarm - retain the
03917             // reminder and remove other subsidiary alarms.
03918             mMainExpired = true;    // mark the alarm as expired now
03919             --mAlarmCount;
03920             set_deferral(NO_DEFERRAL);
03921             if (mDisplaying)
03922             {
03923                 mDisplaying = false;
03924                 --mAlarmCount;
03925             }
03926             // fall through to AT_LOGIN_ALARM
03927         case KAAlarm::AT_LOGIN_ALARM:
03928             if (mRepeatAtLogin)
03929             {
03930                 // Remove the at-login alarm, but keep a note of it for archiving purposes
03931                 mArchiveRepeatAtLogin = true;
03932                 mRepeatAtLogin = false;
03933                 --mAlarmCount;
03934             }
03935             break;
03936         case KAAlarm::REMINDER_ALARM:
03937             // Remove any reminder alarm, but keep a note of it for archiving purposes
03938             // and for restoration after the next recurrence.
03939             activate_reminder(false);
03940             break;
03941         case KAAlarm::DEFERRED_REMINDER_ALARM:
03942         case KAAlarm::DEFERRED_ALARM:
03943             set_deferral(NO_DEFERRAL);
03944             break;
03945         case KAAlarm::DISPLAYING_ALARM:
03946             if (mDisplaying)
03947             {
03948                 mDisplaying = false;
03949                 --mAlarmCount;
03950             }
03951             break;
03952         case KAAlarm::INVALID_ALARM:
03953         default:
03954             break;
03955     }
03956     if (mAlarmCount != count)
03957         mTriggerChanged = true;
03958 }
03959 
03960 void KAEvent::startChanges()
03961 {
03962     d->startChanges();
03963 }
03964 
03965 /******************************************************************************
03966 * Indicate that changes to the instance are complete.
03967 * This allows trigger times to be recalculated if any changes have occurred.
03968 */
03969 void KAEvent::endChanges()
03970 {
03971     d->endChanges();
03972 }
03973 
03974 void KAEvent::Private::endChanges()
03975 {
03976     if (mChangeCount > 0)
03977         --mChangeCount;
03978 }
03979 
03980 #ifndef USE_KRESOURCES
03981 /******************************************************************************
03982 * Return a list of pointers to KAEvent objects.
03983 */
03984 KAEvent::List KAEvent::ptrList(QVector<KAEvent>& objList)
03985 {
03986     KAEvent::List ptrs;
03987     for (int i = 0, count = objList.count();  i < count;  ++i)
03988         ptrs += &objList[i];
03989     return ptrs;
03990 }
03991 #endif
03992 
03993 void KAEvent::dumpDebug() const
03994 {
03995 #ifndef KDE_NO_DEBUG_OUTPUT
03996     d->dumpDebug();
03997 #endif
03998 }
03999 
04000 #ifndef KDE_NO_DEBUG_OUTPUT
04001 void KAEvent::Private::dumpDebug() const
04002 {
04003     kDebug() << "KAEvent dump:";
04004 #ifdef USE_KRESOURCES
04005     if (mResource) { kDebug() << "-- mResource:" << (void*)mResource; }
04006 #endif
04007     kDebug() << "-- mEventID:" << mEventID;
04008     kDebug() << "-- mActionSubType:" << (mActionSubType == MESSAGE ? "MESSAGE" : mActionSubType == FILE ? "FILE" : mActionSubType == COMMAND ? "COMMAND" : mActionSubType == EMAIL ? "EMAIL" : mActionSubType == AUDIO ? "AUDIO" : "??");
04009     kDebug() << "-- mNextMainDateTime:" << mNextMainDateTime.toString();
04010     kDebug() << "-- mCommandError:" << mCommandError;
04011     kDebug() << "-- mAllTrigger:" << mAllTrigger.toString();
04012     kDebug() << "-- mMainTrigger:" << mMainTrigger.toString();
04013     kDebug() << "-- mAllWorkTrigger:" << mAllWorkTrigger.toString();
04014     kDebug() << "-- mMainWorkTrigger:" << mMainWorkTrigger.toString();
04015     kDebug() << "-- mCategory:" << mCategory;
04016     if (!mTemplateName.isEmpty())
04017     {
04018         kDebug() << "-- mTemplateName:" << mTemplateName;
04019         kDebug() << "-- mTemplateAfterTime:" << mTemplateAfterTime;
04020     }
04021     kDebug() << "-- mText:" << mText;
04022     if (mActionSubType == MESSAGE  ||  mActionSubType == FILE)
04023     {
04024         kDebug() << "-- mBgColour:" << mBgColour.name();
04025         kDebug() << "-- mFgColour:" << mFgColour.name();
04026         kDebug() << "-- mUseDefaultFont:" << mUseDefaultFont;
04027         if (!mUseDefaultFont)
04028             kDebug() << "-- mFont:" << mFont.toString();
04029         kDebug() << "-- mSpeak:" << mSpeak;
04030         kDebug() << "-- mAudioFile:" << mAudioFile;
04031         kDebug() << "-- mPreAction:" << mPreAction;
04032         kDebug() << "-- mCancelOnPreActErr:" << mCancelOnPreActErr;
04033         kDebug() << "-- mDontShowPreActErr:" << mDontShowPreActErr;
04034         kDebug() << "-- mPostAction:" << mPostAction;
04035         kDebug() << "-- mLateCancel:" << mLateCancel;
04036         kDebug() << "-- mAutoClose:" << mAutoClose;
04037     }
04038     else if (mActionSubType == COMMAND)
04039     {
04040         kDebug() << "-- mCommandScript:" << mCommandScript;
04041         kDebug() << "-- mCommandXterm:" << mCommandXterm;
04042         kDebug() << "-- mCommandDisplay:" << mCommandDisplay;
04043         kDebug() << "-- mLogFile:" << mLogFile;
04044     }
04045     else if (mActionSubType == EMAIL)
04046     {
04047         kDebug() << "-- mEmail: FromKMail:" << mEmailFromIdentity;
04048         kDebug() << "--         Addresses:" << mEmailAddresses.join(",");
04049         kDebug() << "--         Subject:" << mEmailSubject;
04050         kDebug() << "--         Attachments:" << mEmailAttachments.join(",");
04051         kDebug() << "--         Bcc:" << mEmailBcc;
04052     }
04053     else if (mActionSubType == AUDIO)
04054         kDebug() << "-- mAudioFile:" << mAudioFile;
04055     kDebug() << "-- mBeep:" << mBeep;
04056     if (mActionSubType == AUDIO  ||  !mAudioFile.isEmpty())
04057     {
04058         if (mSoundVolume >= 0)
04059         {
04060             kDebug() << "-- mSoundVolume:" << mSoundVolume;
04061             if (mFadeVolume >= 0)
04062             {
04063                 kDebug() << "-- mFadeVolume:" << mFadeVolume;
04064                 kDebug() << "-- mFadeSeconds:" << mFadeSeconds;
04065             }
04066             else
04067                 kDebug() << "-- mFadeVolume:-:";
04068         }
04069         else
04070             kDebug() << "-- mSoundVolume:-:";
04071         kDebug() << "-- mRepeatSoundPause:" << mRepeatSoundPause;
04072     }
04073     kDebug() << "-- mKMailSerialNumber:" << mKMailSerialNumber;
04074     kDebug() << "-- mCopyToKOrganizer:" << mCopyToKOrganizer;
04075     kDebug() << "-- mExcludeHolidays:" << (bool)mExcludeHolidays;
04076     kDebug() << "-- mWorkTimeOnly:" << mWorkTimeOnly;
04077     kDebug() << "-- mStartDateTime:" << mStartDateTime.toString();
04078     kDebug() << "-- mCreatedDateTime:" << mCreatedDateTime;
04079     kDebug() << "-- mRepeatAtLogin:" << mRepeatAtLogin;
04080     if (mRepeatAtLogin)
04081         kDebug() << "-- mAtLoginDateTime:" << mAtLoginDateTime;
04082     kDebug() << "-- mArchiveRepeatAtLogin:" << mArchiveRepeatAtLogin;
04083     kDebug() << "-- mConfirmAck:" << mConfirmAck;
04084     kDebug() << "-- mEnabled:" << mEnabled;
04085 #ifndef USE_KRESOURCES
04086     kDebug() << "-- mItemId:" << mItemId;
04087     kDebug() << "-- mCompatibility:" << mCompatibility;
04088     kDebug() << "-- mReadOnly:" << mReadOnly;
04089 #endif
04090     if (mReminderMinutes)
04091     {
04092         kDebug() << "-- mReminderMinutes:" << mReminderMinutes;
04093         kDebug() << "-- mReminderActive:" << (mReminderActive == ACTIVE_REMINDER ? "active" : mReminderActive == HIDDEN_REMINDER ? "hidden" : "no");
04094         kDebug() << "-- mReminderOnceOnly:" << mReminderOnceOnly;
04095     }
04096     else if (mDeferral > 0)
04097     {
04098         kDebug() << "-- mDeferral:" << (mDeferral == NORMAL_DEFERRAL ? "normal" : "reminder");
04099         kDebug() << "-- mDeferralTime:" << mDeferralTime.toString();
04100     }
04101     kDebug() << "-- mDeferDefaultMinutes:" << mDeferDefaultMinutes;
04102     if (mDeferDefaultMinutes)
04103         kDebug() << "-- mDeferDefaultDateOnly:" << mDeferDefaultDateOnly;
04104     if (mDisplaying)
04105     {
04106         kDebug() << "-- mDisplayingTime:" << mDisplayingTime.toString();
04107         kDebug() << "-- mDisplayingFlags:" << mDisplayingFlags;
04108         kDebug() << "-- mDisplayingDefer:" << mDisplayingDefer;
04109         kDebug() << "-- mDisplayingEdit:" << mDisplayingEdit;
04110     }
04111     kDebug() << "-- mRevision:" << mRevision;
04112     kDebug() << "-- mRecurrence:" << mRecurrence;
04113     if (!mRepetition)
04114         kDebug() << "-- mRepetition: 0";
04115     else if (mRepetition.isDaily())
04116         kDebug() << "-- mRepetition: count:" << mRepetition.count() << ", interval:" << mRepetition.intervalDays() << "days";
04117     else
04118         kDebug() << "-- mRepetition: count:" << mRepetition.count() << ", interval:" << mRepetition.intervalMinutes() << "minutes";
04119     kDebug() << "-- mNextRepeat:" << mNextRepeat;
04120     kDebug() << "-- mAlarmCount:" << mAlarmCount;
04121     kDebug() << "-- mMainExpired:" << mMainExpired;
04122     kDebug() << "-- mDisplaying:" << mDisplaying;
04123     kDebug() << "KAEvent dump end";
04124 }
04125 #endif
04126 
04127 
04128 /******************************************************************************
04129 * Fetch the start and next date/time for a KCal::Event.
04130 * Reply = next main date/time.
04131 */
04132 #ifndef USE_KRESOURCES
04133 DateTime KAEvent::Private::readDateTime(const Event::Ptr& event, bool dateOnly, DateTime& start)
04134 #else
04135 DateTime KAEvent::Private::readDateTime(const Event* event, bool dateOnly, DateTime& start)
04136 #endif
04137 {
04138     start = event->dtStart();
04139     if (dateOnly)
04140     {
04141         // A date-only event is indicated by the X-KDE-KALARM-FLAGS:DATE property, not
04142         // by a date-only start date/time (for the reasons given in updateKCalEvent()).
04143         start.setDateOnly(true);
04144     }
04145     DateTime next = start;
04146     QString prop = event->customProperty(KACalendar::APPNAME, Private::NEXT_RECUR_PROPERTY);
04147     if (prop.length() >= 8)
04148     {
04149         // The next due recurrence time is specified
04150         const QDate d(prop.left(4).toInt(), prop.mid(4,2).toInt(), prop.mid(6,2).toInt());
04151         if (d.isValid())
04152         {
04153             if (dateOnly  &&  prop.length() == 8)
04154                 next.setDate(d);
04155             else if (!dateOnly  &&  prop.length() == 15  &&  prop[8] == QChar('T'))
04156             {
04157                 const QTime t(prop.mid(9,2).toInt(), prop.mid(11,2).toInt(), prop.mid(13,2).toInt());
04158                 if (t.isValid())
04159                 {
04160                     next.setDate(d);
04161                     next.setTime(t);
04162                 }
04163             }
04164             if (next < start)
04165                 next = start;   // ensure next recurrence time is valid
04166         }
04167     }
04168     return next;
04169 }
04170 
04171 /******************************************************************************
04172 * Parse the alarms for a KCal::Event.
04173 * Reply = map of alarm data, indexed by KAAlarm::Type
04174 */
04175 #ifndef USE_KRESOURCES
04176 void KAEvent::Private::readAlarms(const Event::Ptr& event, void* almap, bool cmdDisplay)
04177 #else
04178 void KAEvent::Private::readAlarms(const Event* event, void* almap, bool cmdDisplay)
04179 #endif
04180 {
04181     AlarmMap* alarmMap = (AlarmMap*)almap;
04182     Alarm::List alarms = event->alarms();
04183 
04184     // Check if it's an audio event with no display alarm
04185     bool audioOnly = false;
04186     for (int i = 0, end = alarms.count();  i < end;  ++i)
04187     {
04188         switch (alarms[i]->type())
04189         {
04190             case Alarm::Display:
04191             case Alarm::Procedure:
04192                 audioOnly = false;
04193                 i = end;   // exit from the 'for' loop
04194                 break;
04195             case Alarm::Audio:
04196                 audioOnly = true;
04197                 break;
04198             default:
04199                 break;
04200         }
04201     }
04202 
04203     for (int i = 0, end = alarms.count();  i < end;  ++i)
04204     {
04205         // Parse the next alarm's text
04206         AlarmData data;
04207         readAlarm(alarms[i], data, audioOnly, cmdDisplay);
04208         if (data.type != INVALID_ALARM)
04209             alarmMap->insert(data.type, data);
04210     }
04211 }
04212 
04213 /******************************************************************************
04214 * Parse a KCal::Alarm.
04215 * If 'audioMain' is true, the event contains an audio alarm but no display alarm.
04216 * Reply = alarm ID (sequence number)
04217 */
04218 #ifndef USE_KRESOURCES
04219 void KAEvent::Private::readAlarm(const Alarm::Ptr& alarm, AlarmData& data, bool audioMain, bool cmdDisplay)
04220 #else
04221 void KAEvent::Private::readAlarm(const Alarm* alarm, AlarmData& data, bool audioMain, bool cmdDisplay)
04222 #endif
04223 {
04224     // Parse the next alarm's text
04225     data.alarm            = alarm;
04226     data.displayingFlags  = 0;
04227     data.isEmailText      = false;
04228     data.speak            = false;
04229     data.hiddenReminder   = false;
04230     data.timedDeferral    = false;
04231     data.nextRepeat       = 0;
04232     data.repeatSoundPause = -1;
04233     if (alarm->repeatCount())
04234     {
04235         bool ok;
04236         const QString property = alarm->customProperty(KACalendar::APPNAME, Private::NEXT_REPEAT_PROPERTY);
04237         int n = static_cast<int>(property.toUInt(&ok));
04238         if (ok)
04239             data.nextRepeat = n;
04240     }
04241     QString property = alarm->customProperty(KACalendar::APPNAME, Private::FLAGS_PROPERTY);
04242     const QStringList flags = property.split(Private::SC, QString::SkipEmptyParts);
04243     switch (alarm->type())
04244     {
04245         case Alarm::Procedure:
04246             data.action        = KAAlarm::COMMAND;
04247             data.cleanText     = alarm->programFile();
04248             data.commandScript = data.cleanText.isEmpty();   // blank command indicates a script
04249             if (!alarm->programArguments().isEmpty())
04250             {
04251                 if (!data.commandScript)
04252                     data.cleanText += ' ';
04253                 data.cleanText += alarm->programArguments();
04254             }
04255             data.cancelOnPreActErr = flags.contains(Private::CANCEL_ON_ERROR_FLAG);
04256             data.dontShowPreActErr = flags.contains(Private::DONT_SHOW_ERROR_FLAG);
04257             if (!cmdDisplay)
04258                 break;
04259             // fall through to Display
04260         case Alarm::Display:
04261         {
04262             if (alarm->type() == Alarm::Display)
04263             {
04264                 data.action    = KAAlarm::MESSAGE;
04265                 data.cleanText = AlarmText::fromCalendarText(alarm->text(), data.isEmailText);
04266             }
04267             const QString property = alarm->customProperty(KACalendar::APPNAME, Private::FONT_COLOUR_PROPERTY);
04268             const QStringList list = property.split(QLatin1Char(';'), QString::KeepEmptyParts);
04269             data.bgColour = QColor(255, 255, 255);   // white
04270             data.fgColour = QColor(0, 0, 0);         // black
04271             const int n = list.count();
04272             if (n > 0)
04273             {
04274                 if (!list[0].isEmpty())
04275                 {
04276                     QColor c(list[0]);
04277                     if (c.isValid())
04278                         data.bgColour = c;
04279                 }
04280                 if (n > 1  &&  !list[1].isEmpty())
04281                 {
04282                     QColor c(list[1]);
04283                     if (c.isValid())
04284                         data.fgColour = c;
04285                 }
04286             }
04287             data.defaultFont = (n <= 2 || list[2].isEmpty());
04288             if (!data.defaultFont)
04289                 data.font.fromString(list[2]);
04290             break;
04291         }
04292         case Alarm::Email:
04293         {
04294             data.action    = KAAlarm::EMAIL;
04295             data.cleanText = alarm->mailText();
04296             const int i = flags.indexOf(Private::EMAIL_ID_FLAG);
04297             data.emailFromId = (i >= 0  &&  i + 1 < flags.count()) ? flags[i + 1].toUInt() : 0;
04298             break;
04299         }
04300         case Alarm::Audio:
04301         {
04302             data.action      = KAAlarm::AUDIO;
04303             data.cleanText   = alarm->audioFile();
04304             data.repeatSoundPause = (alarm->repeatCount() == -2) ? alarm->snoozeTime().asSeconds()
04305                                   : (alarm->repeatCount() == -1) ? 0 : -1;
04306             data.soundVolume = -1;
04307             data.fadeVolume  = -1;
04308             data.fadeSeconds = 0;
04309             QString property = alarm->customProperty(KACalendar::APPNAME, Private::VOLUME_PROPERTY);
04310             if (!property.isEmpty())
04311             {
04312                 bool ok;
04313                 float fadeVolume;
04314                 int   fadeSecs = 0;
04315                 const QStringList list = property.split(QLatin1Char(';'), QString::KeepEmptyParts);
04316                 data.soundVolume = list[0].toFloat(&ok);
04317                 if (!ok  ||  data.soundVolume > 1.0f)
04318                     data.soundVolume = -1;
04319                 if (data.soundVolume >= 0  &&  list.count() >= 3)
04320                 {
04321                     fadeVolume = list[1].toFloat(&ok);
04322                     if (ok)
04323                         fadeSecs = static_cast<int>(list[2].toUInt(&ok));
04324                     if (ok  &&  fadeVolume >= 0  &&  fadeVolume <= 1.0f  &&  fadeSecs > 0)
04325                     {
04326                         data.fadeVolume  = fadeVolume;
04327                         data.fadeSeconds = fadeSecs;
04328                     }
04329                 }
04330             }
04331             if (!audioMain)
04332             {
04333                 data.type  = AUDIO_ALARM;
04334                 data.speak = flags.contains(Private::SPEAK_FLAG);
04335                 return;
04336             }
04337             break;
04338         }
04339         case Alarm::Invalid:
04340             data.type = INVALID_ALARM;
04341             return;
04342     }
04343 
04344     bool atLogin          = false;
04345     bool reminder         = false;
04346     bool deferral         = false;
04347     bool dateDeferral     = false;
04348     bool repeatSound      = false;
04349     data.type = MAIN_ALARM;
04350     property = alarm->customProperty(KACalendar::APPNAME, Private::TYPE_PROPERTY);
04351     const QStringList types = property.split(QLatin1Char(','), QString::SkipEmptyParts);
04352     for (int i = 0, end = types.count();  i < end;  ++i)
04353     {
04354         const QString type = types[i];
04355         if (type == Private::AT_LOGIN_TYPE)
04356             atLogin = true;
04357         else if (type == Private::FILE_TYPE  &&  data.action == KAAlarm::MESSAGE)
04358             data.action = KAAlarm::FILE;
04359         else if (type == Private::REMINDER_TYPE)
04360             reminder = true;
04361         else if (type == Private::TIME_DEFERRAL_TYPE)
04362             deferral = true;
04363         else if (type == Private::DATE_DEFERRAL_TYPE)
04364             dateDeferral = deferral = true;
04365         else if (type == Private::DISPLAYING_TYPE)
04366             data.type = DISPLAYING_ALARM;
04367         else if (type == Private::PRE_ACTION_TYPE  &&  data.action == KAAlarm::COMMAND)
04368             data.type = PRE_ACTION_ALARM;
04369         else if (type == Private::POST_ACTION_TYPE  &&  data.action == KAAlarm::COMMAND)
04370             data.type = POST_ACTION_ALARM;
04371         else if (type == Private::SOUND_REPEAT_TYPE  &&  data.action == KAAlarm::AUDIO)
04372             repeatSound = true;
04373     }
04374     if (repeatSound && data.repeatSoundPause < 0)
04375         data.repeatSoundPause = 0;
04376     else if (!repeatSound)
04377         data.repeatSoundPause = -1;
04378 
04379     if (reminder)
04380     {
04381         if (data.type == MAIN_ALARM)
04382         {
04383             data.type = deferral ? DEFERRED_REMINDER_ALARM : REMINDER_ALARM;
04384             data.timedDeferral = (deferral && !dateDeferral);
04385         }
04386         else if (data.type == DISPLAYING_ALARM)
04387             data.displayingFlags = dateDeferral ? REMINDER | DATE_DEFERRAL
04388                                  : deferral ? REMINDER | TIME_DEFERRAL : REMINDER;
04389         else if (data.type == REMINDER_ALARM
04390              &&  flags.contains(Private::HIDDEN_REMINDER_FLAG))
04391             data.hiddenReminder = true;
04392     }
04393     else if (deferral)
04394     {
04395         if (data.type == MAIN_ALARM)
04396         {
04397             data.type = DEFERRED_ALARM;
04398             data.timedDeferral = !dateDeferral;
04399         }
04400         else if (data.type == DISPLAYING_ALARM)
04401             data.displayingFlags = dateDeferral ? DATE_DEFERRAL : TIME_DEFERRAL;
04402     }
04403     if (atLogin)
04404     {
04405         if (data.type == MAIN_ALARM)
04406             data.type = AT_LOGIN_ALARM;
04407         else if (data.type == DISPLAYING_ALARM)
04408             data.displayingFlags = REPEAT_AT_LOGIN;
04409     }
04410 //kDebug()<<"text="<<alarm->text()<<", time="<<alarm->time().toString()<<", valid time="<<alarm->time().isValid();
04411 }
04412 
04413 /******************************************************************************
04414 * Calculate the next trigger times of the alarm.
04415 * This should only be called when changes have actually occurred which might
04416 * affect the event's trigger times.
04417 * mMainTrigger is set to the next scheduled recurrence/sub-repetition, or the
04418 *              deferral time if a deferral is pending.
04419 * mAllTrigger is the same as mMainTrigger, but takes account of reminders.
04420 * mMainWorkTrigger is set to the next scheduled recurrence/sub-repetition
04421 *                  which occurs in working hours, if working-time-only is set.
04422 * mAllWorkTrigger is the same as mMainWorkTrigger, but takes account of reminders.
04423 */
04424 void KAEvent::Private::calcTriggerTimes() const
04425 {
04426     if (mChangeCount)
04427         return;
04428 #ifdef __GNUC__
04429 #warning May need to set date-only alarms to after start-of-day time in working-time checks
04430 #endif
04431     bool recurs = (checkRecur() != KARecurrence::NO_RECUR);
04432     if ((recurs  &&  mWorkTimeOnly  &&  mWorkTimeOnly != mWorkTimeIndex)
04433     ||  (recurs  &&  mExcludeHolidays  &&  mExcludeHolidays != mHolidays))
04434     {
04435         // It's a work time alarm, and work days/times have changed, or
04436         // it excludes holidays, and the holidays definition has changed.
04437         mTriggerChanged = true;
04438     }
04439     else if (!mTriggerChanged)
04440         return;
04441     mTriggerChanged = false;
04442     if (recurs  &&  mWorkTimeOnly)
04443         mWorkTimeOnly = mWorkTimeIndex;   // note which work time definition was used in calculation
04444     if (recurs  &&  mExcludeHolidays)
04445         mExcludeHolidays = mHolidays;     // note which holiday definition was used in calculation
04446 
04447     if (mCategory == CalEvent::ARCHIVED  ||  mCategory == CalEvent::TEMPLATE)
04448     {
04449         // It's a template or archived
04450         mAllTrigger = mMainTrigger = mAllWorkTrigger = mMainWorkTrigger = KDateTime();
04451     }
04452     else if (mDeferral == NORMAL_DEFERRAL)
04453     {
04454         // For a deferred alarm, working time setting is ignored
04455         mAllTrigger = mMainTrigger = mAllWorkTrigger = mMainWorkTrigger = mDeferralTime;
04456     }
04457     else
04458     {
04459         mMainTrigger = mainDateTime(true);   // next recurrence or sub-repetition
04460         mAllTrigger = (mDeferral == REMINDER_DEFERRAL)     ? mDeferralTime
04461                     : (mReminderActive != ACTIVE_REMINDER) ? mMainTrigger
04462                     : (mReminderMinutes < 0)               ? mReminderAfterTime
04463                     :                                        mMainTrigger.addMins(-mReminderMinutes);
04464         // It's not deferred.
04465         // If only-during-working-time is set and it recurs, it won't actually trigger
04466         // unless it falls during working hours.
04467         if ((!mWorkTimeOnly && !mExcludeHolidays)
04468         ||  !recurs
04469         ||  isWorkingTime(mMainTrigger.kDateTime()))
04470         {
04471             // It only occurs once, or it complies with any working hours/holiday
04472             // restrictions.
04473             mMainWorkTrigger = mMainTrigger;
04474             mAllWorkTrigger = mAllTrigger;
04475         }
04476         else if (mWorkTimeOnly)
04477         {
04478             // The alarm is restricted to working hours.
04479             // Finding the next occurrence during working hours can sometimes take a long time,
04480             // so mark the next actual trigger as invalid until the calculation completes.
04481             // Note that reminders are only triggered if the main alarm is during working time.
04482             if (!mExcludeHolidays)
04483             {
04484                 // There are no holiday restrictions.
04485                 calcNextWorkingTime(mMainTrigger);
04486             }
04487             else if (mHolidays)
04488             {
04489                 // Holidays are excluded.
04490                 DateTime nextTrigger = mMainTrigger;
04491                 KDateTime kdt;
04492                 for (int i = 0;  i < 20;  ++i)
04493                 {
04494                     calcNextWorkingTime(nextTrigger);
04495                     if (!mHolidays->isHoliday(mMainWorkTrigger.date()))
04496                         return;   // found a non-holiday occurrence
04497                     kdt = mMainWorkTrigger.effectiveKDateTime();
04498                     kdt.setTime(QTime(23,59,59));
04499                     const OccurType type = nextOccurrence(kdt, nextTrigger, RETURN_REPETITION);
04500                     if (!nextTrigger.isValid())
04501                         break;
04502                     if (isWorkingTime(nextTrigger.kDateTime()))
04503                     {
04504                         const int reminder = (mReminderMinutes > 0) ? mReminderMinutes : 0;   // only interested in reminders BEFORE the alarm
04505                         mMainWorkTrigger = nextTrigger;
04506                         mAllWorkTrigger = (type & OCCURRENCE_REPEAT) ? mMainWorkTrigger : mMainWorkTrigger.addMins(-reminder);
04507                         return;   // found a non-holiday occurrence
04508                     }
04509                 }
04510                 mMainWorkTrigger = mAllWorkTrigger = DateTime();
04511             }
04512         }
04513         else if (mExcludeHolidays  &&  mHolidays)
04514         {
04515             // Holidays are excluded.
04516             DateTime nextTrigger = mMainTrigger;
04517             KDateTime kdt;
04518             for (int i = 0;  i < 20;  ++i)
04519             {
04520                 kdt = nextTrigger.effectiveKDateTime();
04521                 kdt.setTime(QTime(23,59,59));
04522                 const OccurType type = nextOccurrence(kdt, nextTrigger, RETURN_REPETITION);
04523                 if (!nextTrigger.isValid())
04524                     break;
04525                 if (!mHolidays->isHoliday(nextTrigger.date()))
04526                 {
04527                     const int reminder = (mReminderMinutes > 0) ? mReminderMinutes : 0;   // only interested in reminders BEFORE the alarm
04528                     mMainWorkTrigger = nextTrigger;
04529                     mAllWorkTrigger = (type & OCCURRENCE_REPEAT) ? mMainWorkTrigger : mMainWorkTrigger.addMins(-reminder);
04530                     return;   // found a non-holiday occurrence
04531                 }
04532             }
04533             mMainWorkTrigger = mAllWorkTrigger = DateTime();
04534         }
04535     }
04536 }
04537 
04538 /******************************************************************************
04539 * Return the time of the next scheduled occurrence of the event during working
04540 * hours, for an alarm which is restricted to working hours.
04541 * On entry, 'nextTrigger' = the next recurrence or repetition (as returned by
04542 * mainDateTime(true) ).
04543 */
04544 void KAEvent::Private::calcNextWorkingTime(const DateTime& nextTrigger) const
04545 {
04546     kDebug() << "next=" << nextTrigger.kDateTime().dateTime();
04547     mMainWorkTrigger = mAllWorkTrigger = DateTime();
04548 
04549     for (int i = 0;  ;  ++i)
04550     {
04551         if (i >= 7)
04552             return;   // no working days are defined
04553         if (mWorkDays.testBit(i))
04554             break;
04555     }
04556     const KARecurrence::Type recurType = checkRecur();
04557     KDateTime kdt = nextTrigger.effectiveKDateTime();
04558     const int reminder = (mReminderMinutes > 0) ? mReminderMinutes : 0;   // only interested in reminders BEFORE the alarm
04559     // Check if it always falls on the same day(s) of the week.
04560     const RecurrenceRule* rrule = mRecurrence->defaultRRuleConst();
04561     if (!rrule)
04562         return;   // no recurrence rule!
04563     unsigned allDaysMask = 0x7F;  // mask bits for all days of week
04564     bool noWorkPos = false;  // true if no recurrence day position is working day
04565     const QList<RecurrenceRule::WDayPos> pos = rrule->byDays();
04566     const int nDayPos = pos.count();  // number of day positions
04567     if (nDayPos)
04568     {
04569         noWorkPos = true;
04570         allDaysMask = 0;
04571         for (int i = 0;  i < nDayPos;  ++i)
04572         {
04573             const int day = pos[i].day() - 1;  // Monday = 0
04574             if (mWorkDays.testBit(day))
04575                 noWorkPos = false;   // found a working day occurrence
04576             allDaysMask |= 1 << day;
04577         }
04578         if (noWorkPos  &&  !mRepetition)
04579             return;   // never occurs on a working day
04580     }
04581     DateTime newdt;
04582 
04583     if (mStartDateTime.isDateOnly())
04584     {
04585         // It's a date-only alarm.
04586         // Sub-repetitions also have to be date-only.
04587         const int repeatFreq = mRepetition.intervalDays();
04588         const bool weeklyRepeat = mRepetition && !(repeatFreq % 7);
04589         const Duration interval = mRecurrence->regularInterval();
04590         if ((interval  &&  !(interval.asDays() % 7))
04591         ||  nDayPos == 1)
04592         {
04593             // It recurs on the same day each week
04594             if (!mRepetition || weeklyRepeat)
04595                 return;   // any repetitions are also weekly
04596 
04597             // It's a weekly recurrence with a non-weekly sub-repetition.
04598             // Check one cycle of repetitions for the next one that lands
04599             // on a working day.
04600             KDateTime dt(nextTrigger.kDateTime().addDays(1));
04601             dt.setTime(QTime(0,0,0));
04602             previousOccurrence(dt, newdt, false);
04603             if (!newdt.isValid())
04604                 return;   // this should never happen
04605             kdt = newdt.effectiveKDateTime();
04606             const int day = kdt.date().dayOfWeek() - 1;   // Monday = 0
04607             for (int repeatNum = mNextRepeat + 1;  ;  ++repeatNum)
04608             {
04609                 if (repeatNum > mRepetition.count())
04610                     repeatNum = 0;
04611                 if (repeatNum == mNextRepeat)
04612                     break;
04613                 if (!repeatNum)
04614                 {
04615                     nextOccurrence(newdt.kDateTime(), newdt, IGNORE_REPETITION);
04616                     if (mWorkDays.testBit(day))
04617                     {
04618                         mMainWorkTrigger = newdt;
04619                         mAllWorkTrigger  = mMainWorkTrigger.addMins(-reminder);
04620                         return;
04621                     }
04622                     kdt = newdt.effectiveKDateTime();
04623                 }
04624                 else
04625                 {
04626                     const int inc = repeatFreq * repeatNum;
04627                     if (mWorkDays.testBit((day + inc) % 7))
04628                     {
04629                         kdt = kdt.addDays(inc);
04630                         kdt.setDateOnly(true);
04631                         mMainWorkTrigger = mAllWorkTrigger = kdt;
04632                         return;
04633                     }
04634                 }
04635             }
04636             return;
04637         }
04638         if (!mRepetition  ||  weeklyRepeat)
04639         {
04640             // It's a date-only alarm with either no sub-repetition or a
04641             // sub-repetition which always falls on the same day of the week
04642             // as the recurrence (if any).
04643             unsigned days = 0;
04644             for ( ; ; )
04645             {
04646                 kdt.setTime(QTime(23,59,59));
04647                 nextOccurrence(kdt, newdt, IGNORE_REPETITION);
04648                 if (!newdt.isValid())
04649                     return;
04650                 kdt = newdt.effectiveKDateTime();
04651                 const int day = kdt.date().dayOfWeek() - 1;
04652                 if (mWorkDays.testBit(day))
04653                     break;   // found a working day occurrence
04654                 // Prevent indefinite looping (which should never happen anyway)
04655                 if ((days & allDaysMask) == allDaysMask)
04656                     return;  // found a recurrence on every possible day of the week!?!
04657                 days |= 1 << day;
04658             }
04659             kdt.setDateOnly(true);
04660             mMainWorkTrigger = kdt;
04661             mAllWorkTrigger  = kdt.addSecs(-60 * reminder);
04662             return;
04663         }
04664 
04665         // It's a date-only alarm which recurs on different days of the week,
04666         // as does the sub-repetition.
04667         // Find the previous recurrence (as opposed to sub-repetition)
04668         unsigned days = 1 << (kdt.date().dayOfWeek() - 1);
04669         KDateTime dt(nextTrigger.kDateTime().addDays(1));
04670         dt.setTime(QTime(0,0,0));
04671         previousOccurrence(dt, newdt, false);
04672         if (!newdt.isValid())
04673             return;   // this should never happen
04674         kdt = newdt.effectiveKDateTime();
04675         int day = kdt.date().dayOfWeek() - 1;   // Monday = 0
04676         for (int repeatNum = mNextRepeat;  ;  repeatNum = 0)
04677         {
04678             while (++repeatNum <= mRepetition.count())
04679             {
04680                 const int inc = repeatFreq * repeatNum;
04681                 if (mWorkDays.testBit((day + inc) % 7))
04682                 {
04683                     kdt = kdt.addDays(inc);
04684                     kdt.setDateOnly(true);
04685                     mMainWorkTrigger = mAllWorkTrigger = kdt;
04686                     return;
04687                 }
04688                 if ((days & allDaysMask) == allDaysMask)
04689                     return;  // found an occurrence on every possible day of the week!?!
04690                 days |= 1 << day;
04691             }
04692             nextOccurrence(kdt, newdt, IGNORE_REPETITION);
04693             if (!newdt.isValid())
04694                 return;
04695             kdt = newdt.effectiveKDateTime();
04696             day = kdt.date().dayOfWeek() - 1;
04697             if (mWorkDays.testBit(day))
04698             {
04699                 kdt.setDateOnly(true);
04700                 mMainWorkTrigger = kdt;
04701                 mAllWorkTrigger  = kdt.addSecs(-60 * reminder);
04702                 return;
04703             }
04704             if ((days & allDaysMask) == allDaysMask)
04705                 return;  // found an occurrence on every possible day of the week!?!
04706             days |= 1 << day;
04707         }
04708         return;
04709     }
04710 
04711     // It's a date-time alarm.
04712 
04713     /* Check whether the recurrence or sub-repetition occurs at the same time
04714      * every day. Note that because of seasonal time changes, a recurrence
04715      * defined in terms of minutes will vary its time of day even if its value
04716      * is a multiple of a day (24*60 minutes). Sub-repetitions are considered
04717      * to repeat at the same time of day regardless of time changes if they
04718      * are multiples of a day, which doesn't strictly conform to the iCalendar
04719      * format because this only allows their interval to be recorded in seconds.
04720      */
04721     const bool recurTimeVaries = (recurType == KARecurrence::MINUTELY);
04722     const bool repeatTimeVaries = (mRepetition  &&  !mRepetition.isDaily());
04723 
04724     if (!recurTimeVaries  &&  !repeatTimeVaries)
04725     {
04726         // The alarm always occurs at the same time of day.
04727         // Check whether it can ever occur during working hours.
04728         if (!mayOccurDailyDuringWork(kdt))
04729             return;   // never occurs during working hours
04730 
04731         // Find the next working day it occurs on
04732         bool repetition = false;
04733         unsigned days = 0;
04734         for ( ; ; )
04735         {
04736             OccurType type = nextOccurrence(kdt, newdt, RETURN_REPETITION);
04737             if (!newdt.isValid())
04738                 return;
04739             repetition = (type & OCCURRENCE_REPEAT);
04740             kdt = newdt.effectiveKDateTime();
04741             const int day = kdt.date().dayOfWeek() - 1;
04742             if (mWorkDays.testBit(day))
04743                 break;   // found a working day occurrence
04744             // Prevent indefinite looping (which should never happen anyway)
04745             if (!repetition)
04746             {
04747                 if ((days & allDaysMask) == allDaysMask)
04748                     return;  // found a recurrence on every possible day of the week!?!
04749                 days |= 1 << day;
04750             }
04751         }
04752         mMainWorkTrigger = nextTrigger;
04753         mMainWorkTrigger.setDate(kdt.date());
04754         mAllWorkTrigger = repetition ? mMainWorkTrigger : mMainWorkTrigger.addMins(-reminder);
04755         return;
04756     }
04757 
04758     // The alarm occurs at different times of day.
04759     // We may need to check for a full annual cycle of seasonal time changes, in
04760     // case it only occurs during working hours after a time change.
04761     KTimeZone tz = kdt.timeZone();
04762     if (tz.isValid()  &&  tz.type() == "KSystemTimeZone")
04763     {
04764         // It's a system time zone, so fetch full transition information
04765         const KTimeZone ktz = KSystemTimeZones::readZone(tz.name());
04766         if (ktz.isValid())
04767             tz = ktz;
04768     }
04769     const QList<KTimeZone::Transition> tzTransitions = tz.transitions();
04770 
04771     if (recurTimeVaries)
04772     {
04773         /* The alarm recurs at regular clock intervals, at different times of day.
04774          * Note that for this type of recurrence, it's necessary to avoid the
04775          * performance overhead of Recurrence class calls since these can in the
04776          * worst case cause the program to hang for a significant length of time.
04777          * In this case, we can calculate the next recurrence by simply adding the
04778          * recurrence interval, since KAlarm offers no facility to regularly miss
04779          * recurrences. (But exception dates/times need to be taken into account.)
04780          */
04781         KDateTime kdtRecur;
04782         int repeatFreq = 0;
04783         int repeatNum = 0;
04784         if (mRepetition)
04785         {
04786             // It's a repetition inside a recurrence, each of which occurs
04787             // at different times of day (bearing in mind that the repetition
04788             // may occur at daily intervals after each recurrence).
04789             // Find the previous recurrence (as opposed to sub-repetition)
04790             repeatFreq = mRepetition.intervalSeconds();
04791             previousOccurrence(kdt.addSecs(1), newdt, false);
04792             if (!newdt.isValid())
04793                 return;   // this should never happen
04794             kdtRecur = newdt.effectiveKDateTime();
04795             repeatNum = kdtRecur.secsTo(kdt) / repeatFreq;
04796             kdt = kdtRecur.addSecs(repeatNum * repeatFreq);
04797         }
04798         else
04799         {
04800             // There is no sub-repetition.
04801             // (N.B. Sub-repetitions can't exist without a recurrence.)
04802             // Check until the original time wraps round, but ensure that
04803             // if there are seasonal time changes, that all other subsequent
04804             // time offsets within the next year are checked.
04805             // This does not guarantee to find the next working time,
04806             // particularly if there are exceptions, but it's a
04807             // reasonable try.
04808             kdtRecur = kdt;
04809         }
04810         QTime firstTime = kdtRecur.time();
04811         int firstOffset = kdtRecur.utcOffset();
04812         int currentOffset = firstOffset;
04813         int dayRecur = kdtRecur.date().dayOfWeek() - 1;   // Monday = 0
04814         int firstDay = dayRecur;
04815         QDate finalDate;
04816         const bool subdaily = (repeatFreq < 24*3600);
04817 //        int period = mRecurrence->frequency() % (24*60);  // it is by definition a MINUTELY recurrence
04818 //        int limit = (24*60 + period - 1) / period;  // number of times until recurrence wraps round
04819         int transitionIndex = -1;
04820         for (int n = 0;  n < 7*24*60;  ++n)
04821         {
04822             if (mRepetition)
04823             {
04824                 // Check the sub-repetitions for this recurrence
04825                 for ( ; ; )
04826                 {
04827                     // Find the repeat count to the next start of the working day
04828                     const int inc = subdaily ? nextWorkRepetition(kdt) : 1;
04829                     repeatNum += inc;
04830                     if (repeatNum > mRepetition.count())
04831                         break;
04832                     kdt = kdt.addSecs(inc * repeatFreq);
04833                     const QTime t = kdt.time();
04834                     if (t >= mWorkDayStart  &&  t < mWorkDayEnd)
04835                     {
04836                         if (mWorkDays.testBit(kdt.date().dayOfWeek() - 1))
04837                         {
04838                             mMainWorkTrigger = mAllWorkTrigger = kdt;
04839                             return;
04840                         }
04841                     }
04842                 }
04843                 repeatNum = 0;
04844             }
04845             nextOccurrence(kdtRecur, newdt, IGNORE_REPETITION);
04846             if (!newdt.isValid())
04847                 return;
04848             kdtRecur = newdt.effectiveKDateTime();
04849             dayRecur = kdtRecur.date().dayOfWeek() - 1;   // Monday = 0
04850             const QTime t = kdtRecur.time();
04851             if (t >= mWorkDayStart  &&  t < mWorkDayEnd)
04852             {
04853                 if (mWorkDays.testBit(dayRecur))
04854                 {
04855                     mMainWorkTrigger = kdtRecur;
04856                     mAllWorkTrigger  = kdtRecur.addSecs(-60 * reminder);
04857                     return;
04858                 }
04859             }
04860             if (kdtRecur.utcOffset() != currentOffset)
04861                 currentOffset = kdtRecur.utcOffset();
04862             if (t == firstTime  &&  dayRecur == firstDay  &&  currentOffset == firstOffset)
04863             {
04864                 // We've wrapped round to the starting day and time.
04865                 // If there are seasonal time changes, check for up
04866                 // to the next year in other time offsets in case the
04867                 // alarm occurs inside working hours then.
04868                 if (!finalDate.isValid())
04869                     finalDate = kdtRecur.date();
04870                 const int i = tz.transitionIndex(kdtRecur.toUtc().dateTime());
04871                 if (i < 0)
04872                     return;
04873                 if (i > transitionIndex)
04874                     transitionIndex = i;
04875                 if (++transitionIndex >= static_cast<int>(tzTransitions.count()))
04876                     return;
04877                 previousOccurrence(KDateTime(tzTransitions[transitionIndex].time(), KDateTime::UTC), newdt, IGNORE_REPETITION);
04878                 kdtRecur = newdt.effectiveKDateTime();
04879                 if (finalDate.daysTo(kdtRecur.date()) > 365)
04880                     return;
04881                 firstTime = kdtRecur.time();
04882                 firstOffset = kdtRecur.utcOffset();
04883                 currentOffset = firstOffset;
04884                 firstDay = kdtRecur.date().dayOfWeek() - 1;
04885             }
04886             kdt = kdtRecur;
04887         }
04888 //kDebug()<<"-----exit loop: count="<<limit<<endl;
04889         return;   // too many iterations
04890     }
04891 
04892     if (repeatTimeVaries)
04893     {
04894         /* There's a sub-repetition which occurs at different times of
04895          * day, inside a recurrence which occurs at the same time of day.
04896          * We potentially need to check recurrences starting on each day.
04897          * Then, it is still possible that a working time sub-repetition
04898          * could occur immediately after a seasonal time change.
04899          */
04900         // Find the previous recurrence (as opposed to sub-repetition)
04901         const int repeatFreq = mRepetition.intervalSeconds();
04902         previousOccurrence(kdt.addSecs(1), newdt, false);
04903         if (!newdt.isValid())
04904             return;   // this should never happen
04905         KDateTime kdtRecur = newdt.effectiveKDateTime();
04906         const bool recurDuringWork = (kdtRecur.time() >= mWorkDayStart  &&  kdtRecur.time() < mWorkDayEnd);
04907 
04908         // Use the previous recurrence as a base for checking whether
04909         // our tests have wrapped round to the same time/day of week.
04910         const bool subdaily = (repeatFreq < 24*3600);
04911         unsigned days = 0;
04912         bool checkTimeChangeOnly = false;
04913         int transitionIndex = -1;
04914         for (int limit = 10;  --limit >= 0;  )
04915         {
04916             // Check the next seasonal time change (for an arbitrary 10 times,
04917             // even though that might not guarantee the correct result)
04918             QDate dateRecur = kdtRecur.date();
04919             int dayRecur = dateRecur.dayOfWeek() - 1;   // Monday = 0
04920             int repeatNum = kdtRecur.secsTo(kdt) / repeatFreq;
04921             kdt = kdtRecur.addSecs(repeatNum * repeatFreq);
04922 
04923             // Find the next recurrence, which sets the limit on possible sub-repetitions.
04924             // Note that for a monthly recurrence, for example, a sub-repetition could
04925             // be defined which is longer than the recurrence interval in short months.
04926             // In these cases, the sub-repetition is truncated by the following
04927             // recurrence.
04928             nextOccurrence(kdtRecur, newdt, IGNORE_REPETITION);
04929             KDateTime kdtNextRecur = newdt.effectiveKDateTime();
04930 
04931             int repeatsToCheck = mRepetition.count();
04932             int repeatsDuringWork = 0;  // 0=unknown, 1=does, -1=never
04933             for ( ; ; )
04934             {
04935                 // Check the sub-repetitions for this recurrence
04936                 if (repeatsDuringWork >= 0)
04937                 {
04938                     for ( ; ; )
04939                     {
04940                         // Find the repeat count to the next start of the working day
04941                         int inc = subdaily ? nextWorkRepetition(kdt) : 1;
04942                         repeatNum += inc;
04943                         const bool pastEnd = (repeatNum > mRepetition.count());
04944                         if (pastEnd)
04945                             inc -= repeatNum - mRepetition.count();
04946                         repeatsToCheck -= inc;
04947                         kdt = kdt.addSecs(inc * repeatFreq);
04948                         if (kdtNextRecur.isValid()  &&  kdt >= kdtNextRecur)
04949                         {
04950                             // This sub-repetition is past the next recurrence,
04951                             // so start the check again from the next recurrence.
04952                             repeatsToCheck = mRepetition.count();
04953                             break;
04954                         }
04955                         if (pastEnd)
04956                             break;
04957                         const QTime t = kdt.time();
04958                         if (t >= mWorkDayStart  &&  t < mWorkDayEnd)
04959                         {
04960                             if (mWorkDays.testBit(kdt.date().dayOfWeek() - 1))
04961                             {
04962                                 mMainWorkTrigger = mAllWorkTrigger = kdt;
04963                                 return;
04964                             }
04965                             repeatsDuringWork = 1;
04966                         }
04967                         else if (!repeatsDuringWork  &&  repeatsToCheck <= 0)
04968                         {
04969                             // Sub-repetitions never occur during working hours
04970                             repeatsDuringWork = -1;
04971                             break;
04972                         }
04973                     }
04974                 }
04975                 repeatNum = 0;
04976                 if (repeatsDuringWork < 0  &&  !recurDuringWork)
04977                     break;   // it never occurs during working hours
04978 
04979                 // Check the next recurrence
04980                 if (!kdtNextRecur.isValid())
04981                     return;
04982                 if (checkTimeChangeOnly  ||  (days & allDaysMask) == allDaysMask)
04983                     break;  // found a recurrence on every possible day of the week!?!
04984                 kdtRecur = kdtNextRecur;
04985                 nextOccurrence(kdtRecur, newdt, IGNORE_REPETITION);
04986                 kdtNextRecur = newdt.effectiveKDateTime();
04987                 dateRecur = kdtRecur.date();
04988                 dayRecur = dateRecur.dayOfWeek() - 1;
04989                 if (recurDuringWork  &&  mWorkDays.testBit(dayRecur))
04990                 {
04991                     mMainWorkTrigger = kdtRecur;
04992                     mAllWorkTrigger  = kdtRecur.addSecs(-60 * reminder);
04993                     return;
04994                 }
04995                 days |= 1 << dayRecur;
04996                 kdt = kdtRecur;
04997             }
04998 
04999             // Find the next recurrence before a seasonal time change,
05000             // and ensure the time change is after the last one processed.
05001             checkTimeChangeOnly = true;
05002             const int i = tz.transitionIndex(kdtRecur.toUtc().dateTime());
05003             if (i < 0)
05004                 return;
05005             if (i > transitionIndex)
05006                 transitionIndex = i;
05007             if (++transitionIndex >= static_cast<int>(tzTransitions.count()))
05008                 return;
05009             kdt = KDateTime(tzTransitions[transitionIndex].time(), KDateTime::UTC);
05010             previousOccurrence(kdt, newdt, IGNORE_REPETITION);
05011             kdtRecur = newdt.effectiveKDateTime();
05012         }
05013         return;  // not found - give up
05014     }
05015 }
05016 
05017 /******************************************************************************
05018 * Find the repeat count to the next start of a working day.
05019 * This allows for possible daylight saving time changes during the repetition.
05020 * Use for repetitions which occur at different times of day.
05021 */
05022 int KAEvent::Private::nextWorkRepetition(const KDateTime& pre) const
05023 {
05024     KDateTime nextWork(pre);
05025     if (pre.time() < mWorkDayStart)
05026         nextWork.setTime(mWorkDayStart);
05027     else
05028     {
05029         const int preDay = pre.date().dayOfWeek() - 1;   // Monday = 0
05030         for (int n = 1;  ;  ++n)
05031         {
05032             if (n >= 7)
05033                 return mRepetition.count() + 1;  // should never happen
05034             if (mWorkDays.testBit((preDay + n) % 7))
05035             {
05036                 nextWork = nextWork.addDays(n);
05037                 nextWork.setTime(mWorkDayStart);
05038                 break;
05039             }
05040         }
05041     }
05042     return (pre.secsTo(nextWork) - 1) / mRepetition.intervalSeconds() + 1;
05043 }
05044 
05045 /******************************************************************************
05046 * Check whether an alarm which recurs at the same time of day can possibly
05047 * occur during working hours.
05048 * This does not determine whether it actually does, but rather whether it could
05049 * potentially given enough repetitions.
05050 * Reply = false if it can never occur during working hours, true if it might.
05051 */
05052 bool KAEvent::Private::mayOccurDailyDuringWork(const KDateTime& kdt) const
05053 {
05054     if (!kdt.isDateOnly()
05055     &&  (kdt.time() < mWorkDayStart || kdt.time() >= mWorkDayEnd))
05056         return false;   // its time is outside working hours
05057     // Check if it always occurs on the same day of the week
05058     const Duration interval = mRecurrence->regularInterval();
05059     if (interval  &&  interval.isDaily()  &&  !(interval.asDays() % 7))
05060     {
05061         // It recurs weekly
05062         if (!mRepetition  ||  (mRepetition.isDaily() && !(mRepetition.intervalDays() % 7)))
05063             return false;   // any repetitions are also weekly
05064         // Repetitions are daily. Check if any occur on working days
05065         // by checking the first recurrence and up to 6 repetitions.
05066         int day = mRecurrence->startDateTime().date().dayOfWeek() - 1;   // Monday = 0
05067         const int repeatDays = mRepetition.intervalDays();
05068         const int maxRepeat = (mRepetition.count() < 6) ? mRepetition.count() : 6;
05069         for (int i = 0;  !mWorkDays.testBit(day);  ++i, day = (day + repeatDays) % 7)
05070         {
05071             if (i >= maxRepeat)
05072                 return false;  // no working day occurrences
05073         }
05074     }
05075     return true;
05076 }
05077 
05078 /******************************************************************************
05079 * Set the specified alarm to be an audio alarm with the given file name.
05080 */
05081 #ifndef USE_KRESOURCES
05082 void KAEvent::Private::setAudioAlarm(const Alarm::Ptr& alarm) const
05083 #else
05084 void KAEvent::Private::setAudioAlarm(Alarm* alarm) const
05085 #endif
05086 {
05087     alarm->setAudioAlarm(mAudioFile);  // empty for a beep or for speaking
05088     if (mSoundVolume >= 0)
05089         alarm->setCustomProperty(KACalendar::APPNAME, VOLUME_PROPERTY,
05090                       QString::fromLatin1("%1;%2;%3;%4").arg(QString::number(mSoundVolume, 'f', 2))
05091                                                      .arg(QString::number(mFadeVolume, 'f', 2))
05092                                                      .arg(mFadeSeconds));
05093 }
05094 
05095 /******************************************************************************
05096 * Get the date/time of the next recurrence of the event, after the specified
05097 * date/time.
05098 * 'result' = date/time of next occurrence, or invalid date/time if none.
05099 */
05100 KAEvent::OccurType KAEvent::Private::nextRecurrence(const KDateTime& preDateTime, DateTime& result) const
05101 {
05102     const KDateTime recurStart = mRecurrence->startDateTime();
05103     KDateTime pre = preDateTime.toTimeSpec(mStartDateTime.timeSpec());
05104     if (mStartDateTime.isDateOnly()  &&  !pre.isDateOnly()  &&  pre.time() < DateTime::startOfDay())
05105     {
05106         pre = pre.addDays(-1);    // today's recurrence (if today recurs) is still to come
05107         pre.setTime(DateTime::startOfDay());
05108     }
05109     const KDateTime dt = mRecurrence->getNextDateTime(pre);
05110     result = dt;
05111     result.setDateOnly(mStartDateTime.isDateOnly());
05112     if (!dt.isValid())
05113         return NO_OCCURRENCE;
05114     if (dt == recurStart)
05115         return FIRST_OR_ONLY_OCCURRENCE;
05116     if (mRecurrence->duration() >= 0  &&  dt == mRecurrence->endDateTime())
05117         return LAST_RECURRENCE;
05118     return result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
05119 }
05120 
05121 /******************************************************************************
05122 * Validate the event's recurrence data, correcting any inconsistencies (which
05123 * should never occur!).
05124 * Reply = recurrence period type.
05125 */
05126 KARecurrence::Type KAEvent::Private::checkRecur() const
05127 {
05128     if (mRecurrence)
05129     {
05130         KARecurrence::Type type = mRecurrence->type();
05131         switch (type)
05132         {
05133             case KARecurrence::MINUTELY:     // hourly
05134             case KARecurrence::DAILY:        // daily
05135             case KARecurrence::WEEKLY:       // weekly on multiple days of week
05136             case KARecurrence::MONTHLY_DAY:  // monthly on multiple dates in month
05137             case KARecurrence::MONTHLY_POS:  // monthly on multiple nth day of week
05138             case KARecurrence::ANNUAL_DATE:  // annually on multiple months (day of month = start date)
05139             case KARecurrence::ANNUAL_POS:   // annually on multiple nth day of week in multiple months
05140                 return type;
05141             default:
05142                 if (mRecurrence)
05143                     const_cast<KAEvent::Private*>(this)->clearRecur();  // this shouldn't ever be necessary!!
05144                 break;
05145         }
05146     }
05147     if (mRepetition)    // can't have a repetition without a recurrence
05148         const_cast<KAEvent::Private*>(this)->clearRecur();  // this shouldn't ever be necessary!!
05149     return KARecurrence::NO_RECUR;
05150 }
05151 
05152 /******************************************************************************
05153 * If the calendar was written by a previous version of KAlarm, do any
05154 * necessary format conversions on the events to ensure that when the calendar
05155 * is saved, no information is lost or corrupted.
05156 * Reply = true if any conversions were done.
05157 */
05158 #ifndef USE_KRESOURCES
05159 bool KAEvent::convertKCalEvents(const Calendar::Ptr& calendar, int calendarVersion)
05160 #else
05161 bool KAEvent::convertKCalEvents(CalendarLocal& calendar, int calendarVersion)
05162 #endif
05163 {
05164     // KAlarm pre-0.9 codes held in the alarm's DESCRIPTION property
05165     static const QChar   SEPARATOR        = QLatin1Char(';');
05166     static const QChar   LATE_CANCEL_CODE = QLatin1Char('C');
05167     static const QChar   AT_LOGIN_CODE    = QLatin1Char('L');   // subsidiary alarm at every login
05168     static const QChar   DEFERRAL_CODE    = QLatin1Char('D');   // extra deferred alarm
05169     static const QString TEXT_PREFIX      = QLatin1String("TEXT:");
05170     static const QString FILE_PREFIX      = QLatin1String("FILE:");
05171     static const QString COMMAND_PREFIX   = QLatin1String("CMD:");
05172 
05173     // KAlarm pre-0.9.2 codes held in the event's CATEGORY property
05174     static const QString BEEP_CATEGORY    = QLatin1String("BEEP");
05175 
05176     // KAlarm pre-1.1.1 LATECANCEL category with no parameter
05177     static const QString LATE_CANCEL_CAT = QLatin1String("LATECANCEL");
05178 
05179     // KAlarm pre-1.3.0 TMPLDEFTIME category with no parameter
05180     static const QString TEMPL_DEF_TIME_CAT = QLatin1String("TMPLDEFTIME");
05181 
05182     // KAlarm pre-1.3.1 XTERM category
05183     static const QString EXEC_IN_XTERM_CAT  = QLatin1String("XTERM");
05184 
05185     // KAlarm pre-1.9.0 categories
05186     static const QString DATE_ONLY_CATEGORY        = QLatin1String("DATE");
05187     static const QString EMAIL_BCC_CATEGORY        = QLatin1String("BCC");
05188     static const QString CONFIRM_ACK_CATEGORY      = QLatin1String("ACKCONF");
05189     static const QString KORGANIZER_CATEGORY       = QLatin1String("KORG");
05190     static const QString DEFER_CATEGORY            = QLatin1String("DEFER;");
05191     static const QString ARCHIVE_CATEGORY          = QLatin1String("SAVE");
05192     static const QString ARCHIVE_CATEGORIES        = QLatin1String("SAVE:");
05193     static const QString LATE_CANCEL_CATEGORY      = QLatin1String("LATECANCEL;");
05194     static const QString AUTO_CLOSE_CATEGORY       = QLatin1String("LATECLOSE;");
05195     static const QString TEMPL_AFTER_TIME_CATEGORY = QLatin1String("TMPLAFTTIME;");
05196     static const QString KMAIL_SERNUM_CATEGORY     = QLatin1String("KMAIL:");
05197     static const QString LOG_CATEGORY              = QLatin1String("LOG:");
05198 
05199     // KAlarm pre-1.5.0/1.9.9 properties
05200     static const QByteArray KMAIL_ID_PROPERTY("KMAILID");    // X-KDE-KALARM-KMAILID property
05201 
05202     // KAlarm pre-2.6.0 properties
05203     static const QByteArray ARCHIVE_PROPERTY("ARCHIVE");     // X-KDE-KALARM-ARCHIVE property
05204     static const QString ARCHIVE_REMINDER_ONCE_TYPE = QLatin1String("ONCE");
05205     static const QString REMINDER_ONCE_TYPE         = QLatin1String("REMINDER_ONCE");
05206     static const QByteArray EMAIL_ID_PROPERTY("EMAILID");         // X-KDE-KALARM-EMAILID property
05207     static const QByteArray SPEAK_PROPERTY("SPEAK");              // X-KDE-KALARM-SPEAK property
05208     static const QByteArray CANCEL_ON_ERROR_PROPERTY("ERRCANCEL");// X-KDE-KALARM-ERRCANCEL property
05209     static const QByteArray DONT_SHOW_ERROR_PROPERTY("ERRNOSHOW");// X-KDE-KALARM-ERRNOSHOW property
05210 
05211     bool adjustSummerTime = false;
05212     if (calendarVersion == -Version(0,5,7))
05213     {
05214         // The calendar file was written by the KDE 3.0.0 version of KAlarm 0.5.7.
05215         // Summer time was ignored when converting to UTC.
05216         calendarVersion = -calendarVersion;
05217         adjustSummerTime = true;
05218     }
05219 
05220     if (calendarVersion >= currentCalendarVersion())
05221         return false;
05222 
05223     kDebug() << "Adjusting version" << calendarVersion;
05224     const bool pre_0_7    = (calendarVersion < Version(0,7,0));
05225     const bool pre_0_9    = (calendarVersion < Version(0,9,0));
05226     const bool pre_0_9_2  = (calendarVersion < Version(0,9,2));
05227     const bool pre_1_1_1  = (calendarVersion < Version(1,1,1));
05228     const bool pre_1_2_1  = (calendarVersion < Version(1,2,1));
05229     const bool pre_1_3_0  = (calendarVersion < Version(1,3,0));
05230     const bool pre_1_3_1  = (calendarVersion < Version(1,3,1));
05231     const bool pre_1_4_14 = (calendarVersion < Version(1,4,14));
05232     const bool pre_1_5_0  = (calendarVersion < Version(1,5,0));
05233     const bool pre_1_9_0  = (calendarVersion < Version(1,9,0));
05234     const bool pre_1_9_2  = (calendarVersion < Version(1,9,2));
05235     const bool pre_1_9_7  = (calendarVersion < Version(1,9,7));
05236     const bool pre_1_9_9  = (calendarVersion < Version(1,9,9));
05237     const bool pre_1_9_10 = (calendarVersion < Version(1,9,10));
05238     const bool pre_2_2_9  = (calendarVersion < Version(2,2,9));
05239     const bool pre_2_3_0  = (calendarVersion < Version(2,3,0));
05240     const bool pre_2_3_2  = (calendarVersion < Version(2,3,2));
05241     const bool pre_2_7_0  = (calendarVersion < Version(2,7,0));
05242     Q_ASSERT(currentCalendarVersion() == Version(2,7,0));
05243 
05244     KTimeZone localZone;
05245     if (pre_1_9_2)
05246         localZone = KSystemTimeZones::local();
05247 
05248     bool converted = false;
05249 #ifndef USE_KRESOURCES
05250     const Event::List events = calendar->rawEvents();
05251 #else
05252     const Event::List events = calendar.rawEvents();
05253 #endif
05254     for (int ei = 0, eend = events.count();  ei < eend;  ++ei)
05255     {
05256 #ifndef USE_KRESOURCES
05257         Event::Ptr event = events[ei];
05258 #else
05259         Event* event = events[ei];
05260 #endif
05261         const Alarm::List alarms = event->alarms();
05262         if (alarms.isEmpty())
05263             continue;    // KAlarm isn't interested in events without alarms
05264         event->startUpdates();   // prevent multiple update notifications
05265         const bool readOnly = event->isReadOnly();
05266         if (readOnly)
05267             event->setReadOnly(false);
05268         QStringList cats = event->categories();
05269         bool addLateCancel = false;
05270         QStringList flags;
05271 
05272         if (pre_0_7  &&  event->allDay())
05273         {
05274             // It's a KAlarm pre-0.7 calendar file.
05275             // Ensure that when the calendar is saved, the alarm time isn't lost.
05276             event->setAllDay(false);
05277         }
05278 
05279         if (pre_0_9)
05280         {
05281             /*
05282              * It's a KAlarm pre-0.9 calendar file.
05283              * All alarms were of type DISPLAY. Instead of the X-KDE-KALARM-TYPE
05284              * alarm property, characteristics were stored as a prefix to the
05285              * alarm DESCRIPTION property, as follows:
05286              *   SEQNO;[FLAGS];TYPE:TEXT
05287              * where
05288              *   SEQNO = sequence number of alarm within the event
05289              *   FLAGS = C for late-cancel, L for repeat-at-login, D for deferral
05290              *   TYPE = TEXT or FILE or CMD
05291              *   TEXT = message text, file name/URL or command
05292              */
05293             for (int ai = 0, aend = alarms.count();  ai < aend;  ++ai)
05294             {
05295 #ifndef USE_KRESOURCES
05296                 Alarm::Ptr alarm = alarms[ai];
05297 #else
05298                 Alarm* alarm = alarms[ai];
05299 #endif
05300                 bool atLogin    = false;
05301                 bool deferral   = false;
05302                 bool lateCancel = false;
05303                 KAAlarm::Action action = KAAlarm::MESSAGE;
05304                 QString txt = alarm->text();
05305                 const int length = txt.length();
05306                 int i = 0;
05307                 if (txt[0].isDigit())
05308                 {
05309                     while (++i < length  &&  txt[i].isDigit()) ;
05310                     if (i < length  &&  txt[i++] == SEPARATOR)
05311                     {
05312                         while (i < length)
05313                         {
05314                             const QChar ch = txt[i++];
05315                             if (ch == SEPARATOR)
05316                                 break;
05317                             if (ch == LATE_CANCEL_CODE)
05318                                 lateCancel = true;
05319                             else if (ch == AT_LOGIN_CODE)
05320                                 atLogin = true;
05321                             else if (ch == DEFERRAL_CODE)
05322                                 deferral = true;
05323                         }
05324                     }
05325                     else
05326                         i = 0;     // invalid prefix
05327                 }
05328                 if (txt.indexOf(TEXT_PREFIX, i) == i)
05329                     i += TEXT_PREFIX.length();
05330                 else if (txt.indexOf(FILE_PREFIX, i) == i)
05331                 {
05332                     action = KAAlarm::FILE;
05333                     i += FILE_PREFIX.length();
05334                 }
05335                 else if (txt.indexOf(COMMAND_PREFIX, i) == i)
05336                 {
05337                     action = KAAlarm::COMMAND;
05338                     i += COMMAND_PREFIX.length();
05339                 }
05340                 else
05341                     i = 0;
05342                 txt = txt.mid(i);
05343 
05344                 QStringList types;
05345                 switch (action)
05346                 {
05347                     case KAAlarm::FILE:
05348                         types += Private::FILE_TYPE;
05349                         // fall through to MESSAGE
05350                     case KAAlarm::MESSAGE:
05351                         alarm->setDisplayAlarm(txt);
05352                         break;
05353                     case KAAlarm::COMMAND:
05354                         setProcedureAlarm(alarm, txt);
05355                         break;
05356                     case KAAlarm::EMAIL:     // email alarms were introduced in KAlarm 0.9
05357                     case KAAlarm::AUDIO:     // audio alarms (with no display) were introduced in KAlarm 2.3.2
05358                         break;
05359                 }
05360                 if (atLogin)
05361                 {
05362                     types += Private::AT_LOGIN_TYPE;
05363                     lateCancel = false;
05364                 }
05365                 else if (deferral)
05366                     types += Private::TIME_DEFERRAL_TYPE;
05367                 if (lateCancel)
05368                     addLateCancel = true;
05369                 if (types.count() > 0)
05370                     alarm->setCustomProperty(KACalendar::APPNAME, Private::TYPE_PROPERTY, types.join(","));
05371 
05372                 if (pre_0_7  &&  alarm->repeatCount() > 0  &&  alarm->snoozeTime().value() > 0)
05373                 {
05374                     // It's a KAlarm pre-0.7 calendar file.
05375                     // Minutely recurrences were stored differently.
05376                     Recurrence* recur = event->recurrence();
05377                     if (recur  &&  recur->recurs())
05378                     {
05379                         recur->setMinutely(alarm->snoozeTime().asSeconds() / 60);
05380                         recur->setDuration(alarm->repeatCount() + 1);
05381                         alarm->setRepeatCount(0);
05382                         alarm->setSnoozeTime(0);
05383                     }
05384                 }
05385 
05386                 if (adjustSummerTime)
05387                 {
05388                     // The calendar file was written by the KDE 3.0.0 version of KAlarm 0.5.7.
05389                     // Summer time was ignored when converting to UTC.
05390                     KDateTime dt = alarm->time();
05391                     const time_t t = dt.toTime_t();
05392                     const struct tm* dtm = localtime(&t);
05393                     if (dtm->tm_isdst)
05394                     {
05395                         dt = dt.addSecs(-3600);
05396                         alarm->setTime(dt);
05397                     }
05398                 }
05399             }
05400         }
05401 
05402         if (pre_0_9_2)
05403         {
05404             /*
05405              * It's a KAlarm pre-0.9.2 calendar file.
05406              * For the archive calendar, set the CREATED time to the DTEND value.
05407              * Convert date-only DTSTART to date/time, and add category "DATE".
05408              * Set the DTEND time to the DTSTART time.
05409              * Convert all alarm times to DTSTART offsets.
05410              * For display alarms, convert the first unlabelled category to an
05411              * X-KDE-KALARM-FONTCOLOUR property.
05412              * Convert BEEP category into an audio alarm with no audio file.
05413              */
05414             if (CalEvent::status(event) == CalEvent::ARCHIVED)
05415                 event->setCreated(event->dtEnd());
05416             KDateTime start = event->dtStart();
05417             if (event->allDay())
05418             {
05419                 event->setAllDay(false);
05420                 start.setTime(QTime(0, 0));
05421                 flags += Private::DATE_ONLY_FLAG;
05422             }
05423             event->setHasEndDate(false);
05424 
05425             for (int ai = 0, aend = alarms.count();  ai < aend;  ++ai)
05426             {
05427 #ifndef USE_KRESOURCES
05428                 Alarm::Ptr alarm = alarms[ai];
05429 #else
05430                 Alarm* alarm = alarms[ai];
05431 #endif
05432                 alarm->setStartOffset(start.secsTo(alarm->time()));
05433             }
05434 
05435             if (!cats.isEmpty())
05436             {
05437                 for (int ai = 0, aend = alarms.count();  ai < aend;  ++ai)
05438                 {
05439 #ifndef USE_KRESOURCES
05440                     Alarm::Ptr alarm = alarms[ai];
05441 #else
05442                     Alarm* alarm = alarms[ai];
05443 #endif
05444                     if (alarm->type() == Alarm::Display)
05445                         alarm->setCustomProperty(KACalendar::APPNAME, Private::FONT_COLOUR_PROPERTY,
05446                                                  QString::fromLatin1("%1;;").arg(cats[0]));
05447                 }
05448                 cats.removeAt(0);
05449             }
05450 
05451             for (int i = 0, end = cats.count();  i < end;  ++i)
05452             {
05453                 if (cats[i] == BEEP_CATEGORY)
05454                 {
05455                     cats.removeAt(i);
05456 
05457 #ifndef USE_KRESOURCES
05458                     Alarm::Ptr alarm = event->newAlarm();
05459 #else
05460                     Alarm* alarm = event->newAlarm();
05461 #endif
05462                     alarm->setEnabled(true);
05463                     alarm->setAudioAlarm();
05464                     KDateTime dt = event->dtStart();    // default
05465 
05466                     // Parse and order the alarms to know which one's date/time to use
05467                     Private::AlarmMap alarmMap;
05468                     Private::readAlarms(event, &alarmMap);
05469                     Private::AlarmMap::ConstIterator it = alarmMap.constBegin();
05470                     if (it != alarmMap.constEnd())
05471                     {
05472                         dt = it.value().alarm->time();
05473                         break;
05474                     }
05475                     alarm->setStartOffset(start.secsTo(dt));
05476                     break;
05477                 }
05478             }
05479         }
05480 
05481         if (pre_1_1_1)
05482         {
05483             /*
05484              * It's a KAlarm pre-1.1.1 calendar file.
05485              * Convert simple LATECANCEL category to LATECANCEL:n where n = minutes late.
05486              */
05487             int i;
05488             while ((i = cats.indexOf(LATE_CANCEL_CAT)) >= 0)
05489             {
05490                 cats.removeAt(i);
05491                 addLateCancel = true;
05492             }
05493         }
05494 
05495         if (pre_1_2_1)
05496         {
05497             /*
05498              * It's a KAlarm pre-1.2.1 calendar file.
05499              * Convert email display alarms from translated to untranslated header prefixes.
05500              */
05501             for (int ai = 0, aend = alarms.count();  ai < aend;  ++ai)
05502             {
05503 #ifndef USE_KRESOURCES
05504                 Alarm::Ptr alarm = alarms[ai];
05505 #else
05506                 Alarm* alarm = alarms[ai];
05507 #endif
05508                 if (alarm->type() == Alarm::Display)
05509                 {
05510                     const QString oldtext = alarm->text();
05511                     const QString newtext = AlarmText::toCalendarText(oldtext);
05512                     if (oldtext != newtext)
05513                         alarm->setDisplayAlarm(newtext);
05514                 }
05515             }
05516         }
05517 
05518         if (pre_1_3_0)
05519         {
05520             /*
05521              * It's a KAlarm pre-1.3.0 calendar file.
05522              * Convert simple TMPLDEFTIME category to TMPLAFTTIME:n where n = minutes after.
05523              */
05524             int i;
05525             while ((i = cats.indexOf(TEMPL_DEF_TIME_CAT)) >= 0)
05526             {
05527                 cats.removeAt(i);
05528                 (flags += Private::TEMPL_AFTER_TIME_FLAG) += QLatin1String("0");
05529             }
05530         }
05531 
05532         if (pre_1_3_1)
05533         {
05534             /*
05535              * It's a KAlarm pre-1.3.1 calendar file.
05536              * Convert simple XTERM category to LOG:xterm:
05537              */
05538             int i;
05539             while ((i = cats.indexOf(EXEC_IN_XTERM_CAT)) >= 0)
05540             {
05541                 cats.removeAt(i);
05542                 event->setCustomProperty(KACalendar::APPNAME, Private::LOG_PROPERTY, Private::xtermURL);
05543             }
05544         }
05545 
05546         if (pre_1_9_0)
05547         {
05548             /*
05549              * It's a KAlarm pre-1.9 calendar file.
05550              * Add the X-KDE-KALARM-STATUS custom property.
05551              * Convert KAlarm categories to custom fields.
05552              */
05553             CalEvent::setStatus(event, CalEvent::status(event));
05554             for (int i = 0;  i < cats.count(); )
05555             {
05556                 QString cat = cats[i];
05557                 if (cat == DATE_ONLY_CATEGORY)
05558                     flags += Private::DATE_ONLY_FLAG;
05559                 else if (cat == CONFIRM_ACK_CATEGORY)
05560                     flags += Private::CONFIRM_ACK_FLAG;
05561                 else if (cat == EMAIL_BCC_CATEGORY)
05562                     flags += Private::EMAIL_BCC_FLAG;
05563                 else if (cat == KORGANIZER_CATEGORY)
05564                     flags += Private::KORGANIZER_FLAG;
05565                 else if (cat.startsWith(DEFER_CATEGORY))
05566                     (flags += Private::DEFER_FLAG) += cat.mid(DEFER_CATEGORY.length());
05567                 else if (cat.startsWith(TEMPL_AFTER_TIME_CATEGORY))
05568                     (flags += Private::TEMPL_AFTER_TIME_FLAG) += cat.mid(TEMPL_AFTER_TIME_CATEGORY.length());
05569                 else if (cat.startsWith(LATE_CANCEL_CATEGORY))
05570                     (flags += Private::LATE_CANCEL_FLAG) += cat.mid(LATE_CANCEL_CATEGORY.length());
05571                 else if (cat.startsWith(AUTO_CLOSE_CATEGORY))
05572                     (flags += Private::AUTO_CLOSE_FLAG) += cat.mid(AUTO_CLOSE_CATEGORY.length());
05573                 else if (cat.startsWith(KMAIL_SERNUM_CATEGORY))
05574                     (flags += Private::KMAIL_SERNUM_FLAG) += cat.mid(KMAIL_SERNUM_CATEGORY.length());
05575                 else if (cat == ARCHIVE_CATEGORY)
05576                     event->setCustomProperty(KACalendar::APPNAME, ARCHIVE_PROPERTY, QLatin1String("0"));
05577                 else if (cat.startsWith(ARCHIVE_CATEGORIES))
05578                     event->setCustomProperty(KACalendar::APPNAME, ARCHIVE_PROPERTY, cat.mid(ARCHIVE_CATEGORIES.length()));
05579                 else if (cat.startsWith(LOG_CATEGORY))
05580                     event->setCustomProperty(KACalendar::APPNAME, Private::LOG_PROPERTY, cat.mid(LOG_CATEGORY.length()));
05581                 else
05582                 {
05583                     ++i;   // Not a KAlarm category, so leave it
05584                     continue;
05585                 }
05586                 cats.removeAt(i);
05587             }
05588         }
05589 
05590         if (pre_1_9_2)
05591         {
05592             /*
05593              * It's a KAlarm pre-1.9.2 calendar file.
05594              * Convert from clock time to the local system time zone.
05595              */
05596             event->shiftTimes(KDateTime::ClockTime, localZone);
05597             converted = true;
05598         }
05599 
05600         if (addLateCancel)
05601             (flags += Private::LATE_CANCEL_FLAG) += QLatin1String("1");
05602         if (!flags.isEmpty())
05603             event->setCustomProperty(KACalendar::APPNAME, Private::FLAGS_PROPERTY, flags.join(Private::SC));
05604         event->setCategories(cats);
05605 
05606 
05607         if ((pre_1_4_14  ||  (pre_1_9_7 && !pre_1_9_0))
05608         &&  event->recurrence()  &&  event->recurrence()->recurs())
05609         {
05610             /*
05611              * It's a KAlarm pre-1.4.14 or KAlarm 1.9 series pre-1.9.7 calendar file.
05612              * For recurring events, convert the main alarm offset to an absolute
05613              * time in the X-KDE-KALARM-NEXTRECUR property, and set main alarm
05614              * offsets to zero, and convert deferral alarm offsets to be relative to
05615              * the next recurrence.
05616              */
05617             const QStringList flags = event->customProperty(KACalendar::APPNAME, Private::FLAGS_PROPERTY).split(Private::SC, QString::SkipEmptyParts);
05618             const bool dateOnly = flags.contains(Private::DATE_ONLY_FLAG);
05619             KDateTime startDateTime = event->dtStart();
05620             if (dateOnly)
05621                 startDateTime.setDateOnly(true);
05622             // Convert the main alarm and get the next main trigger time from it
05623             KDateTime nextMainDateTime;
05624             bool mainExpired = true;
05625             for (int i = 0, alend = alarms.count();  i < alend;  ++i)
05626             {
05627 #ifndef USE_KRESOURCES
05628                 Alarm::Ptr alarm = alarms[i];
05629 #else
05630                 Alarm* alarm = alarms[i];
05631 #endif
05632                 if (!alarm->hasStartOffset())
05633                     continue;
05634                 // Find whether the alarm triggers at the same time as the main
05635                 // alarm, in which case its offset needs to be set to 0. The
05636                 // following trigger with the main alarm:
05637                 //  - Additional audio alarm
05638                 //  - PRE_ACTION_TYPE
05639                 //  - POST_ACTION_TYPE
05640                 //  - DISPLAYING_TYPE
05641                 bool mainAlarm = true;
05642                 QString property = alarm->customProperty(KACalendar::APPNAME, Private::TYPE_PROPERTY);
05643                 QStringList types = property.split(QChar(','), QString::SkipEmptyParts);
05644                 for (int t = 0;  t < types.count();  ++t)
05645                 {
05646                     QString type = types[t];
05647                     if (type == Private::AT_LOGIN_TYPE
05648                     ||  type == Private::TIME_DEFERRAL_TYPE
05649                     ||  type == Private::DATE_DEFERRAL_TYPE
05650                     ||  type == Private::REMINDER_TYPE
05651                     ||  type == REMINDER_ONCE_TYPE)
05652                     {
05653                         mainAlarm = false;
05654                         break;
05655                     }
05656                 }
05657                 if (mainAlarm)
05658                 {
05659                     if (mainExpired)
05660                     {
05661                         // All main alarms are supposed to be at the same time, so
05662                         // don't readjust the event's time for subsequent main alarms.
05663                         mainExpired = false;
05664                         nextMainDateTime = alarm->time();
05665                         nextMainDateTime.setDateOnly(dateOnly);
05666                         nextMainDateTime = nextMainDateTime.toTimeSpec(startDateTime);
05667                         if (nextMainDateTime != startDateTime)
05668                         {
05669                             QDateTime dt = nextMainDateTime.dateTime();
05670                             event->setCustomProperty(KACalendar::APPNAME, Private::NEXT_RECUR_PROPERTY,
05671                                                      dt.toString(dateOnly ? "yyyyMMdd" : "yyyyMMddThhmmss"));
05672                         }
05673                     }
05674                     alarm->setStartOffset(0);
05675                     converted = true;
05676                 }
05677             }
05678             int adjustment;
05679             if (mainExpired)
05680             {
05681                 // It's an expired recurrence.
05682                 // Set the alarm offset relative to the first actual occurrence
05683                 // (taking account of possible exceptions).
05684                 KDateTime dt = event->recurrence()->getNextDateTime(startDateTime.addDays(-1));
05685                 dt.setDateOnly(dateOnly);
05686                 adjustment = startDateTime.secsTo(dt);
05687             }
05688             else
05689                 adjustment = startDateTime.secsTo(nextMainDateTime);
05690             if (adjustment)
05691             {
05692                 // Convert deferred alarms
05693                 for (int i = 0, alend = alarms.count();  i < alend;  ++i)
05694                 {
05695 #ifndef USE_KRESOURCES
05696                     Alarm::Ptr alarm = alarms[i];
05697 #else
05698                     Alarm* alarm = alarms[i];
05699 #endif
05700                     if (!alarm->hasStartOffset())
05701                         continue;
05702                     const QString property = alarm->customProperty(KACalendar::APPNAME, Private::TYPE_PROPERTY);
05703                     const QStringList types = property.split(QChar(','), QString::SkipEmptyParts);
05704                     for (int t = 0;  t < types.count();  ++t)
05705                     {
05706                         const QString type = types[t];
05707                         if (type == Private::TIME_DEFERRAL_TYPE
05708                         ||  type == Private::DATE_DEFERRAL_TYPE)
05709                         {
05710                             alarm->setStartOffset(alarm->startOffset().asSeconds() - adjustment);
05711                             converted = true;
05712                             break;
05713                         }
05714                     }
05715                 }
05716             }
05717         }
05718 
05719         if (pre_1_5_0  ||  (pre_1_9_9 && !pre_1_9_0))
05720         {
05721             /*
05722              * It's a KAlarm pre-1.5.0 or KAlarm 1.9 series pre-1.9.9 calendar file.
05723              * Convert email identity names to uoids.
05724              */
05725             for (int i = 0, alend = alarms.count();  i < alend;  ++i)
05726             {
05727 #ifndef USE_KRESOURCES
05728                 Alarm::Ptr alarm = alarms[i];
05729 #else
05730                 Alarm* alarm = alarms[i];
05731 #endif
05732                 const QString name = alarm->customProperty(KACalendar::APPNAME, KMAIL_ID_PROPERTY);
05733                 if (name.isEmpty())
05734                     continue;
05735                 const uint id = Identities::identityUoid(name);
05736                 if (id)
05737                     alarm->setCustomProperty(KACalendar::APPNAME, EMAIL_ID_PROPERTY, QString::number(id));
05738                 alarm->removeCustomProperty(KACalendar::APPNAME, KMAIL_ID_PROPERTY);
05739                 converted = true;
05740             }
05741         }
05742 
05743         if (pre_1_9_10)
05744         {
05745             /*
05746              * It's a KAlarm pre-1.9.10 calendar file.
05747              * Convert simple repetitions without a recurrence, to a recurrence.
05748              */
05749             if (Private::convertRepetition(event))
05750                 converted = true;
05751         }
05752 
05753         if (pre_2_2_9  ||  (pre_2_3_2 && !pre_2_3_0))
05754         {
05755             /*
05756              * It's a KAlarm pre-2.2.9 or KAlarm 2.3 series pre-2.3.2 calendar file.
05757              * Set the time in the calendar for all date-only alarms to 00:00.
05758              */
05759             if (Private::convertStartOfDay(event))
05760                 converted = true;
05761         }
05762 
05763         if (pre_2_7_0)
05764         {
05765             /*
05766              * It's a KAlarm pre-2.7.0 calendar file.
05767              * Archive and at-login flags were stored in event's ARCHIVE property when the main alarm had expired.
05768              * Reminder parameters were stored in event's ARCHIVE property when no reminder was pending.
05769              * Negative reminder periods (i.e. alarm offset > 0) were invalid, so convert to 0.
05770              * Now store reminder information in FLAGS property, whether reminder is pending or not.
05771              * Move EMAILID, SPEAK, ERRCANCEL and ERRNOSHOW alarm properties into new FLAGS property.
05772              */
05773             bool flagsValid = false;
05774             QStringList flags;
05775             QString reminder;
05776             bool    reminderOnce = false;
05777             const QString prop = event->customProperty(KACalendar::APPNAME, ARCHIVE_PROPERTY);
05778             if (!prop.isEmpty())
05779             {
05780                 // Convert the event's ARCHIVE property to parameters in the FLAGS property
05781                 flags = event->customProperty(KACalendar::APPNAME, Private::FLAGS_PROPERTY).split(Private::SC, QString::SkipEmptyParts);
05782                 flags << Private::ARCHIVE_FLAG;
05783                 flagsValid = true;
05784                 if (prop != QLatin1String("0"))   // "0" was a dummy parameter if no others were present
05785                 {
05786                     // It's the archive property containing a reminder time and/or repeat-at-login flag.
05787                     // This was present when no reminder/at-login alarm was pending.
05788                     const QStringList list = prop.split(Private::SC, QString::SkipEmptyParts);
05789                     for (int i = 0;  i < list.count();  ++i)
05790                     {
05791                         if (list[i] == Private::AT_LOGIN_TYPE)
05792                             flags << Private::AT_LOGIN_TYPE;
05793                         else if (list[i] == ARCHIVE_REMINDER_ONCE_TYPE)
05794                             reminderOnce = true;
05795                         else if (!list[i].isEmpty()  &&  !list[i].startsWith(QChar::fromLatin1('-')))
05796                             reminder = list[i];
05797                     }
05798                 }
05799                 event->setCustomProperty(KACalendar::APPNAME, Private::FLAGS_PROPERTY, flags.join(Private::SC));
05800                 event->removeCustomProperty(KACalendar::APPNAME, ARCHIVE_PROPERTY);
05801             }
05802 
05803             for (int i = 0, alend = alarms.count();  i < alend;  ++i)
05804             {
05805 #ifndef USE_KRESOURCES
05806                 Alarm::Ptr alarm = alarms[i];
05807 #else
05808                 Alarm* alarm = alarms[i];
05809 #endif
05810                 // Convert EMAILID, SPEAK, ERRCANCEL, ERRNOSHOW properties
05811                 QStringList flags;
05812                 QString property = alarm->customProperty(KACalendar::APPNAME, EMAIL_ID_PROPERTY);
05813                 if (!property.isEmpty())
05814                 {
05815                     flags << Private::EMAIL_ID_FLAG << property;
05816                     alarm->removeCustomProperty(KACalendar::APPNAME, EMAIL_ID_PROPERTY);
05817                 }
05818                 if (!alarm->customProperty(KACalendar::APPNAME, SPEAK_PROPERTY).isEmpty())
05819                 {
05820                     flags << Private::SPEAK_FLAG;
05821                     alarm->removeCustomProperty(KACalendar::APPNAME, SPEAK_PROPERTY);
05822                 }
05823                 if (!alarm->customProperty(KACalendar::APPNAME, CANCEL_ON_ERROR_PROPERTY).isEmpty())
05824                 {
05825                     flags << Private::CANCEL_ON_ERROR_FLAG;
05826                     alarm->removeCustomProperty(KACalendar::APPNAME, CANCEL_ON_ERROR_PROPERTY);
05827                 }
05828                 if (!alarm->customProperty(KACalendar::APPNAME, DONT_SHOW_ERROR_PROPERTY).isEmpty())
05829                 {
05830                     flags << Private::DONT_SHOW_ERROR_FLAG;
05831                     alarm->removeCustomProperty(KACalendar::APPNAME, DONT_SHOW_ERROR_PROPERTY);
05832                 }
05833                 if (!flags.isEmpty())
05834                     alarm->setCustomProperty(KACalendar::APPNAME, Private::FLAGS_PROPERTY, flags.join(Private::SC));
05835 
05836                 // Invalidate negative reminder periods in alarms
05837                 if (!alarm->hasStartOffset())
05838                     continue;
05839                 property = alarm->customProperty(KACalendar::APPNAME, Private::TYPE_PROPERTY);
05840                 QStringList types = property.split(QChar::fromLatin1(','), QString::SkipEmptyParts);
05841                 const int r = types.indexOf(REMINDER_ONCE_TYPE);
05842                 if (r >= 0)
05843                 {
05844                     // Move reminder-once indicator from the alarm to the event's FLAGS property
05845                     types[r] = Private::REMINDER_TYPE;
05846                     alarm->setCustomProperty(KACalendar::APPNAME, Private::TYPE_PROPERTY, types.join(QChar::fromLatin1(',')));
05847                     reminderOnce = true;
05848                 }
05849                 if (r >= 0  ||  types.contains(Private::REMINDER_TYPE))
05850                 {
05851                     // The alarm is a reminder alarm
05852                     const int offset = alarm->startOffset().asSeconds();
05853                     if (offset > 0)
05854                     {
05855                         alarm->setStartOffset(0);
05856                         converted = true;
05857                     }
05858                     else if (offset < 0)
05859                         reminder = reminderToString(offset / 60);
05860                 }
05861             }
05862             if (!reminder.isEmpty())
05863             {
05864                 // Write reminder parameters into the event's FLAGS property
05865                 if (!flagsValid)
05866                     flags = event->customProperty(KACalendar::APPNAME, Private::FLAGS_PROPERTY).split(Private::SC, QString::SkipEmptyParts);
05867                 if (flags.indexOf(Private::REMINDER_TYPE) < 0)
05868                 {
05869                     flags += Private::REMINDER_TYPE;
05870                     if (reminderOnce)
05871                         flags += Private::REMINDER_ONCE_FLAG;
05872                     flags += reminder;
05873                 }
05874             }
05875         }
05876 
05877         if (readOnly)
05878             event->setReadOnly(true);
05879         event->endUpdates();     // finally issue an update notification
05880     }
05881     return converted;
05882 }
05883 
05884 /******************************************************************************
05885 * Set the time for a date-only event to 00:00.
05886 * Reply = true if the event was updated.
05887 */
05888 #ifndef USE_KRESOURCES
05889 bool KAEvent::Private::convertStartOfDay(const Event::Ptr& event)
05890 #else
05891 bool KAEvent::Private::convertStartOfDay(Event* event)
05892 #endif
05893 {
05894     bool changed = false;
05895     const QTime midnight(0, 0);
05896     const QStringList flags = event->customProperty(KACalendar::APPNAME, Private::FLAGS_PROPERTY).split(Private::SC, QString::SkipEmptyParts);
05897     if (flags.indexOf(Private::DATE_ONLY_FLAG) >= 0)
05898     {
05899         // It's an untimed event, so fix it
05900         const KDateTime oldDt = event->dtStart();
05901         const int adjustment = oldDt.time().secsTo(midnight);
05902         if (adjustment)
05903         {
05904             event->setDtStart(KDateTime(oldDt.date(), midnight, oldDt.timeSpec()));
05905             int deferralOffset = 0;
05906             AlarmMap alarmMap;
05907             readAlarms(event, &alarmMap);
05908             for (AlarmMap::ConstIterator it = alarmMap.constBegin();  it != alarmMap.constEnd();  ++it)
05909             {
05910                 const AlarmData& data = it.value();
05911                 if (!data.alarm->hasStartOffset())
05912                     continue;
05913                 if (data.timedDeferral)
05914                 {
05915                     // Found a timed deferral alarm, so adjust the offset
05916                     deferralOffset = data.alarm->startOffset().asSeconds();
05917 #ifndef USE_KRESOURCES
05918                     const_cast<Alarm*>(data.alarm.data())->setStartOffset(deferralOffset - adjustment);
05919 #else
05920                     const_cast<Alarm*>(data.alarm)->setStartOffset(deferralOffset - adjustment);
05921 #endif
05922                 }
05923                 else if (data.type == AUDIO_ALARM
05924                 &&       data.alarm->startOffset().asSeconds() == deferralOffset)
05925                 {
05926                     // Audio alarm is set for the same time as the above deferral alarm
05927 #ifndef USE_KRESOURCES
05928                     const_cast<Alarm*>(data.alarm.data())->setStartOffset(deferralOffset - adjustment);
05929 #else
05930                     const_cast<Alarm*>(data.alarm)->setStartOffset(deferralOffset - adjustment);
05931 #endif
05932                 }
05933             }
05934             changed = true;
05935         }
05936     }
05937     else
05938     {
05939         // It's a timed event. Fix any untimed alarms.
05940         bool foundDeferral = false;
05941         int deferralOffset = 0;
05942         int newDeferralOffset = 0;
05943         DateTime start;
05944         const KDateTime nextMainDateTime = readDateTime(event, false, start).kDateTime();
05945         AlarmMap alarmMap;
05946         readAlarms(event, &alarmMap);
05947         for (AlarmMap::ConstIterator it = alarmMap.constBegin();  it != alarmMap.constEnd();  ++it)
05948         {
05949             const AlarmData& data = it.value();
05950             if (!data.alarm->hasStartOffset())
05951                 continue;
05952             if ((data.type & DEFERRED_ALARM)  &&  !data.timedDeferral)
05953             {
05954                 // Found a date-only deferral alarm, so adjust its time
05955                 KDateTime altime = data.alarm->startOffset().end(nextMainDateTime);
05956                 altime.setTime(midnight);
05957                 deferralOffset = data.alarm->startOffset().asSeconds();
05958                 newDeferralOffset = event->dtStart().secsTo(altime);
05959 #ifndef USE_KRESOURCES
05960                 const_cast<Alarm*>(data.alarm.data())->setStartOffset(newDeferralOffset);
05961 #else
05962                 const_cast<Alarm*>(data.alarm)->setStartOffset(newDeferralOffset);
05963 #endif
05964                 foundDeferral = true;
05965                 changed = true;
05966             }
05967             else if (foundDeferral
05968                  &&  data.type == AUDIO_ALARM
05969                  &&  data.alarm->startOffset().asSeconds() == deferralOffset)
05970             {
05971                 // Audio alarm is set for the same time as the above deferral alarm
05972 #ifndef USE_KRESOURCES
05973                 const_cast<Alarm*>(data.alarm.data())->setStartOffset(newDeferralOffset);
05974 #else
05975                 const_cast<Alarm*>(data.alarm)->setStartOffset(newDeferralOffset);
05976 #endif
05977                 changed = true;
05978             }
05979         }
05980     }
05981     return changed;
05982 }
05983 
05984 /******************************************************************************
05985 * Convert simple repetitions in an event without a recurrence, to a
05986 * recurrence. Repetitions which are an exact multiple of 24 hours are converted
05987 * to daily recurrences; else they are converted to minutely recurrences. Note
05988 * that daily and minutely recurrences produce different results when they span
05989 * a daylight saving time change.
05990 * Reply = true if any conversions were done.
05991 */
05992 #ifndef USE_KRESOURCES
05993 bool KAEvent::Private::convertRepetition(const Event::Ptr& event)
05994 #else
05995 bool KAEvent::Private::convertRepetition(Event* event)
05996 #endif
05997 {
05998     const Alarm::List alarms = event->alarms();
05999     if (alarms.isEmpty())
06000         return false;
06001     Recurrence* recur = event->recurrence();   // guaranteed to return non-null
06002     if (recur->recurs())
06003         return false;
06004     bool converted = false;
06005     const bool readOnly = event->isReadOnly();
06006     for (int ai = 0, aend = alarms.count();  ai < aend;  ++ai)
06007     {
06008 #ifndef USE_KRESOURCES
06009         Alarm::Ptr alarm = alarms[ai];
06010 #else
06011         Alarm* alarm = alarms[ai];
06012 #endif
06013         if (alarm->repeatCount() > 0  &&  alarm->snoozeTime().value() > 0)
06014         {
06015             if (!converted)
06016             {
06017                 event->startUpdates();   // prevent multiple update notifications
06018                 if (readOnly)
06019                     event->setReadOnly(false);
06020                 if ((alarm->snoozeTime().asSeconds() % (24*3600)) != 0)
06021                     recur->setMinutely(alarm->snoozeTime().asSeconds() / 60);
06022                 else
06023                     recur->setDaily(alarm->snoozeTime().asDays());
06024                 recur->setDuration(alarm->repeatCount() + 1);
06025                 converted = true;
06026             }
06027             alarm->setRepeatCount(0);
06028             alarm->setSnoozeTime(0);
06029         }
06030     }
06031     if (converted)
06032     {
06033         if (readOnly)
06034             event->setReadOnly(true);
06035         event->endUpdates();     // finally issue an update notification
06036     }
06037     return converted;
06038 }
06039 
06040 
06041 
06042 /*=============================================================================
06043 = Class KAAlarm
06044 = Corresponds to a single KCal::Alarm instance.
06045 =============================================================================*/
06046 
06047 KAAlarm::KAAlarm()
06048     : d(new Private)
06049 {
06050 }
06051 
06052 KAAlarm::Private::Private()
06053     : mType(INVALID_ALARM),
06054       mNextRepeat(0),
06055       mRepeatAtLogin(false),
06056       mDeferred(false)
06057 {
06058 }
06059 
06060 KAAlarm::KAAlarm(const KAAlarm& other)
06061     : d(new Private(*other.d))
06062 {
06063 }
06064 
06065 KAAlarm::~KAAlarm()
06066 {
06067     delete d;
06068 }
06069 
06070 KAAlarm& KAAlarm::operator=(const KAAlarm& other)
06071 {
06072     if (&other != this)
06073         *d = *other.d;
06074     return *this;
06075 }
06076 
06077 KAAlarm::Action KAAlarm::action() const
06078 {
06079     return d->mActionType;
06080 }
06081 
06082 bool KAAlarm::isValid() const
06083 {
06084     return d->mType != INVALID_ALARM;
06085 }
06086 
06087 KAAlarm::Type KAAlarm::type() const
06088 {
06089     return d->mType;
06090 }
06091 
06092 DateTime KAAlarm::dateTime(bool withRepeats) const
06093 {
06094     return (withRepeats && d->mNextRepeat && d->mRepetition)
06095          ? d->mRepetition.duration(d->mNextRepeat).end(d->mNextMainDateTime.kDateTime())
06096          : d->mNextMainDateTime;
06097 }
06098 
06099 QDate KAAlarm::date() const
06100 {
06101     return d->mNextMainDateTime.date();
06102 }
06103 
06104 QTime KAAlarm::time() const
06105 {
06106     return d->mNextMainDateTime.effectiveTime();
06107 }
06108 
06109 bool KAAlarm::repeatAtLogin() const
06110 {
06111     return d->mRepeatAtLogin;
06112 }
06113 
06114 bool KAAlarm::isReminder() const
06115 {
06116     return d->mType == REMINDER_ALARM;
06117 }
06118 
06119 bool KAAlarm::deferred() const
06120 {
06121     return d->mDeferred;
06122 }
06123 
06124 bool KAAlarm::timedDeferral() const
06125 {
06126     return d->mDeferred && d->mTimedDeferral;
06127 }
06128 
06129 void KAAlarm::setTime(const DateTime& dt)
06130 {
06131     d->mNextMainDateTime = dt;
06132 }
06133 
06134 void KAAlarm::setTime(const KDateTime& dt)
06135 {
06136     d->mNextMainDateTime = dt;
06137 }
06138 
06139 #ifdef KDE_NO_DEBUG_OUTPUT
06140 const char* KAAlarm::debugType(Type)  { return ""; }
06141 #else
06142 const char* KAAlarm::debugType(Type type)
06143 {
06144     switch (type)
06145     {
06146         case MAIN_ALARM:               return "MAIN";
06147         case REMINDER_ALARM:           return "REMINDER";
06148         case DEFERRED_ALARM:           return "DEFERRED";
06149         case DEFERRED_REMINDER_ALARM:  return "DEFERRED_REMINDER";
06150         case AT_LOGIN_ALARM:           return "LOGIN";
06151         case DISPLAYING_ALARM:         return "DISPLAYING";
06152         default:                       return "INVALID";
06153     }
06154 }
06155 #endif
06156 
06157 
06158 /*=============================================================================
06159 = Class EmailAddressList
06160 =============================================================================*/
06161 
06162 /******************************************************************************
06163 * Sets the list of email addresses, removing any empty addresses.
06164 * Reply = false if empty addresses were found.
06165 */
06166 #ifndef USE_KRESOURCES
06167 EmailAddressList& EmailAddressList::operator=(const Person::List& addresses)
06168 #else
06169 EmailAddressList& EmailAddressList::operator=(const QList<Person>& addresses)
06170 #endif
06171 {
06172     clear();
06173     for (int p = 0, end = addresses.count();  p < end;  ++p)
06174     {
06175 #ifndef USE_KRESOURCES
06176         if (!addresses[p]->email().isEmpty())
06177 #else
06178         if (!addresses[p].email().isEmpty())
06179 #endif
06180             append(addresses[p]);
06181     }
06182     return *this;
06183 }
06184 
06185 /******************************************************************************
06186 * Return the email address list as a string list of email addresses.
06187 */
06188 EmailAddressList::operator QStringList() const
06189 {
06190     QStringList list;
06191     for (int p = 0, end = count();  p < end;  ++p)
06192         list += address(p);
06193     return list;
06194 }
06195 
06196 /******************************************************************************
06197 * Return the email address list as a string, each address being delimited by
06198 * the specified separator string.
06199 */
06200 QString EmailAddressList::join(const QString& separator) const
06201 {
06202     QString result;
06203     bool first = true;
06204     for (int p = 0, end = count();  p < end;  ++p)
06205     {
06206         if (first)
06207             first = false;
06208         else
06209             result += separator;
06210         result += address(p);
06211     }
06212     return result;
06213 }
06214 
06215 /******************************************************************************
06216 * Convert one item into an email address, including name.
06217 */
06218 QString EmailAddressList::address(int index) const
06219 {
06220     if (index < 0  ||  index > count())
06221         return QString();
06222     QString result;
06223     bool quote = false;
06224 #ifndef USE_KRESOURCES
06225     const Person::Ptr person = (*this)[index];
06226     const QString name = person->name();
06227 #else
06228     const Person person = (*this)[index];
06229     const QString name = person.name();
06230 #endif
06231     if (!name.isEmpty())
06232     {
06233         // Need to enclose the name in quotes if it has any special characters
06234         for (int i = 0, len = name.length();  i < len;  ++i)
06235         {
06236             const QChar ch = name[i];
06237             if (!ch.isLetterOrNumber())
06238             {
06239                 quote = true;
06240                 result += '\"';
06241                 break;
06242             }
06243         }
06244 #ifndef USE_KRESOURCES
06245         result += (*this)[index]->name();
06246 #else
06247         result += (*this)[index].name();
06248 #endif
06249         result += (quote ? "\" <" : " <");
06250         quote = true;    // need angle brackets round email address
06251     }
06252 
06253 #ifndef USE_KRESOURCES
06254     result += person->email();
06255 #else
06256     result += person.email();
06257 #endif
06258     if (quote)
06259         result += '>';
06260     return result;
06261 }
06262 
06263 /******************************************************************************
06264 * Return a list of the pure email addresses, excluding names.
06265 */
06266 QStringList EmailAddressList::pureAddresses() const
06267 {
06268     QStringList list;
06269     for (int p = 0, end = count();  p < end;  ++p)
06270 #ifndef USE_KRESOURCES
06271         list += at(p)->email();
06272 #else
06273         list += at(p).email();
06274 #endif
06275     return list;
06276 }
06277 
06278 /******************************************************************************
06279 * Return a list of the pure email addresses, excluding names, as a string.
06280 */
06281 QString EmailAddressList::pureAddresses(const QString& separator) const
06282 {
06283     QString result;
06284     bool first = true;
06285     for (int p = 0, end = count();  p < end;  ++p)
06286     {
06287         if (first)
06288             first = false;
06289         else
06290             result += separator;
06291 #ifndef USE_KRESOURCES
06292         result += at(p)->email();
06293 #else
06294         result += at(p).email();
06295 #endif
06296     }
06297     return result;
06298 }
06299 
06300 
06301 /*=============================================================================
06302 = Static functions
06303 =============================================================================*/
06304 
06305 /******************************************************************************
06306 * Set the specified alarm to be a procedure alarm with the given command line.
06307 * The command line is first split into its program file and arguments before
06308 * initialising the alarm.
06309 */
06310 #ifndef USE_KRESOURCES
06311 static void setProcedureAlarm(const Alarm::Ptr& alarm, const QString& commandLine)
06312 #else
06313 static void setProcedureAlarm(Alarm* alarm, const QString& commandLine)
06314 #endif
06315 {
06316     QString command;
06317     QString arguments;
06318     QChar quoteChar;
06319     bool quoted = false;
06320     const uint posMax = commandLine.length();
06321     uint pos;
06322     for (pos = 0;  pos < posMax;  ++pos)
06323     {
06324         const QChar ch = commandLine[pos];
06325         if (quoted)
06326         {
06327             if (ch == quoteChar)
06328             {
06329                 ++pos;    // omit the quote character
06330                 break;
06331             }
06332             command += ch;
06333         }
06334         else
06335         {
06336             bool done = false;
06337             switch (ch.toAscii())
06338             {
06339                 case ' ':
06340                 case ';':
06341                 case '|':
06342                 case '<':
06343                 case '>':
06344                     done = !command.isEmpty();
06345                     break;
06346                 case '\'':
06347                 case '"':
06348                     if (command.isEmpty())
06349                     {
06350                         // Start of a quoted string. Omit the quote character.
06351                         quoted = true;
06352                         quoteChar = ch;
06353                         break;
06354                     }
06355                     // fall through to default
06356                 default:
06357                     command += ch;
06358                     break;
06359             }
06360             if (done)
06361                 break;
06362         }
06363     }
06364 
06365     // Skip any spaces after the command
06366     for ( ;  pos < posMax  &&  commandLine[pos] == QLatin1Char(' ');  ++pos) ;
06367     arguments = commandLine.mid(pos);
06368 
06369     alarm->setProcedureAlarm(command, arguments);
06370 }
06371 
06372 /******************************************************************************
06373 * Converts a reminder interval into a parameter string for the
06374 * X-KDE-KALARM-FLAGS property.
06375 */
06376 QString reminderToString(int minutes)
06377 {
06378         char unit = 'M';
06379         int count = abs(minutes);
06380         if (count % 1440 == 0)
06381         {
06382             unit = 'D';
06383             count /= 1440;
06384         }
06385         else if (count % 60 == 0)
06386         {
06387             unit = 'H';
06388             count /= 60;
06389         }
06390         if (minutes < 0)
06391             count = -count;
06392         return QString("%1%2").arg(count).arg(unit);
06393 }
06394 
06395 } // namespace KAlarmCal
06396 
06397 // vim: et sw=4:

KAlarm Library

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal