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