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

KDE's Doxygen guidelines are available online.

KAlarm Library

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

kdepimlibs-4.8.3 API Reference

Skip menu "kdepimlibs-4.8.3 API Reference"
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal