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