/* * alarmevent.cpp - represents calendar alarms and events * Program: kalarm * Copyright © 2001-2009 by David Jarvie * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kalarm.h" #include #include #include #include #include #include #include #include "alarmtext.h" #include "functions.h" #include "kalarmapp.h" #include "kamail.h" #include "preferences.h" #include "alarmcalendar.h" #include "alarmevent.h" using namespace KCal; const TQCString APPNAME("KALARM"); // KAlarm version which first used the current calendar/event format. // If this changes, KAEvent::convertKCalEvents() must be changed correspondingly. // The string version is the KAlarm version string used in the calendar file. TQString KAEvent::calVersionString() { return TQString::fromLatin1("1.5.0"); } int KAEvent::calVersion() { return KAlarm::Version(1,5,0); } // Custom calendar properties. // Note that all custom property names are prefixed with X-TDE-KALARM- in the calendar file. // - Event properties static const TQCString NEXT_RECUR_PROPERTY("NEXTRECUR"); // X-TDE-KALARM-NEXTRECUR property static const TQCString REPEAT_PROPERTY("REPEAT"); // X-TDE-KALARM-REPEAT property // - General alarm properties static const TQCString TYPE_PROPERTY("TYPE"); // X-TDE-KALARM-TYPE property static const TQString FILE_TYPE = TQString::fromLatin1("FILE"); static const TQString AT_LOGIN_TYPE = TQString::fromLatin1("LOGIN"); static const TQString REMINDER_TYPE = TQString::fromLatin1("REMINDER"); static const TQString REMINDER_ONCE_TYPE = TQString::fromLatin1("REMINDER_ONCE"); static const TQString ARCHIVE_REMINDER_ONCE_TYPE = TQString::fromLatin1("ONCE"); static const TQString TIME_DEFERRAL_TYPE = TQString::fromLatin1("DEFERRAL"); static const TQString DATE_DEFERRAL_TYPE = TQString::fromLatin1("DATE_DEFERRAL"); static const TQString DISPLAYING_TYPE = TQString::fromLatin1("DISPLAYING"); // used only in displaying calendar static const TQString PRE_ACTION_TYPE = TQString::fromLatin1("PRE"); static const TQString POST_ACTION_TYPE = TQString::fromLatin1("POST"); static const TQCString NEXT_REPEAT_PROPERTY("NEXTREPEAT"); // X-TDE-KALARM-NEXTREPEAT property // - Display alarm properties static const TQCString FONT_COLOUR_PROPERTY("FONTCOLOR"); // X-TDE-KALARM-FONTCOLOR property // - Email alarm properties static const TQCString EMAIL_ID_PROPERTY("EMAILID"); // X-TDE-KALARM-EMAILID property // - Audio alarm properties static const TQCString VOLUME_PROPERTY("VOLUME"); // X-TDE-KALARM-VOLUME property static const TQCString SPEAK_PROPERTY("SPEAK"); // X-TDE-KALARM-SPEAK property // Event categories static const TQString DATE_ONLY_CATEGORY = TQString::fromLatin1("DATE"); static const TQString EMAIL_BCC_CATEGORY = TQString::fromLatin1("BCC"); static const TQString CONFIRM_ACK_CATEGORY = TQString::fromLatin1("ACKCONF"); static const TQString LATE_CANCEL_CATEGORY = TQString::fromLatin1("LATECANCEL;"); static const TQString AUTO_CLOSE_CATEGORY = TQString::fromLatin1("LATECLOSE;"); static const TQString TEMPL_AFTER_TIME_CATEGORY = TQString::fromLatin1("TMPLAFTTIME;"); static const TQString KMAIL_SERNUM_CATEGORY = TQString::fromLatin1("KMAIL:"); static const TQString KORGANIZER_CATEGORY = TQString::fromLatin1("KORG"); static const TQString DEFER_CATEGORY = TQString::fromLatin1("DEFER;"); static const TQString ARCHIVE_CATEGORY = TQString::fromLatin1("SAVE"); static const TQString ARCHIVE_CATEGORIES = TQString::fromLatin1("SAVE:"); static const TQString LOG_CATEGORY = TQString::fromLatin1("LOG:"); static const TQString xtermURL = TQString::fromLatin1("xterm:"); // Event status strings static const TQString DISABLED_STATUS = TQString::fromLatin1("DISABLED"); static const TQString EXPIRED_UID = TQString::fromLatin1("-exp-"); static const TQString DISPLAYING_UID = TQString::fromLatin1("-disp-"); static const TQString TEMPLATE_UID = TQString::fromLatin1("-tmpl-"); static const TQString KORGANIZER_UID = TQString::fromLatin1("-korg-"); struct AlarmData { const Alarm* alarm; TQString cleanText; // text or audio file name uint emailFromId; EmailAddressList emailAddresses; TQString emailSubject; TQStringList emailAttachments; TQFont font; TQColor bgColour, fgColour; float soundVolume; float fadeVolume; int fadeSeconds; int startOffsetSecs; bool speak; KAAlarm::SubType type; KAAlarmEventBase::Type action; int displayingFlags; bool defaultFont; bool reminderOnceOnly; bool isEmailText; bool commandScript; int repeatCount; int repeatInterval; int nextRepeat; }; typedef TQMap AlarmMap; static void setProcedureAlarm(Alarm*, const TQString& commandLine); /*============================================================================= = Class KAEvent = Corresponds to a KCal::Event instance. =============================================================================*/ inline void KAEvent::set_deferral(DeferType type) { if (type) { if (!mDeferral) ++mAlarmCount; } else { if (mDeferral) --mAlarmCount; } mDeferral = type; } inline void KAEvent::set_reminder(int minutes) { if (minutes && !mReminderMinutes) ++mAlarmCount; else if (!minutes && mReminderMinutes) --mAlarmCount; mReminderMinutes = minutes; mArchiveReminderMinutes = 0; } inline void KAEvent::set_archiveReminder() { if (mReminderMinutes) --mAlarmCount; mArchiveReminderMinutes = mReminderMinutes; mReminderMinutes = 0; } void KAEvent::copy(const KAEvent& event) { KAAlarmEventBase::copy(event); mTemplateName = event.mTemplateName; mAudioFile = event.mAudioFile; mPreAction = event.mPreAction; mPostAction = event.mPostAction; mStartDateTime = event.mStartDateTime; mSaveDateTime = event.mSaveDateTime; mAtLoginDateTime = event.mAtLoginDateTime; mDeferralTime = event.mDeferralTime; mDisplayingTime = event.mDisplayingTime; mDisplayingFlags = event.mDisplayingFlags; mReminderMinutes = event.mReminderMinutes; mArchiveReminderMinutes = event.mArchiveReminderMinutes; mDeferDefaultMinutes = event.mDeferDefaultMinutes; mRevision = event.mRevision; mAlarmCount = event.mAlarmCount; mDeferral = event.mDeferral; mLogFile = event.mLogFile; mCommandXterm = event.mCommandXterm; mKMailSerialNumber = event.mKMailSerialNumber; mCopyToKOrganizer = event.mCopyToKOrganizer; mReminderOnceOnly = event.mReminderOnceOnly; mMainExpired = event.mMainExpired; mArchiveRepeatAtLogin = event.mArchiveRepeatAtLogin; mArchive = event.mArchive; mTemplateAfterTime = event.mTemplateAfterTime; mEnabled = event.mEnabled; mUpdated = event.mUpdated; delete mRecurrence; if (event.mRecurrence) mRecurrence = new KARecurrence(*event.mRecurrence); else mRecurrence = 0; } /****************************************************************************** * Initialise the KAEvent from a KCal::Event. */ void KAEvent::set(const Event& event) { // Extract status from the event mEventID = event.uid(); mRevision = event.revision(); mTemplateName = TQString(); mLogFile = TQString(); mTemplateAfterTime = -1; mBeep = false; mSpeak = false; mEmailBcc = false; mCommandXterm = false; mCopyToKOrganizer = false; mConfirmAck = false; mArchive = false; mReminderOnceOnly = false; mAutoClose = false; mArchiveRepeatAtLogin = false; mArchiveReminderMinutes = 0; mDeferDefaultMinutes = 0; mLateCancel = 0; mKMailSerialNumber = 0; mBgColour = TQColor(255, 255, 255); // missing/invalid colour - return white background mFgColour = TQColor(0, 0, 0); // and black foreground mDefaultFont = true; mEnabled = true; clearRecur(); bool ok; bool dateOnly = false; const TQStringList cats = event.categories(); for (unsigned int i = 0; i < cats.count(); ++i) { if (cats[i] == DATE_ONLY_CATEGORY) dateOnly = true; else if (cats[i] == CONFIRM_ACK_CATEGORY) mConfirmAck = true; else if (cats[i] == EMAIL_BCC_CATEGORY) mEmailBcc = true; else if (cats[i] == ARCHIVE_CATEGORY) mArchive = true; else if (cats[i] == KORGANIZER_CATEGORY) mCopyToKOrganizer = true; else if (cats[i].startsWith(KMAIL_SERNUM_CATEGORY)) mKMailSerialNumber = cats[i].mid(KMAIL_SERNUM_CATEGORY.length()).toULong(); else if (cats[i].startsWith(LOG_CATEGORY)) { TQString logUrl = cats[i].mid(LOG_CATEGORY.length()); if (logUrl == xtermURL) mCommandXterm = true; else mLogFile = logUrl; } else if (cats[i].startsWith(ARCHIVE_CATEGORIES)) { // It's the archive flag plus a reminder time and/or repeat-at-login flag mArchive = true; TQStringList list = TQStringList::split(';', cats[i].mid(ARCHIVE_CATEGORIES.length())); for (unsigned int j = 0; j < list.count(); ++j) { if (list[j] == AT_LOGIN_TYPE) mArchiveRepeatAtLogin = true; else if (list[j] == ARCHIVE_REMINDER_ONCE_TYPE) mReminderOnceOnly = true; else { char ch; const char* cat = list[j].latin1(); while ((ch = *cat) != 0 && (ch < '0' || ch > '9')) ++cat; if (ch) { mArchiveReminderMinutes = ch - '0'; while ((ch = *++cat) >= '0' && ch <= '9') mArchiveReminderMinutes = mArchiveReminderMinutes * 10 + ch - '0'; switch (ch) { case 'M': break; case 'H': mArchiveReminderMinutes *= 60; break; case 'D': mArchiveReminderMinutes *= 1440; break; } } } } } else if (cats[i].startsWith(DEFER_CATEGORY)) { mDeferDefaultMinutes = static_cast(cats[i].mid(DEFER_CATEGORY.length()).toUInt(&ok)); if (!ok) mDeferDefaultMinutes = 0; // invalid parameter } else if (cats[i].startsWith(TEMPL_AFTER_TIME_CATEGORY)) { mTemplateAfterTime = static_cast(cats[i].mid(TEMPL_AFTER_TIME_CATEGORY.length()).toUInt(&ok)); if (!ok) mTemplateAfterTime = -1; // invalid parameter } else if (cats[i].startsWith(LATE_CANCEL_CATEGORY)) { mLateCancel = static_cast(cats[i].mid(LATE_CANCEL_CATEGORY.length()).toUInt(&ok)); if (!ok || !mLateCancel) mLateCancel = 1; // invalid parameter defaults to 1 minute } else if (cats[i].startsWith(AUTO_CLOSE_CATEGORY)) { mLateCancel = static_cast(cats[i].mid(AUTO_CLOSE_CATEGORY.length()).toUInt(&ok)); if (!ok || !mLateCancel) mLateCancel = 1; // invalid parameter defaults to 1 minute mAutoClose = true; } } TQString prop = event.customProperty(APPNAME, REPEAT_PROPERTY); if (!prop.isEmpty()) { // This property is used when the main alarm has expired TQStringList list = TQStringList::split(':', prop); if (list.count() >= 2) { int interval = static_cast(list[0].toUInt()); int count = static_cast(list[1].toUInt()); if (interval && count) { mRepeatInterval = interval; mRepeatCount = count; } } } mNextMainDateTime = readDateTime(event, dateOnly, mStartDateTime); mSaveDateTime = event.created(); if (uidStatus() == TEMPLATE) mTemplateName = event.summary(); if (event.statusStr() == DISABLED_STATUS) mEnabled = false; // Extract status from the event's alarms. // First set up defaults. mActionType = T_MESSAGE; mMainExpired = true; mRepeatAtLogin = false; mDisplaying = false; mRepeatSound = false; mCommandScript = false; mDeferral = NO_DEFERRAL; mSoundVolume = -1; mFadeVolume = -1; mFadeSeconds = 0; mReminderMinutes = 0; mEmailFromIdentity = 0; mText = ""; mAudioFile = ""; mPreAction = ""; mPostAction = ""; mEmailSubject = ""; mEmailAddresses.clear(); mEmailAttachments.clear(); // Extract data from all the event's alarms and index the alarms by sequence number AlarmMap alarmMap; readAlarms(event, &alarmMap); // Incorporate the alarms' details into the overall event mAlarmCount = 0; // initialise as invalid DateTime alTime; bool set = false; bool isEmailText = false; bool setDeferralTime = false; Duration deferralOffset; for (AlarmMap::ConstIterator it = alarmMap.begin(); it != alarmMap.end(); ++it) { const AlarmData& data = it.data(); DateTime dateTime = data.alarm->hasStartOffset() ? mNextMainDateTime.addSecs(data.alarm->startOffset().asSeconds()) : data.alarm->time(); switch (data.type) { case KAAlarm::MAIN__ALARM: mMainExpired = false; alTime = dateTime; alTime.setDateOnly(mStartDateTime.isDateOnly()); if (data.repeatCount && data.repeatInterval) { mRepeatInterval = data.repeatInterval; // values may be adjusted in setRecurrence() mRepeatCount = data.repeatCount; mNextRepeat = data.nextRepeat; } break; case KAAlarm::AT_LOGIN__ALARM: mRepeatAtLogin = true; mAtLoginDateTime = dateTime.rawDateTime(); alTime = mAtLoginDateTime; break; case KAAlarm::REMINDER__ALARM: mReminderMinutes = -(data.startOffsetSecs / 60); if (mReminderMinutes) mArchiveReminderMinutes = 0; break; case KAAlarm::DEFERRED_REMINDER_DATE__ALARM: case KAAlarm::DEFERRED_DATE__ALARM: mDeferral = (data.type == KAAlarm::DEFERRED_REMINDER_DATE__ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL; mDeferralTime = dateTime; mDeferralTime.setDateOnly(true); if (data.alarm->hasStartOffset()) deferralOffset = data.alarm->startOffset(); break; case KAAlarm::DEFERRED_REMINDER_TIME__ALARM: case KAAlarm::DEFERRED_TIME__ALARM: mDeferral = (data.type == KAAlarm::DEFERRED_REMINDER_TIME__ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL; mDeferralTime = dateTime; if (data.alarm->hasStartOffset()) deferralOffset = data.alarm->startOffset(); break; case KAAlarm::DISPLAYING__ALARM: { mDisplaying = true; mDisplayingFlags = data.displayingFlags; bool dateOnly = (mDisplayingFlags & DEFERRAL) ? !(mDisplayingFlags & TIMED_FLAG) : mStartDateTime.isDateOnly(); mDisplayingTime = dateTime; mDisplayingTime.setDateOnly(dateOnly); alTime = mDisplayingTime; break; } case KAAlarm::AUDIO__ALARM: mAudioFile = data.cleanText; mSpeak = data.speak && mAudioFile.isEmpty(); mBeep = !mSpeak && mAudioFile.isEmpty(); mSoundVolume = (!mBeep && !mSpeak) ? data.soundVolume : -1; mFadeVolume = (mSoundVolume >= 0 && data.fadeSeconds > 0) ? data.fadeVolume : -1; mFadeSeconds = (mFadeVolume >= 0) ? data.fadeSeconds : 0; mRepeatSound = (!mBeep && !mSpeak) && (data.repeatCount < 0); break; case KAAlarm::PRE_ACTION__ALARM: mPreAction = data.cleanText; break; case KAAlarm::POST_ACTION__ALARM: mPostAction = data.cleanText; break; case KAAlarm::INVALID__ALARM: default: break; } if (data.reminderOnceOnly) mReminderOnceOnly = true; bool noSetNextTime = false; switch (data.type) { case KAAlarm::DEFERRED_REMINDER_DATE__ALARM: case KAAlarm::DEFERRED_DATE__ALARM: case KAAlarm::DEFERRED_REMINDER_TIME__ALARM: case KAAlarm::DEFERRED_TIME__ALARM: if (!set) { // The recurrence has to be evaluated before we can // calculate the time of a deferral alarm. setDeferralTime = true; noSetNextTime = true; } // fall through to AT_LOGIN__ALARM etc. case KAAlarm::AT_LOGIN__ALARM: case KAAlarm::REMINDER__ALARM: case KAAlarm::DISPLAYING__ALARM: if (!set && !noSetNextTime) mNextMainDateTime = alTime; // fall through to MAIN__ALARM case KAAlarm::MAIN__ALARM: // Ensure that the basic fields are set up even if there is no main // alarm in the event (if it has expired and then been deferred) if (!set) { mActionType = data.action; mText = (mActionType == T_COMMAND) ? data.cleanText.stripWhiteSpace() : data.cleanText; switch (data.action) { case T_MESSAGE: mFont = data.font; mDefaultFont = data.defaultFont; if (data.isEmailText) isEmailText = true; // fall through to T_FILE case T_FILE: mBgColour = data.bgColour; mFgColour = data.fgColour; break; case T_COMMAND: mCommandScript = data.commandScript; break; case T_EMAIL: mEmailFromIdentity = data.emailFromId; mEmailAddresses = data.emailAddresses; mEmailSubject = data.emailSubject; mEmailAttachments = data.emailAttachments; break; default: break; } set = true; } if (data.action == T_FILE && mActionType == T_MESSAGE) mActionType = T_FILE; ++mAlarmCount; break; case KAAlarm::AUDIO__ALARM: case KAAlarm::PRE_ACTION__ALARM: case KAAlarm::POST_ACTION__ALARM: case KAAlarm::INVALID__ALARM: default: break; } } if (!isEmailText) mKMailSerialNumber = 0; if (mRepeatAtLogin) mArchiveRepeatAtLogin = false; Recurrence* recur = event.recurrence(); if (recur && recur->doesRecur()) { int nextRepeat = mNextRepeat; // setRecurrence() clears mNextRepeat setRecurrence(*recur); if (nextRepeat <= mRepeatCount) mNextRepeat = nextRepeat; } else checkRepetition(); if (mMainExpired && deferralOffset.asSeconds() && checkRecur() != KARecurrence::NO_RECUR) { // Adjust the deferral time for an expired recurrence, since the // offset is relative to the first actual occurrence. DateTime dt = mRecurrence->getNextDateTime(mStartDateTime.dateTime().addDays(-1)); dt.setDateOnly(mStartDateTime.isDateOnly()); if (mDeferralTime.isDateOnly()) { mDeferralTime = dt.addSecs(deferralOffset.asSeconds()); mDeferralTime.setDateOnly(true); } else mDeferralTime = deferralOffset.end(dt.dateTime()); } if (mDeferral) { if (mNextMainDateTime == mDeferralTime) mDeferral = CANCEL_DEFERRAL; // it's a cancelled deferral if (setDeferralTime) mNextMainDateTime = mDeferralTime; } mUpdated = false; } /****************************************************************************** * Fetch the start and next date/time for a KCal::Event. * Reply = next main date/time. */ DateTime KAEvent::readDateTime(const Event& event, bool dateOnly, DateTime& start) { start.set(event.dtStart(), dateOnly); DateTime next = start; TQString prop = event.customProperty(APPNAME, NEXT_RECUR_PROPERTY); if (prop.length() >= 8) { // The next due recurrence time is specified TQDate d(prop.left(4).toInt(), prop.mid(4,2).toInt(), prop.mid(6,2).toInt()); if (d.isValid()) { if (dateOnly && prop.length() == 8) next = d; else if (!dateOnly && prop.length() == 15 && prop[8] == TQChar('T')) { TQTime t(prop.mid(9,2).toInt(), prop.mid(11,2).toInt(), prop.mid(13,2).toInt()); if (t.isValid()) next = TQDateTime(d, t); } } } return next; } /****************************************************************************** * Parse the alarms for a KCal::Event. * Reply = map of alarm data, indexed by KAAlarm::Type */ void KAEvent::readAlarms(const Event& event, void* almap) { AlarmMap* alarmMap = (AlarmMap*)almap; Alarm::List alarms = event.alarms(); for (Alarm::List::ConstIterator it = alarms.begin(); it != alarms.end(); ++it) { // Parse the next alarm's text AlarmData data; readAlarm(**it, data); if (data.type != KAAlarm::INVALID__ALARM) alarmMap->insert(data.type, data); } } /****************************************************************************** * Parse a KCal::Alarm. * Reply = alarm ID (sequence number) */ void KAEvent::readAlarm(const Alarm& alarm, AlarmData& data) { // Parse the next alarm's text data.alarm = &alarm; data.startOffsetSecs = alarm.startOffset().asSeconds(); // can have start offset but no valid date/time (e.g. reminder in template) data.displayingFlags = 0; data.isEmailText = false; data.nextRepeat = 0; data.repeatInterval = alarm.snoozeTime(); data.repeatCount = alarm.repeatCount(); if (data.repeatCount) { bool ok; TQString property = alarm.customProperty(APPNAME, NEXT_REPEAT_PROPERTY); int n = static_cast(property.toUInt(&ok)); if (ok) data.nextRepeat = n; } switch (alarm.type()) { case Alarm::Procedure: data.action = T_COMMAND; data.cleanText = alarm.programFile(); data.commandScript = data.cleanText.isEmpty(); // blank command indicates a script if (!alarm.programArguments().isEmpty()) { if (!data.commandScript) data.cleanText += ' '; data.cleanText += alarm.programArguments(); } break; case Alarm::Email: data.action = T_EMAIL; data.emailFromId = alarm.customProperty(APPNAME, EMAIL_ID_PROPERTY).toUInt(); data.emailAddresses = alarm.mailAddresses(); data.emailSubject = alarm.mailSubject(); data.emailAttachments = alarm.mailAttachments(); data.cleanText = alarm.mailText(); break; case Alarm::Display: { data.action = T_MESSAGE; data.cleanText = AlarmText::fromCalendarText(alarm.text(), data.isEmailText); TQString property = alarm.customProperty(APPNAME, FONT_COLOUR_PROPERTY); TQStringList list = TQStringList::split(TQChar(';'), property, true); data.bgColour = TQColor(255, 255, 255); // white data.fgColour = TQColor(0, 0, 0); // black int n = list.count(); if (n > 0) { if (!list[0].isEmpty()) { TQColor c(list[0]); if (c.isValid()) data.bgColour = c; } if (n > 1 && !list[1].isEmpty()) { TQColor c(list[1]); if (c.isValid()) data.fgColour = c; } } data.defaultFont = (n <= 2 || list[2].isEmpty()); if (!data.defaultFont) data.font.fromString(list[2]); break; } case Alarm::Audio: { data.action = T_AUDIO; data.cleanText = alarm.audioFile(); data.type = KAAlarm::AUDIO__ALARM; data.soundVolume = -1; data.fadeVolume = -1; data.fadeSeconds = 0; data.speak = !alarm.customProperty(APPNAME, SPEAK_PROPERTY).isNull(); TQString property = alarm.customProperty(APPNAME, VOLUME_PROPERTY); if (!property.isEmpty()) { bool ok; float fadeVolume; int fadeSecs = 0; TQStringList list = TQStringList::split(TQChar(';'), property, true); data.soundVolume = list[0].toFloat(&ok); if (!ok) data.soundVolume = -1; if (data.soundVolume >= 0 && list.count() >= 3) { fadeVolume = list[1].toFloat(&ok); if (ok) fadeSecs = static_cast(list[2].toUInt(&ok)); if (ok && fadeVolume >= 0 && fadeSecs > 0) { data.fadeVolume = fadeVolume; data.fadeSeconds = fadeSecs; } } } return; } case Alarm::Invalid: data.type = KAAlarm::INVALID__ALARM; return; } bool atLogin = false; bool reminder = false; bool deferral = false; bool dateDeferral = false; data.reminderOnceOnly = false; data.type = KAAlarm::MAIN__ALARM; TQString property = alarm.customProperty(APPNAME, TYPE_PROPERTY); TQStringList types = TQStringList::split(TQChar(','), property); for (unsigned int i = 0; i < types.count(); ++i) { TQString type = types[i]; if (type == AT_LOGIN_TYPE) atLogin = true; else if (type == FILE_TYPE && data.action == T_MESSAGE) data.action = T_FILE; else if (type == REMINDER_TYPE) reminder = true; else if (type == REMINDER_ONCE_TYPE) reminder = data.reminderOnceOnly = true; else if (type == TIME_DEFERRAL_TYPE) deferral = true; else if (type == DATE_DEFERRAL_TYPE) dateDeferral = deferral = true; else if (type == DISPLAYING_TYPE) data.type = KAAlarm::DISPLAYING__ALARM; else if (type == PRE_ACTION_TYPE && data.action == T_COMMAND) data.type = KAAlarm::PRE_ACTION__ALARM; else if (type == POST_ACTION_TYPE && data.action == T_COMMAND) data.type = KAAlarm::POST_ACTION__ALARM; } if (reminder) { if (data.type == KAAlarm::MAIN__ALARM) data.type = dateDeferral ? KAAlarm::DEFERRED_REMINDER_DATE__ALARM : deferral ? KAAlarm::DEFERRED_REMINDER_TIME__ALARM : KAAlarm::REMINDER__ALARM; else if (data.type == KAAlarm::DISPLAYING__ALARM) data.displayingFlags = dateDeferral ? REMINDER | DATE_DEFERRAL : deferral ? REMINDER | TIME_DEFERRAL : REMINDER; } else if (deferral) { if (data.type == KAAlarm::MAIN__ALARM) data.type = dateDeferral ? KAAlarm::DEFERRED_DATE__ALARM : KAAlarm::DEFERRED_TIME__ALARM; else if (data.type == KAAlarm::DISPLAYING__ALARM) data.displayingFlags = dateDeferral ? DATE_DEFERRAL : TIME_DEFERRAL; } if (atLogin) { if (data.type == KAAlarm::MAIN__ALARM) data.type = KAAlarm::AT_LOGIN__ALARM; else if (data.type == KAAlarm::DISPLAYING__ALARM) data.displayingFlags = REPEAT_AT_LOGIN; } //kdDebug(5950)<<"ReadAlarm(): text="<= 0) { mFadeVolume = (fadeSeconds > 0) ? fadeVolume : -1; mFadeSeconds = (mFadeVolume >= 0) ? fadeSeconds : 0; } else { mFadeVolume = -1; mFadeSeconds = 0; } mUpdated = true; } void KAEvent::setReminder(int minutes, bool onceOnly) { if (minutes != mReminderMinutes) { set_reminder(minutes); mReminderOnceOnly = onceOnly; mUpdated = true; } } /****************************************************************************** * Return the time of the next scheduled occurrence of the event. * Reminders and deferred reminders can optionally be ignored. */ DateTime KAEvent::displayDateTime() const { DateTime dt = mainDateTime(true); if (mDeferral > 0 && mDeferral != REMINDER_DEFERRAL) { if (mMainExpired) return mDeferralTime; return TQMIN(mDeferralTime, dt); } return dt; } /****************************************************************************** * Convert a unique ID to indicate that the event is in a specified calendar file. */ TQString KAEvent::uid(const TQString& id, Status status) { TQString result = id; Status oldStatus; int i, len; if ((i = result.find(EXPIRED_UID)) > 0) { oldStatus = EXPIRED; len = EXPIRED_UID.length(); } else if ((i = result.find(DISPLAYING_UID)) > 0) { oldStatus = DISPLAYING; len = DISPLAYING_UID.length(); } else if ((i = result.find(TEMPLATE_UID)) > 0) { oldStatus = TEMPLATE; len = TEMPLATE_UID.length(); } else if ((i = result.find(KORGANIZER_UID)) > 0) { oldStatus = KORGANIZER; len = KORGANIZER_UID.length(); } else { oldStatus = ACTIVE; i = result.findRev('-'); len = 1; } if (status != oldStatus && i > 0) { TQString part; switch (status) { case ACTIVE: part = "-"; break; case EXPIRED: part = EXPIRED_UID; break; case DISPLAYING: part = DISPLAYING_UID; break; case TEMPLATE: part = TEMPLATE_UID; break; case KORGANIZER: part = KORGANIZER_UID; break; } result.replace(i, len, part); } return result; } /****************************************************************************** * Get the calendar type for a unique ID. */ KAEvent::Status KAEvent::uidStatus(const TQString& uid) { if (uid.find(EXPIRED_UID) > 0) return EXPIRED; if (uid.find(DISPLAYING_UID) > 0) return DISPLAYING; if (uid.find(TEMPLATE_UID) > 0) return TEMPLATE; if (uid.find(KORGANIZER_UID) > 0) return KORGANIZER; return ACTIVE; } int KAEvent::flags() const { return KAAlarmEventBase::flags() | (mStartDateTime.isDateOnly() ? ANY_TIME : 0) | (mDeferral > 0 ? DEFERRAL : 0) | (mCommandXterm ? EXEC_IN_XTERM : 0) | (mCopyToKOrganizer ? COPY_KORGANIZER : 0) | (mEnabled ? 0 : DISABLED); } /****************************************************************************** * Create a new Event from the KAEvent data. */ Event* KAEvent::event() const { KCal::Event* ev = new KCal::Event; ev->setUid(mEventID); updateKCalEvent(*ev, false); return ev; } /****************************************************************************** * Update an existing KCal::Event with the KAEvent data. * If 'original' is true, the event start date/time is adjusted to its original * value instead of its next occurrence, and the expired main alarm is * reinstated. */ bool KAEvent::updateKCalEvent(Event& ev, bool checkUid, bool original, bool cancelCancelledDefer) const { if ((checkUid && !mEventID.isEmpty() && mEventID != ev.uid()) || (!mAlarmCount && (!original || !mMainExpired))) return false; checkRecur(); // ensure recurrence/repetition data is consistent bool readOnly = ev.isReadOnly(); ev.setReadOnly(false); ev.setTransparency(Event::Transparent); // Set up event-specific data // Set up custom properties. ev.removeCustomProperty(APPNAME, NEXT_RECUR_PROPERTY); ev.removeCustomProperty(APPNAME, REPEAT_PROPERTY); TQStringList cats; if (mStartDateTime.isDateOnly()) cats.append(DATE_ONLY_CATEGORY); if (mConfirmAck) cats.append(CONFIRM_ACK_CATEGORY); if (mEmailBcc) cats.append(EMAIL_BCC_CATEGORY); if (mKMailSerialNumber) cats.append(TQString("%1%2").arg(KMAIL_SERNUM_CATEGORY).arg(mKMailSerialNumber)); if (mCopyToKOrganizer) cats.append(KORGANIZER_CATEGORY); if (mCommandXterm) cats.append(LOG_CATEGORY + xtermURL); else if (!mLogFile.isEmpty()) cats.append(LOG_CATEGORY + mLogFile); if (mLateCancel) cats.append(TQString("%1%2").arg(mAutoClose ? AUTO_CLOSE_CATEGORY : LATE_CANCEL_CATEGORY).arg(mLateCancel)); if (mDeferDefaultMinutes) cats.append(TQString("%1%2").arg(DEFER_CATEGORY).arg(mDeferDefaultMinutes)); if (!mTemplateName.isEmpty() && mTemplateAfterTime >= 0) cats.append(TQString("%1%2").arg(TEMPL_AFTER_TIME_CATEGORY).arg(mTemplateAfterTime)); if (mArchive && !original) { TQStringList params; if (mArchiveReminderMinutes) { if (mReminderOnceOnly) params += ARCHIVE_REMINDER_ONCE_TYPE; char unit = 'M'; int count = mArchiveReminderMinutes; if (count % 1440 == 0) { unit = 'D'; count /= 1440; } else if (count % 60 == 0) { unit = 'H'; count /= 60; } params += TQString("%1%2").arg(count).arg(unit); } if (mArchiveRepeatAtLogin) params += AT_LOGIN_TYPE; if (params.count() > 0) { TQString cat = ARCHIVE_CATEGORIES; cat += params.join(TQString::fromLatin1(";")); cats.append(cat); } else cats.append(ARCHIVE_CATEGORY); } ev.setCategories(cats); ev.setCustomStatus(mEnabled ? TQString() : DISABLED_STATUS); ev.setRevision(mRevision); ev.clearAlarms(); // Always set DTSTART as date/time, since alarm times can only be specified // in local time (instead of UTC) if they are relative to a DTSTART or DTEND // which is also specified in local time. Instead of calling setFloats() to // indicate a date-only event, the category "DATE" is included. ev.setDtStart(mStartDateTime.dateTime()); ev.setFloats(false); ev.setHasEndDate(false); DateTime dtMain = original ? mStartDateTime : mNextMainDateTime; int ancillaryType = 0; // 0 = invalid, 1 = time, 2 = offset DateTime ancillaryTime; // time for ancillary alarms (audio, pre-action, etc) int ancillaryOffset = 0; // start offset for ancillary alarms if (!mMainExpired || original) { /* The alarm offset must always be zero for the main alarm. To determine * which recurrence is due, the property X-TDE-KALARM_NEXTRECUR is used. * If the alarm offset was non-zero, exception dates and rules would not * work since they apply to the event time, not the alarm time. */ if (!original && checkRecur() != KARecurrence::NO_RECUR) { TQDateTime dt = mNextMainDateTime.dateTime(); ev.setCustomProperty(APPNAME, NEXT_RECUR_PROPERTY, dt.toString(mNextMainDateTime.isDateOnly() ? "yyyyMMdd" : "yyyyMMddThhmmss")); } // Add the main alarm initKCalAlarm(ev, 0, TQStringList(), KAAlarm::MAIN_ALARM); ancillaryOffset = 0; ancillaryType = dtMain.isValid() ? 2 : 0; } else if (mRepeatCount && mRepeatInterval) { // Alarm repetition is normally held in the main alarm, but since // the main alarm has expired, store in a custom property. TQString param = TQString("%1:%2").arg(mRepeatInterval).arg(mRepeatCount); ev.setCustomProperty(APPNAME, REPEAT_PROPERTY, param); } // Add subsidiary alarms if (mRepeatAtLogin || (mArchiveRepeatAtLogin && original)) { DateTime dtl; if (mArchiveRepeatAtLogin) dtl = mStartDateTime.dateTime().addDays(-1); else if (mAtLoginDateTime.isValid()) dtl = mAtLoginDateTime; else if (mStartDateTime.isDateOnly()) dtl = TQDate::currentDate().addDays(-1); else dtl = TQDateTime::currentDateTime(); initKCalAlarm(ev, dtl, AT_LOGIN_TYPE); if (!ancillaryType && dtl.isValid()) { ancillaryTime = dtl; ancillaryType = 1; } } if (mReminderMinutes || (mArchiveReminderMinutes && original)) { int minutes = mReminderMinutes ? mReminderMinutes : mArchiveReminderMinutes; initKCalAlarm(ev, -minutes * 60, TQStringList(mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE)); if (!ancillaryType) { ancillaryOffset = -minutes * 60; ancillaryType = 2; } } if (mDeferral > 0 || (mDeferral == CANCEL_DEFERRAL && !cancelCancelledDefer)) { DateTime nextDateTime = mNextMainDateTime; if (mMainExpired) { if (checkRecur() == KARecurrence::NO_RECUR) nextDateTime = mStartDateTime; else if (!original) { // It's a deferral of an expired recurrence. // Need to ensure that the alarm offset is to an occurrence // which isn't excluded by an exception - otherwise, it will // never be triggered. So choose the first recurrence which // isn't an exception. nextDateTime = mRecurrence->getNextDateTime(mStartDateTime.dateTime().addDays(-1)); nextDateTime.setDateOnly(mStartDateTime.isDateOnly()); } } int startOffset; TQStringList list; if (mDeferralTime.isDateOnly()) { startOffset = nextDateTime.secsTo(mDeferralTime.dateTime()); list += DATE_DEFERRAL_TYPE; } else { startOffset = nextDateTime.dateTime().secsTo(mDeferralTime.dateTime()); list += TIME_DEFERRAL_TYPE; } if (mDeferral == REMINDER_DEFERRAL) list += mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE; initKCalAlarm(ev, startOffset, list); if (!ancillaryType && mDeferralTime.isValid()) { ancillaryOffset = startOffset; ancillaryType = 2; } } if (!mTemplateName.isEmpty()) ev.setSummary(mTemplateName); else if (mDisplaying) { TQStringList list(DISPLAYING_TYPE); if (mDisplayingFlags & REPEAT_AT_LOGIN) list += AT_LOGIN_TYPE; else if (mDisplayingFlags & DEFERRAL) { if (mDisplayingFlags & TIMED_FLAG) list += TIME_DEFERRAL_TYPE; else list += DATE_DEFERRAL_TYPE; } if (mDisplayingFlags & REMINDER) list += mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE; initKCalAlarm(ev, mDisplayingTime, list); if (!ancillaryType && mDisplayingTime.isValid()) { ancillaryTime = mDisplayingTime; ancillaryType = 1; } } if (mBeep || mSpeak || !mAudioFile.isEmpty()) { // A sound is specified if (ancillaryType == 2) initKCalAlarm(ev, ancillaryOffset, TQStringList(), KAAlarm::AUDIO_ALARM); else initKCalAlarm(ev, ancillaryTime, TQStringList(), KAAlarm::AUDIO_ALARM); } if (!mPreAction.isEmpty()) { // A pre-display action is specified if (ancillaryType == 2) initKCalAlarm(ev, ancillaryOffset, TQStringList(PRE_ACTION_TYPE), KAAlarm::PRE_ACTION_ALARM); else initKCalAlarm(ev, ancillaryTime, TQStringList(PRE_ACTION_TYPE), KAAlarm::PRE_ACTION_ALARM); } if (!mPostAction.isEmpty()) { // A post-display action is specified if (ancillaryType == 2) initKCalAlarm(ev, ancillaryOffset, TQStringList(POST_ACTION_TYPE), KAAlarm::POST_ACTION_ALARM); else initKCalAlarm(ev, ancillaryTime, TQStringList(POST_ACTION_TYPE), KAAlarm::POST_ACTION_ALARM); } if (mRecurrence) mRecurrence->writeRecurrence(*ev.recurrence()); else ev.clearRecurrence(); if (mSaveDateTime.isValid()) ev.setCreated(mSaveDateTime); ev.setReadOnly(readOnly); return true; } /****************************************************************************** * Create a new alarm for a libkcal event, and initialise it according to the * alarm action. If 'types' is non-null, it is appended to the X-TDE-KALARM-TYPE * property value list. */ Alarm* KAEvent::initKCalAlarm(Event& event, const DateTime& dt, const TQStringList& types, KAAlarm::Type type) const { int startOffset = dt.isDateOnly() ? mStartDateTime.secsTo(dt) : mStartDateTime.dateTime().secsTo(dt.dateTime()); return initKCalAlarm(event, startOffset, types, type); } Alarm* KAEvent::initKCalAlarm(Event& event, int startOffsetSecs, const TQStringList& types, KAAlarm::Type type) const { TQStringList alltypes; Alarm* alarm = event.newAlarm(); alarm->setEnabled(true); if (type != KAAlarm::MAIN_ALARM) { // RFC2445 specifies that absolute alarm times must be stored as UTC. // So, in order to store local times, set the alarm time as an offset to DTSTART. alarm->setStartOffset(startOffsetSecs); } switch (type) { case KAAlarm::AUDIO_ALARM: alarm->setAudioAlarm(mAudioFile); // empty for a beep or for speaking if (mSpeak) alarm->setCustomProperty(APPNAME, SPEAK_PROPERTY, TQString::fromLatin1("Y")); if (mRepeatSound) { alarm->setRepeatCount(-1); alarm->setSnoozeTime(0); } if (!mAudioFile.isEmpty() && mSoundVolume >= 0) alarm->setCustomProperty(APPNAME, VOLUME_PROPERTY, TQString::fromLatin1("%1;%2;%3").arg(TQString::number(mSoundVolume, 'f', 2)) .arg(TQString::number(mFadeVolume, 'f', 2)) .arg(mFadeSeconds)); break; case KAAlarm::PRE_ACTION_ALARM: setProcedureAlarm(alarm, mPreAction); break; case KAAlarm::POST_ACTION_ALARM: setProcedureAlarm(alarm, mPostAction); break; case KAAlarm::MAIN_ALARM: alarm->setSnoozeTime(mRepeatInterval); alarm->setRepeatCount(mRepeatCount); if (mRepeatCount) alarm->setCustomProperty(APPNAME, NEXT_REPEAT_PROPERTY, TQString::number(mNextRepeat)); // fall through to INVALID_ALARM case KAAlarm::INVALID_ALARM: switch (mActionType) { case T_FILE: alltypes += FILE_TYPE; // fall through to T_MESSAGE case T_MESSAGE: alarm->setDisplayAlarm(AlarmText::toCalendarText(mText)); alarm->setCustomProperty(APPNAME, FONT_COLOUR_PROPERTY, TQString::fromLatin1("%1;%2;%3").arg(mBgColour.name()) .arg(mFgColour.name()) .arg(mDefaultFont ? TQString() : mFont.toString())); break; case T_COMMAND: if (mCommandScript) alarm->setProcedureAlarm("", mText); else setProcedureAlarm(alarm, mText); break; case T_EMAIL: alarm->setEmailAlarm(mEmailSubject, mText, mEmailAddresses, mEmailAttachments); if (mEmailFromIdentity) alarm->setCustomProperty(APPNAME, EMAIL_ID_PROPERTY, TQString::number(mEmailFromIdentity)); break; case T_AUDIO: break; } break; case KAAlarm::REMINDER_ALARM: case KAAlarm::DEFERRED_ALARM: case KAAlarm::DEFERRED_REMINDER_ALARM: case KAAlarm::AT_LOGIN_ALARM: case KAAlarm::DISPLAYING_ALARM: break; } alltypes += types; if (alltypes.count() > 0) alarm->setCustomProperty(APPNAME, TYPE_PROPERTY, alltypes.join(",")); return alarm; } /****************************************************************************** * Return the alarm of the specified type. */ KAAlarm KAEvent::alarm(KAAlarm::Type type) const { checkRecur(); // ensure recurrence/repetition data is consistent KAAlarm al; // this sets type to INVALID_ALARM if (mAlarmCount) { al.mEventID = mEventID; al.mActionType = mActionType; al.mText = mText; al.mBgColour = mBgColour; al.mFgColour = mFgColour; al.mFont = mFont; al.mDefaultFont = mDefaultFont; al.mBeep = mBeep; al.mSpeak = mSpeak; al.mSoundVolume = mSoundVolume; al.mFadeVolume = mFadeVolume; al.mFadeSeconds = mFadeSeconds; al.mRepeatSound = mRepeatSound; al.mConfirmAck = mConfirmAck; al.mRepeatCount = 0; al.mRepeatInterval = 0; al.mRepeatAtLogin = false; al.mDeferred = false; al.mLateCancel = mLateCancel; al.mAutoClose = mAutoClose; al.mEmailBcc = mEmailBcc; al.mCommandScript = mCommandScript; if (mActionType == T_EMAIL) { al.mEmailFromIdentity = mEmailFromIdentity; al.mEmailAddresses = mEmailAddresses; al.mEmailSubject = mEmailSubject; al.mEmailAttachments = mEmailAttachments; } switch (type) { case KAAlarm::MAIN_ALARM: if (!mMainExpired) { al.mType = KAAlarm::MAIN__ALARM; al.mNextMainDateTime = mNextMainDateTime; al.mRepeatCount = mRepeatCount; al.mRepeatInterval = mRepeatInterval; al.mNextRepeat = mNextRepeat; } break; case KAAlarm::REMINDER_ALARM: if (mReminderMinutes) { al.mType = KAAlarm::REMINDER__ALARM; if (mReminderOnceOnly) al.mNextMainDateTime = mStartDateTime.addMins(-mReminderMinutes); else al.mNextMainDateTime = mNextMainDateTime.addMins(-mReminderMinutes); } break; case KAAlarm::DEFERRED_REMINDER_ALARM: if (mDeferral != REMINDER_DEFERRAL) break; // fall through to DEFERRED_ALARM case KAAlarm::DEFERRED_ALARM: if (mDeferral > 0) { al.mType = static_cast((mDeferral == REMINDER_DEFERRAL ? KAAlarm::DEFERRED_REMINDER_ALARM : KAAlarm::DEFERRED_ALARM) | (mDeferralTime.isDateOnly() ? 0 : KAAlarm::TIMED_DEFERRAL_FLAG)); al.mNextMainDateTime = mDeferralTime; al.mDeferred = true; } break; case KAAlarm::AT_LOGIN_ALARM: if (mRepeatAtLogin) { al.mType = KAAlarm::AT_LOGIN__ALARM; al.mNextMainDateTime = mAtLoginDateTime; al.mRepeatAtLogin = true; al.mLateCancel = 0; al.mAutoClose = false; } break; case KAAlarm::DISPLAYING_ALARM: if (mDisplaying) { al.mType = KAAlarm::DISPLAYING__ALARM; al.mNextMainDateTime = mDisplayingTime; al.mDisplaying = true; } break; case KAAlarm::AUDIO_ALARM: case KAAlarm::PRE_ACTION_ALARM: case KAAlarm::POST_ACTION_ALARM: case KAAlarm::INVALID_ALARM: default: break; } } return al; } /****************************************************************************** * Return the main alarm for the event. * If the main alarm does not exist, one of the subsidiary ones is returned if * possible. * N.B. a repeat-at-login alarm can only be returned if it has been read from/ * written to the calendar file. */ KAAlarm KAEvent::firstAlarm() const { if (mAlarmCount) { if (!mMainExpired) return alarm(KAAlarm::MAIN_ALARM); return nextAlarm(KAAlarm::MAIN_ALARM); } return KAAlarm(); } /****************************************************************************** * Return the next alarm for the event, after the specified alarm. * N.B. a repeat-at-login alarm can only be returned if it has been read from/ * written to the calendar file. */ KAAlarm KAEvent::nextAlarm(KAAlarm::Type prevType) const { switch (prevType) { case KAAlarm::MAIN_ALARM: if (mReminderMinutes) return alarm(KAAlarm::REMINDER_ALARM); // fall through to REMINDER_ALARM case KAAlarm::REMINDER_ALARM: // There can only be one deferral alarm if (mDeferral == REMINDER_DEFERRAL) return alarm(KAAlarm::DEFERRED_REMINDER_ALARM); if (mDeferral == NORMAL_DEFERRAL) return alarm(KAAlarm::DEFERRED_ALARM); // fall through to DEFERRED_ALARM case KAAlarm::DEFERRED_REMINDER_ALARM: case KAAlarm::DEFERRED_ALARM: if (mRepeatAtLogin) return alarm(KAAlarm::AT_LOGIN_ALARM); // fall through to AT_LOGIN_ALARM case KAAlarm::AT_LOGIN_ALARM: if (mDisplaying) return alarm(KAAlarm::DISPLAYING_ALARM); // fall through to DISPLAYING_ALARM case KAAlarm::DISPLAYING_ALARM: // fall through to default case KAAlarm::AUDIO_ALARM: case KAAlarm::PRE_ACTION_ALARM: case KAAlarm::POST_ACTION_ALARM: case KAAlarm::INVALID_ALARM: default: break; } return KAAlarm(); } /****************************************************************************** * Remove the alarm of the specified type from the event. * This must only be called to remove an alarm which has expired, not to * reconfigure the event. */ void KAEvent::removeExpiredAlarm(KAAlarm::Type type) { int count = mAlarmCount; switch (type) { case KAAlarm::MAIN_ALARM: mAlarmCount = 0; // removing main alarm - also remove subsidiary alarms break; case KAAlarm::AT_LOGIN_ALARM: if (mRepeatAtLogin) { // Remove the at-login alarm, but keep a note of it for archiving purposes mArchiveRepeatAtLogin = true; mRepeatAtLogin = false; --mAlarmCount; } break; case KAAlarm::REMINDER_ALARM: // Remove any reminder alarm, but keep a note of it for archiving purposes set_archiveReminder(); break; case KAAlarm::DEFERRED_REMINDER_ALARM: case KAAlarm::DEFERRED_ALARM: set_deferral(NO_DEFERRAL); break; case KAAlarm::DISPLAYING_ALARM: if (mDisplaying) { mDisplaying = false; --mAlarmCount; } break; case KAAlarm::AUDIO_ALARM: case KAAlarm::PRE_ACTION_ALARM: case KAAlarm::POST_ACTION_ALARM: case KAAlarm::INVALID_ALARM: default: break; } if (mAlarmCount != count) mUpdated = true; } /****************************************************************************** * Defer the event to the specified time. * If the main alarm time has passed, the main alarm is marked as expired. * If 'adjustRecurrence' is true, ensure that the next scheduled recurrence is * after the current time. * Reply = true if a repetition has been deferred. */ bool KAEvent::defer(const DateTime& dateTime, bool reminder, bool adjustRecurrence) { bool result = false; bool setNextRepetition = false; bool checkRepetition = false; cancelCancelledDeferral(); if (checkRecur() == KARecurrence::NO_RECUR) { if (mReminderMinutes || mDeferral == REMINDER_DEFERRAL || mArchiveReminderMinutes) { if (dateTime < mNextMainDateTime.dateTime()) { set_deferral(REMINDER_DEFERRAL); // defer reminder alarm mDeferralTime = dateTime; } else { // Deferring past the main alarm time, so adjust any existing deferral if (mReminderMinutes || mDeferral == REMINDER_DEFERRAL) set_deferral(NO_DEFERRAL); } // Remove any reminder alarm, but keep a note of it for archiving purposes if (mReminderMinutes) set_archiveReminder(); } if (mDeferral != REMINDER_DEFERRAL) { // We're deferring the main alarm, not a reminder if (mRepeatCount && mRepeatInterval && dateTime < mainEndRepeatTime()) { // The alarm is repeated, and we're deferring to a time before the last repetition set_deferral(NORMAL_DEFERRAL); mDeferralTime = dateTime; result = true; setNextRepetition = true; } else { // Main alarm has now expired mNextMainDateTime = mDeferralTime = dateTime; set_deferral(NORMAL_DEFERRAL); if (!mMainExpired) { // Mark the alarm as expired now mMainExpired = true; --mAlarmCount; if (mRepeatAtLogin) { // Remove the repeat-at-login alarm, but keep a note of it for archiving purposes mArchiveRepeatAtLogin = true; mRepeatAtLogin = false; --mAlarmCount; } } } } } else if (reminder) { // Deferring a reminder for a recurring alarm if (dateTime >= mNextMainDateTime.dateTime()) set_deferral(NO_DEFERRAL); // (error) else { set_deferral(REMINDER_DEFERRAL); mDeferralTime = dateTime; checkRepetition = true; } } else { mDeferralTime = dateTime; if (mDeferral <= 0) set_deferral(NORMAL_DEFERRAL); if (adjustRecurrence) { TQDateTime now = TQDateTime::currentDateTime(); if (mainEndRepeatTime() < now) { // The last repetition (if any) of the current recurrence has already passed. // Adjust to the next scheduled recurrence after now. if (!mMainExpired && setNextOccurrence(now) == NO_OCCURRENCE) { mMainExpired = true; --mAlarmCount; } } else setNextRepetition = (mRepeatCount && mRepeatInterval); } else checkRepetition = true; } if (checkRepetition) setNextRepetition = (mRepeatCount && mRepeatInterval && mDeferralTime < mainEndRepeatTime()); if (setNextRepetition) { // The alarm is repeated, and we're deferring to a time before the last repetition. // Set the next scheduled repetition to the one after the deferral. mNextRepeat = (mNextMainDateTime < mDeferralTime) ? mNextMainDateTime.secsTo(mDeferralTime) / (mRepeatInterval * 60) + 1 : 0; } mUpdated = true; return result; } /****************************************************************************** * Cancel any deferral alarm. */ void KAEvent::cancelDefer() { if (mDeferral > 0) { // Set the deferral time to be the same as the next recurrence/repetition. // This prevents an immediate retriggering of the alarm. if (mMainExpired || nextOccurrence(TQDateTime::currentDateTime(), mDeferralTime, RETURN_REPETITION) == NO_OCCURRENCE) { // The main alarm has expired, so simply delete the deferral mDeferralTime = DateTime(); set_deferral(NO_DEFERRAL); } else set_deferral(CANCEL_DEFERRAL); mUpdated = true; } } /****************************************************************************** * Cancel any cancelled deferral alarm. */ void KAEvent::cancelCancelledDeferral() { if (mDeferral == CANCEL_DEFERRAL) { mDeferralTime = DateTime(); set_deferral(NO_DEFERRAL); } } /****************************************************************************** * Find the latest time which the alarm can currently be deferred to. */ DateTime KAEvent::deferralLimit(KAEvent::DeferLimitType* limitType) const { DeferLimitType ltype; DateTime endTime; bool recurs = (checkRecur() != KARecurrence::NO_RECUR); if (recurs || mRepeatCount) { // It's a repeated alarm. Don't allow it to be deferred past its // next occurrence or repetition. DateTime reminderTime; TQDateTime now = TQDateTime::currentDateTime(); OccurType type = nextOccurrence(now, endTime, RETURN_REPETITION); if (type & OCCURRENCE_REPEAT) ltype = LIMIT_REPETITION; else if (type == NO_OCCURRENCE) ltype = LIMIT_NONE; else if (mReminderMinutes && (now < (reminderTime = endTime.addMins(-mReminderMinutes)))) { endTime = reminderTime; ltype = LIMIT_REMINDER; } else if (type == FIRST_OR_ONLY_OCCURRENCE && !recurs) ltype = LIMIT_REPETITION; else ltype = LIMIT_RECURRENCE; } else if ((mReminderMinutes || mDeferral == REMINDER_DEFERRAL || mArchiveReminderMinutes) && TQDateTime::currentDateTime() < mNextMainDateTime.dateTime()) { // It's an reminder alarm. Don't allow it to be deferred past its main alarm time. endTime = mNextMainDateTime; ltype = LIMIT_REMINDER; } else ltype = LIMIT_NONE; if (ltype != LIMIT_NONE) endTime = endTime.addMins(-1); if (limitType) *limitType = ltype; return endTime; } /****************************************************************************** * Set the event to be a copy of the specified event, making the specified * alarm the 'displaying' alarm. * The purpose of setting up a 'displaying' alarm is to be able to reinstate * the alarm message in case of a crash, or to reinstate it should the user * choose to defer the alarm. Note that even repeat-at-login alarms need to be * saved in case their end time expires before the next login. * Reply = true if successful, false if alarm was not copied. */ bool KAEvent::setDisplaying(const KAEvent& event, KAAlarm::Type alarmType, const TQDateTime& repeatAtLoginTime) { if (!mDisplaying && (alarmType == KAAlarm::MAIN_ALARM || alarmType == KAAlarm::REMINDER_ALARM || alarmType == KAAlarm::DEFERRED_REMINDER_ALARM || alarmType == KAAlarm::DEFERRED_ALARM || alarmType == KAAlarm::AT_LOGIN_ALARM)) { //kdDebug(5950)<<"KAEvent::setDisplaying("<frequency(); switch (mRecurrence->defaultRRuleConst()->recurrenceType()) { case RecurrenceRule::rMinutely: if (frequency < 60) return i18n("1 Minute", "%n Minutes", frequency); else if (frequency % 60 == 0) return i18n("1 Hour", "%n Hours", frequency/60); else { TQString mins; return i18n("Hours and Minutes", "%1H %2M").arg(TQString::number(frequency/60)).arg(mins.sprintf("%02d", frequency%60)); } case RecurrenceRule::rDaily: return i18n("1 Day", "%n Days", frequency); case RecurrenceRule::rWeekly: return i18n("1 Week", "%n Weeks", frequency); case RecurrenceRule::rMonthly: return i18n("1 Month", "%n Months", frequency); case RecurrenceRule::rYearly: return i18n("1 Year", "%n Years", frequency); case RecurrenceRule::rNone: default: break; } } return brief ? TQString() : i18n("None"); } /****************************************************************************** * Return the repetition interval as text suitable for display. */ TQString KAEvent::repetitionText(bool brief) const { if (mRepeatCount) { if (mRepeatInterval % 1440) { if (mRepeatInterval < 60) return i18n("1 Minute", "%n Minutes", mRepeatInterval); if (mRepeatInterval % 60 == 0) return i18n("1 Hour", "%n Hours", mRepeatInterval/60); TQString mins; return i18n("Hours and Minutes", "%1H %2M").arg(TQString::number(mRepeatInterval/60)).arg(mins.sprintf("%02d", mRepeatInterval%60)); } if (mRepeatInterval % (7*1440)) return i18n("1 Day", "%n Days", mRepeatInterval/1440); return i18n("1 Week", "%n Weeks", mRepeatInterval/(7*1440)); } return brief ? TQString() : i18n("None"); } /****************************************************************************** * Adjust the event date/time to the first recurrence of the event, on or after * start date/time. The event start date may not be a recurrence date, in which * case a later date will be set. */ void KAEvent::setFirstRecurrence() { switch (checkRecur()) { case KARecurrence::NO_RECUR: case KARecurrence::MINUTELY: return; case KARecurrence::ANNUAL_DATE: case KARecurrence::ANNUAL_POS: if (mRecurrence->yearMonths().isEmpty()) return; // (presumably it's a template) break; case KARecurrence::DAILY: case KARecurrence::WEEKLY: case KARecurrence::MONTHLY_POS: case KARecurrence::MONTHLY_DAY: break; } TQDateTime recurStart = mRecurrence->startDateTime(); if (mRecurrence->recursOn(recurStart.date())) return; // it already recurs on the start date // Set the frequency to 1 to find the first possible occurrence int frequency = mRecurrence->frequency(); mRecurrence->setFrequency(1); DateTime next; nextRecurrence(mNextMainDateTime.dateTime(), next); if (!next.isValid()) mRecurrence->setStartDateTime(recurStart); // reinstate the old value else { mRecurrence->setStartDateTime(next.dateTime()); mStartDateTime = mNextMainDateTime = next; mUpdated = true; } mRecurrence->setFrequency(frequency); // restore the frequency } /****************************************************************************** * Initialise the event's recurrence from a KCal::Recurrence. * The event's start date/time is not changed. */ void KAEvent::setRecurrence(const KARecurrence& recurrence) { mUpdated = true; delete mRecurrence; if (recurrence.doesRecur()) { mRecurrence = new KARecurrence(recurrence); mRecurrence->setStartDateTime(mStartDateTime.dateTime()); mRecurrence->setFloats(mStartDateTime.isDateOnly()); } else mRecurrence = 0; // Adjust sub-repetition values to fit the recurrence setRepetition(mRepeatInterval, mRepeatCount); } /****************************************************************************** * Initialise the event's sub-repetition. * The repetition length is adjusted if necessary to fit any recurrence interval. * Reply = false if a non-daily interval was specified for a date-only recurrence. */ bool KAEvent::setRepetition(int interval, int count) { mUpdated = true; mRepeatInterval = 0; mRepeatCount = 0; mNextRepeat = 0; if (interval > 0 && count > 0 && !mRepeatAtLogin) { Q_ASSERT(checkRecur() != KARecurrence::NO_RECUR); if (interval % 1440 && mStartDateTime.isDateOnly()) return false; // interval must be in units of days for date-only alarms if (checkRecur() != KARecurrence::NO_RECUR) { int longestInterval = mRecurrence->longestInterval() - 1; if (interval * count > longestInterval) count = longestInterval / interval; } mRepeatInterval = interval; mRepeatCount = count; } return true; } /****************************************************************************** * Set the recurrence to recur at a minutes interval. * Parameters: * freq = how many minutes between recurrences. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date/time (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurMinutely(int freq, int count, const TQDateTime& end) { return setRecur(RecurrenceRule::rMinutely, freq, count, end); } /****************************************************************************** * Set the recurrence to recur daily. * Parameters: * freq = how many days between recurrences. * days = which days of the week alarms are allowed to occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurDaily(int freq, const TQBitArray& days, int count, const TQDate& end) { if (!setRecur(RecurrenceRule::rDaily, freq, count, end)) return false; int n = 0; for (int i = 0; i < 7; ++i) { if (days.testBit(i)) ++n; } if (n < 7) mRecurrence->addWeeklyDays(days); return true; } /****************************************************************************** * Set the recurrence to recur weekly, on the specified weekdays. * Parameters: * freq = how many weeks between recurrences. * days = which days of the week alarms should occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurWeekly(int freq, const TQBitArray& days, int count, const TQDate& end) { if (!setRecur(RecurrenceRule::rWeekly, freq, count, end)) return false; mRecurrence->addWeeklyDays(days); return true; } /****************************************************************************** * Set the recurrence to recur monthly, on the specified days within the month. * Parameters: * freq = how many months between recurrences. * days = which days of the month alarms should occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurMonthlyByDate(int freq, const TQValueList& days, int count, const TQDate& end) { if (!setRecur(RecurrenceRule::rMonthly, freq, count, end)) return false; for (TQValueListConstIterator it = days.begin(); it != days.end(); ++it) mRecurrence->addMonthlyDate(*it); return true; } /****************************************************************************** * Set the recurrence to recur monthly, on the specified weekdays in the * specified weeks of the month. * Parameters: * freq = how many months between recurrences. * posns = which days of the week/weeks of the month alarms should occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurMonthlyByPos(int freq, const TQValueList& posns, int count, const TQDate& end) { if (!setRecur(RecurrenceRule::rMonthly, freq, count, end)) return false; for (TQValueListConstIterator it = posns.begin(); it != posns.end(); ++it) mRecurrence->addMonthlyPos((*it).weeknum, (*it).days); return true; } /****************************************************************************** * Set the recurrence to recur annually, on the specified start date in each * of the specified months. * Parameters: * freq = how many years between recurrences. * months = which months of the year alarms should occur on. * day = day of month, or 0 to use start date * feb29 = when February 29th should recur in non-leap years. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurAnnualByDate(int freq, const TQValueList& months, int day, KARecurrence::Feb29Type feb29, int count, const TQDate& end) { if (!setRecur(RecurrenceRule::rYearly, freq, count, end, feb29)) return false; for (TQValueListConstIterator it = months.begin(); it != months.end(); ++it) mRecurrence->addYearlyMonth(*it); if (day) mRecurrence->addMonthlyDate(day); return true; } /****************************************************************************** * Set the recurrence to recur annually, on the specified weekdays in the * specified weeks of the specified months. * Parameters: * freq = how many years between recurrences. * posns = which days of the week/weeks of the month alarms should occur on. * months = which months of the year alarms should occur on. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecurAnnualByPos(int freq, const TQValueList& posns, const TQValueList& months, int count, const TQDate& end) { if (!setRecur(RecurrenceRule::rYearly, freq, count, end)) return false; for (TQValueListConstIterator it = months.begin(); it != months.end(); ++it) mRecurrence->addYearlyMonth(*it); for (TQValueListConstIterator it = posns.begin(); it != posns.end(); ++it) mRecurrence->addYearlyPos((*it).weeknum, (*it).days); return true; } /****************************************************************************** * Initialise the event's recurrence data. * Parameters: * freq = how many intervals between recurrences. * count = number of occurrences, including first and last. * = -1 to recur indefinitely. * = 0 to use 'end' instead. * end = end date/time (invalid to use 'count' instead). * Reply = false if no recurrence was set up. */ bool KAEvent::setRecur(RecurrenceRule::PeriodType recurType, int freq, int count, const TQDateTime& end, KARecurrence::Feb29Type feb29) { if (count >= -1 && (count || end.date().isValid())) { if (!mRecurrence) mRecurrence = new KARecurrence; if (mRecurrence->init(recurType, freq, count, mNextMainDateTime, end, feb29)) { mUpdated = true; return true; } } clearRecur(); return false; } /****************************************************************************** * Clear the event's recurrence and alarm repetition data. */ void KAEvent::clearRecur() { delete mRecurrence; mRecurrence = 0; mRepeatInterval = 0; mRepeatCount = 0; mNextRepeat = 0; mUpdated = true; } /****************************************************************************** * Validate the event's recurrence data, correcting any inconsistencies (which * should never occur!). * Reply = true if a recurrence (as opposed to a login repetition) exists. */ KARecurrence::Type KAEvent::checkRecur() const { if (mRecurrence) { KARecurrence::Type type = mRecurrence->type(); switch (type) { case KARecurrence::MINUTELY: // hourly case KARecurrence::DAILY: // daily case KARecurrence::WEEKLY: // weekly on multiple days of week case KARecurrence::MONTHLY_DAY: // monthly on multiple dates in month case KARecurrence::MONTHLY_POS: // monthly on multiple nth day of week case KARecurrence::ANNUAL_DATE: // annually on multiple months (day of month = start date) case KARecurrence::ANNUAL_POS: // annually on multiple nth day of week in multiple months return type; default: if (mRecurrence) const_cast(this)->clearRecur(); // recurrence shouldn't exist!! break; } } return KARecurrence::NO_RECUR; } /****************************************************************************** * Return the recurrence interval in units of the recurrence period type. */ int KAEvent::recurInterval() const { if (mRecurrence) { switch (mRecurrence->type()) { case KARecurrence::MINUTELY: case KARecurrence::DAILY: case KARecurrence::WEEKLY: case KARecurrence::MONTHLY_DAY: case KARecurrence::MONTHLY_POS: case KARecurrence::ANNUAL_DATE: case KARecurrence::ANNUAL_POS: return mRecurrence->frequency(); default: break; } } return 0; } /****************************************************************************** * Validate the event's alarm sub-repetition data, correcting any * inconsistencies (which should never occur!). */ void KAEvent::checkRepetition() const { if (mRepeatCount && !mRepeatInterval) const_cast(this)->mRepeatCount = 0; if (!mRepeatCount && mRepeatInterval) const_cast(this)->mRepeatInterval = 0; } #if 0 /****************************************************************************** * Convert a TQValueList to TQValueList. */ TQValueList KAEvent::convRecurPos(const TQValueList& wdaypos) { TQValueList mposns; for (TQValueList::ConstIterator it = wdaypos.begin(); it != wdaypos.end(); ++it) { int daybit = (*it).day() - 1; int weeknum = (*it).pos(); bool found = false; for (TQValueList::Iterator mit = mposns.begin(); mit != mposns.end(); ++mit) { if ((*mit).weeknum == weeknum) { (*mit).days.setBit(daybit); found = true; break; } } if (!found) { MonthPos mpos; mpos.days.fill(false); mpos.days.setBit(daybit); mpos.weeknum = weeknum; mposns.append(mpos); } } return mposns; } #endif /****************************************************************************** * Find the alarm template with the specified name. * Reply = invalid event if not found. */ KAEvent KAEvent::findTemplateName(AlarmCalendar& calendar, const TQString& name) { KAEvent event; Event::List events = calendar.events(); for (Event::List::ConstIterator evit = events.begin(); evit != events.end(); ++evit) { Event* ev = *evit; if (ev->summary() == name) { event.set(*ev); if (!event.isTemplate()) return KAEvent(); // this shouldn't ever happen break; } } return event; } /****************************************************************************** * Adjust the time at which date-only events will occur for each of the events * in a list. Events for which both date and time are specified are left * unchanged. * Reply = true if any events have been updated. */ bool KAEvent::adjustStartOfDay(const Event::List& events) { bool changed = false; TQTime startOfDay = Preferences::startOfDay(); for (Event::List::ConstIterator evit = events.begin(); evit != events.end(); ++evit) { Event* event = *evit; const TQStringList cats = event->categories(); if (cats.find(DATE_ONLY_CATEGORY) != cats.end()) { // It's an untimed event, so fix it TQTime oldTime = event->dtStart().time(); int adjustment = oldTime.secsTo(startOfDay); if (adjustment) { event->setDtStart(TQDateTime(event->dtStart().date(), startOfDay)); Alarm::List alarms = event->alarms(); int deferralOffset = 0; for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit) { // Parse the next alarm's text Alarm& alarm = **alit; AlarmData data; readAlarm(alarm, data); if (data.type & KAAlarm::TIMED_DEFERRAL_FLAG) { // Timed deferral alarm, so adjust the offset deferralOffset = alarm.startOffset().asSeconds(); alarm.setStartOffset(deferralOffset - adjustment); } else if (data.type == KAAlarm::AUDIO__ALARM && alarm.startOffset().asSeconds() == deferralOffset) { // Audio alarm is set for the same time as the deferral alarm alarm.setStartOffset(deferralOffset - adjustment); } } changed = true; } } else { // It's a timed event. Fix any untimed alarms. int deferralOffset = 0; int newDeferralOffset = 0; DateTime start; TQDateTime nextMainDateTime = readDateTime(*event, false, start).rawDateTime(); AlarmMap alarmMap; readAlarms(*event, &alarmMap); for (AlarmMap::Iterator it = alarmMap.begin(); it != alarmMap.end(); ++it) { const AlarmData& data = it.data(); if (!data.alarm->hasStartOffset()) continue; if ((data.type & KAAlarm::DEFERRED_ALARM) && !(data.type & KAAlarm::TIMED_DEFERRAL_FLAG)) { // Date-only deferral alarm, so adjust its time TQDateTime altime = nextMainDateTime.addSecs(data.alarm->startOffset().asSeconds()); altime.setTime(startOfDay); deferralOffset = data.alarm->startOffset().asSeconds(); newDeferralOffset = event->dtStart().secsTo(altime); const_cast(data.alarm)->setStartOffset(newDeferralOffset); changed = true; } else if (data.type == KAAlarm::AUDIO__ALARM && data.alarm->startOffset().asSeconds() == deferralOffset) { // Audio alarm is set for the same time as the deferral alarm const_cast(data.alarm)->setStartOffset(newDeferralOffset); changed = true; } } } } return changed; } /****************************************************************************** * If the calendar was written by a previous version of KAlarm, do any * necessary format conversions on the events to ensure that when the calendar * is saved, no information is lost or corrupted. */ void KAEvent::convertKCalEvents(KCal::Calendar& calendar, int version, bool adjustSummerTime) { // KAlarm pre-0.9 codes held in the alarm's DESCRIPTION property static const TQChar SEPARATOR = ';'; static const TQChar LATE_CANCEL_CODE = 'C'; static const TQChar AT_LOGIN_CODE = 'L'; // subsidiary alarm at every login static const TQChar DEFERRAL_CODE = 'D'; // extra deferred alarm static const TQString TEXT_PREFIX = TQString::fromLatin1("TEXT:"); static const TQString FILE_PREFIX = TQString::fromLatin1("FILE:"); static const TQString COMMAND_PREFIX = TQString::fromLatin1("CMD:"); // KAlarm pre-0.9.2 codes held in the event's CATEGORY property static const TQString BEEP_CATEGORY = TQString::fromLatin1("BEEP"); // KAlarm pre-1.1.1 LATECANCEL category with no parameter static const TQString LATE_CANCEL_CAT = TQString::fromLatin1("LATECANCEL"); // KAlarm pre-1.3.0 TMPLDEFTIME category with no parameter static const TQString TEMPL_DEF_TIME_CAT = TQString::fromLatin1("TMPLDEFTIME"); // KAlarm pre-1.3.1 XTERM category static const TQString EXEC_IN_XTERM_CAT = TQString::fromLatin1("XTERM"); // KAlarm pre-1.4.22 properties static const TQCString KMAIL_ID_PROPERTY("KMAILID"); // X-TDE-KALARM-KMAILID property if (version >= calVersion()) return; kdDebug(5950) << "KAEvent::convertKCalEvents(): adjusting version " << version << endl; bool pre_0_7 = (version < KAlarm::Version(0,7,0)); bool pre_0_9 = (version < KAlarm::Version(0,9,0)); bool pre_0_9_2 = (version < KAlarm::Version(0,9,2)); bool pre_1_1_1 = (version < KAlarm::Version(1,1,1)); bool pre_1_2_1 = (version < KAlarm::Version(1,2,1)); bool pre_1_3_0 = (version < KAlarm::Version(1,3,0)); bool pre_1_3_1 = (version < KAlarm::Version(1,3,1)); bool pre_1_4_14 = (version < KAlarm::Version(1,4,14)); bool pre_1_5_0 = (version < KAlarm::Version(1,5,0)); Q_ASSERT(calVersion() == KAlarm::Version(1,5,0)); TQDateTime dt0(TQDate(1970,1,1), TQTime(0,0,0)); TQTime startOfDay = Preferences::startOfDay(); Event::List events = calendar.rawEvents(); for (Event::List::ConstIterator evit = events.begin(); evit != events.end(); ++evit) { Event* event = *evit; Alarm::List alarms = event->alarms(); if (alarms.isEmpty()) continue; // KAlarm isn't interested in events without alarms TQStringList cats = event->categories(); bool addLateCancel = false; if (pre_0_7 && event->doesFloat()) { // It's a KAlarm pre-0.7 calendar file. // Ensure that when the calendar is saved, the alarm time isn't lost. event->setFloats(false); } if (pre_0_9) { /* * It's a KAlarm pre-0.9 calendar file. * All alarms were of type DISPLAY. Instead of the X-TDE-KALARM-TYPE * alarm property, characteristics were stored as a prefix to the * alarm DESCRIPTION property, as follows: * SEQNO;[FLAGS];TYPE:TEXT * where * SEQNO = sequence number of alarm within the event * FLAGS = C for late-cancel, L for repeat-at-login, D for deferral * TYPE = TEXT or FILE or CMD * TEXT = message text, file name/URL or command */ for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit) { Alarm* alarm = *alit; bool atLogin = false; bool deferral = false; bool lateCancel = false; KAAlarmEventBase::Type action = T_MESSAGE; TQString txt = alarm->text(); int length = txt.length(); int i = 0; if (txt[0].isDigit()) { while (++i < length && txt[i].isDigit()) ; if (i < length && txt[i++] == SEPARATOR) { while (i < length) { TQChar ch = txt[i++]; if (ch == SEPARATOR) break; if (ch == LATE_CANCEL_CODE) lateCancel = true; else if (ch == AT_LOGIN_CODE) atLogin = true; else if (ch == DEFERRAL_CODE) deferral = true; } } else i = 0; // invalid prefix } if (txt.find(TEXT_PREFIX, i) == i) i += TEXT_PREFIX.length(); else if (txt.find(FILE_PREFIX, i) == i) { action = T_FILE; i += FILE_PREFIX.length(); } else if (txt.find(COMMAND_PREFIX, i) == i) { action = T_COMMAND; i += COMMAND_PREFIX.length(); } else i = 0; txt = txt.mid(i); TQStringList types; switch (action) { case T_FILE: types += FILE_TYPE; // fall through to T_MESSAGE case T_MESSAGE: alarm->setDisplayAlarm(txt); break; case T_COMMAND: setProcedureAlarm(alarm, txt); break; case T_EMAIL: // email alarms were introduced in KAlarm 0.9 case T_AUDIO: // never occurs in this context break; } if (atLogin) { types += AT_LOGIN_TYPE; lateCancel = false; } else if (deferral) types += TIME_DEFERRAL_TYPE; if (lateCancel) addLateCancel = true; if (types.count() > 0) alarm->setCustomProperty(APPNAME, TYPE_PROPERTY, types.join(",")); if (pre_0_7 && alarm->repeatCount() > 0 && alarm->snoozeTime().value() > 0) { // It's a KAlarm pre-0.7 calendar file. // Minutely recurrences were stored differently. Recurrence* recur = event->recurrence(); if (recur && recur->doesRecur()) { recur->setMinutely(alarm->snoozeTime()); recur->setDuration(alarm->repeatCount() + 1); alarm->setRepeatCount(0); alarm->setSnoozeTime(0); } } if (adjustSummerTime) { // The calendar file was written by the KDE 3.0.0 version of KAlarm 0.5.7. // Summer time was ignored when converting to UTC. TQDateTime dt = alarm->time(); time_t t = dt0.secsTo(dt); struct tm* dtm = localtime(&t); if (dtm->tm_isdst) { dt = dt.addSecs(-3600); alarm->setTime(dt); } } } } if (pre_0_9_2) { /* * It's a KAlarm pre-0.9.2 calendar file. * For the expired calendar, set the CREATED time to the DTEND value. * Convert date-only DTSTART to date/time, and add category "DATE". * Set the DTEND time to the DTSTART time. * Convert all alarm times to DTSTART offsets. * For display alarms, convert the first unlabelled category to an * X-TDE-KALARM-FONTCOLOUR property. * Convert BEEP category into an audio alarm with no audio file. */ if (uidStatus(event->uid()) == EXPIRED) event->setCreated(event->dtEnd()); TQDateTime start = event->dtStart(); if (event->doesFloat()) { event->setFloats(false); start.setTime(startOfDay); cats.append(DATE_ONLY_CATEGORY); } event->setHasEndDate(false); Alarm::List::ConstIterator alit; for (alit = alarms.begin(); alit != alarms.end(); ++alit) { Alarm* alarm = *alit; TQDateTime dt = alarm->time(); alarm->setStartOffset(start.secsTo(dt)); } if (cats.count() > 0) { for (alit = alarms.begin(); alit != alarms.end(); ++alit) { Alarm* alarm = *alit; if (alarm->type() == Alarm::Display) alarm->setCustomProperty(APPNAME, FONT_COLOUR_PROPERTY, TQString::fromLatin1("%1;;").arg(cats[0])); } cats.remove(cats.begin()); } for (TQStringList::Iterator it = cats.begin(); it != cats.end(); ++it) { if (*it == BEEP_CATEGORY) { cats.remove(it); Alarm* alarm = event->newAlarm(); alarm->setEnabled(true); alarm->setAudioAlarm(); TQDateTime dt = event->dtStart(); // default // Parse and order the alarms to know which one's date/time to use AlarmMap alarmMap; readAlarms(*event, &alarmMap); AlarmMap::ConstIterator it = alarmMap.begin(); if (it != alarmMap.end()) { dt = it.data().alarm->time(); break; } alarm->setStartOffset(start.secsTo(dt)); break; } } } if (pre_1_1_1) { /* * It's a KAlarm pre-1.1.1 calendar file. * Convert simple LATECANCEL category to LATECANCEL:n where n = minutes late. */ TQStringList::Iterator it; while ((it = cats.find(LATE_CANCEL_CAT)) != cats.end()) { cats.remove(it); addLateCancel = true; } } if (pre_1_2_1) { /* * It's a KAlarm pre-1.2.1 calendar file. * Convert email display alarms from translated to untranslated header prefixes. */ for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit) { Alarm* alarm = *alit; if (alarm->type() == Alarm::Display) { TQString oldtext = alarm->text(); TQString newtext = AlarmText::toCalendarText(oldtext); if (oldtext != newtext) alarm->setDisplayAlarm(newtext); } } } if (pre_1_3_0) { /* * It's a KAlarm pre-1.3.0 calendar file. * Convert simple TMPLDEFTIME category to TMPLAFTTIME:n where n = minutes after. */ TQStringList::Iterator it; while ((it = cats.find(TEMPL_DEF_TIME_CAT)) != cats.end()) { cats.remove(it); cats.append(TQString("%1%2").arg(TEMPL_AFTER_TIME_CATEGORY).arg(0)); } } if (pre_1_3_1) { /* * It's a KAlarm pre-1.3.1 calendar file. * Convert simple XTERM category to LOG:xterm: */ TQStringList::Iterator it; while ((it = cats.find(EXEC_IN_XTERM_CAT)) != cats.end()) { cats.remove(it); cats.append(LOG_CATEGORY + xtermURL); } } if (addLateCancel) cats.append(TQString("%1%2").arg(LATE_CANCEL_CATEGORY).arg(1)); event->setCategories(cats); if (pre_1_4_14 && event->recurrence() && event->recurrence()->doesRecur()) { /* * It's a KAlarm pre-1.4.14 calendar file. * For recurring events, convert the main alarm offset to an absolute * time in the X-TDE-KALARM-NEXTRECUR property, and convert main * alarm offsets to zero and deferral alarm offsets to be relative to * the next recurrence. */ bool dateOnly = (cats.find(DATE_ONLY_CATEGORY) != cats.end()); DateTime startDateTime(event->dtStart(), dateOnly); // Convert the main alarm and get the next main trigger time from it DateTime nextMainDateTime; bool mainExpired = true; Alarm::List::ConstIterator alit; for (alit = alarms.begin(); alit != alarms.end(); ++alit) { Alarm* alarm = *alit; if (!alarm->hasStartOffset()) continue; bool mainAlarm = true; TQString property = alarm->customProperty(APPNAME, TYPE_PROPERTY); TQStringList types = TQStringList::split(TQChar(','), property); for (unsigned int i = 0; i < types.count(); ++i) { TQString type = types[i]; if (type == AT_LOGIN_TYPE || type == TIME_DEFERRAL_TYPE || type == DATE_DEFERRAL_TYPE || type == REMINDER_TYPE || type == REMINDER_ONCE_TYPE || type == DISPLAYING_TYPE || type == PRE_ACTION_TYPE || type == POST_ACTION_TYPE) mainAlarm = false; } if (mainAlarm) { mainExpired = false; nextMainDateTime = alarm->time(); nextMainDateTime.setDateOnly(dateOnly); if (nextMainDateTime != startDateTime) { TQDateTime dt = nextMainDateTime.dateTime(); event->setCustomProperty(APPNAME, NEXT_RECUR_PROPERTY, dt.toString(dateOnly ? "yyyyMMdd" : "yyyyMMddThhmmss")); } alarm->setStartOffset(0); } } int adjustment; if (mainExpired) { // It's an expired recurrence. // Set the alarm offset relative to the first actual occurrence // (taking account of possible exceptions). DateTime dt = event->recurrence()->getNextDateTime(startDateTime.dateTime().addDays(-1)); dt.setDateOnly(dateOnly); adjustment = startDateTime.secsTo(dt); } else adjustment = startDateTime.secsTo(nextMainDateTime); if (adjustment) { // Convert deferred alarms for (alit = alarms.begin(); alit != alarms.end(); ++alit) { Alarm* alarm = *alit; if (!alarm->hasStartOffset()) continue; TQString property = alarm->customProperty(APPNAME, TYPE_PROPERTY); TQStringList types = TQStringList::split(TQChar(','), property); for (unsigned int i = 0; i < types.count(); ++i) { TQString type = types[i]; if (type == TIME_DEFERRAL_TYPE || type == DATE_DEFERRAL_TYPE) { alarm->setStartOffset(alarm->startOffset().asSeconds() - adjustment); break; } } } } } if (pre_1_5_0) { /* * It's a KAlarm pre-1.5.0 calendar file. * Convert email identity names to uoids. * Convert simple repetitions without a recurrence, to a recurrence. */ for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit) { Alarm* alarm = *alit; TQString name = alarm->customProperty(APPNAME, KMAIL_ID_PROPERTY); if (name.isEmpty()) continue; uint id = KAMail::identityUoid(name); if (id) alarm->setCustomProperty(APPNAME, EMAIL_ID_PROPERTY, TQString::number(id)); alarm->removeCustomProperty(APPNAME, KMAIL_ID_PROPERTY); } convertRepetition(event); } } } /****************************************************************************** * If the calendar was written by a pre-1.4.22 version of KAlarm, or another * program, convert simple repetitions in events without a recurrence, to a * recurrence. * Reply = true if any conversions were done. */ void KAEvent::convertRepetitions(KCal::CalendarLocal& calendar) { Event::List events = calendar.rawEvents(); for (Event::List::ConstIterator ev = events.begin(); ev != events.end(); ++ev) convertRepetition(*ev); } /****************************************************************************** * Convert simple repetitions in an event without a recurrence, to a * recurrence. Repetitions which are an exact multiple of 24 hours are converted * to daily recurrences; else they are converted to minutely recurrences. Note * that daily and minutely recurrences produce different results when they span * a daylight saving time change. * Reply = true if any conversions were done. */ bool KAEvent::convertRepetition(KCal::Event* event) { Alarm::List alarms = event->alarms(); if (alarms.isEmpty()) return false; Recurrence* recur = event->recurrence(); // guaranteed to return non-null if (!recur->doesRecur()) return false; bool converted = false; bool readOnly = event->isReadOnly(); for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit) { Alarm* alarm = *alit; if (alarm->repeatCount() > 0 && alarm->snoozeTime().value() > 0) { if (!converted) { if (readOnly) event->setReadOnly(false); if (alarm->snoozeTime() % (24*3600)) recur->setMinutely(alarm->snoozeTime()); else recur->setDaily(alarm->snoozeTime() / (24*3600)); recur->setDuration(alarm->repeatCount() + 1); converted = true; } alarm->setRepeatCount(0); alarm->setSnoozeTime(0); } } if (converted) { if (readOnly) event->setReadOnly(true); } return converted; } #ifndef NDEBUG void KAEvent::dumpDebug() const { kdDebug(5950) << "KAEvent dump:\n"; KAAlarmEventBase::dumpDebug(); if (!mTemplateName.isEmpty()) { kdDebug(5950) << "-- mTemplateName:" << mTemplateName << ":\n"; kdDebug(5950) << "-- mTemplateAfterTime:" << mTemplateAfterTime << ":\n"; } if (mActionType == T_MESSAGE || mActionType == T_FILE) { kdDebug(5950) << "-- mAudioFile:" << mAudioFile << ":\n"; kdDebug(5950) << "-- mPreAction:" << mPreAction << ":\n"; kdDebug(5950) << "-- mPostAction:" << mPostAction << ":\n"; } else if (mActionType == T_COMMAND) { kdDebug(5950) << "-- mCommandXterm:" << (mCommandXterm ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mLogFile:" << mLogFile << ":\n"; } kdDebug(5950) << "-- mKMailSerialNumber:" << mKMailSerialNumber << ":\n"; kdDebug(5950) << "-- mCopyToKOrganizer:" << (mCopyToKOrganizer ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mStartDateTime:" << mStartDateTime.toString() << ":\n"; kdDebug(5950) << "-- mSaveDateTime:" << mSaveDateTime.toString() << ":\n"; if (mRepeatAtLogin) kdDebug(5950) << "-- mAtLoginDateTime:" << mAtLoginDateTime.toString() << ":\n"; kdDebug(5950) << "-- mArchiveRepeatAtLogin:" << (mArchiveRepeatAtLogin ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mEnabled:" << (mEnabled ? "true" : "false") << ":\n"; if (mReminderMinutes) kdDebug(5950) << "-- mReminderMinutes:" << mReminderMinutes << ":\n"; if (mArchiveReminderMinutes) kdDebug(5950) << "-- mArchiveReminderMinutes:" << mArchiveReminderMinutes << ":\n"; if (mReminderMinutes || mArchiveReminderMinutes) kdDebug(5950) << "-- mReminderOnceOnly:" << mReminderOnceOnly << ":\n"; else if (mDeferral > 0) { kdDebug(5950) << "-- mDeferral:" << (mDeferral == NORMAL_DEFERRAL ? "normal" : "reminder") << ":\n"; kdDebug(5950) << "-- mDeferralTime:" << mDeferralTime.toString() << ":\n"; } else if (mDeferral == CANCEL_DEFERRAL) kdDebug(5950) << "-- mDeferral:cancel:\n"; kdDebug(5950) << "-- mDeferDefaultMinutes:" << mDeferDefaultMinutes << ":\n"; if (mDisplaying) { kdDebug(5950) << "-- mDisplayingTime:" << mDisplayingTime.toString() << ":\n"; kdDebug(5950) << "-- mDisplayingFlags:" << mDisplayingFlags << ":\n"; } kdDebug(5950) << "-- mRevision:" << mRevision << ":\n"; kdDebug(5950) << "-- mRecurrence:" << (mRecurrence ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mAlarmCount:" << mAlarmCount << ":\n"; kdDebug(5950) << "-- mMainExpired:" << (mMainExpired ? "true" : "false") << ":\n"; kdDebug(5950) << "KAEvent dump end\n"; } #endif /*============================================================================= = Class KAAlarm = Corresponds to a single KCal::Alarm instance. =============================================================================*/ KAAlarm::KAAlarm(const KAAlarm& alarm) : KAAlarmEventBase(alarm), mType(alarm.mType), mRecurs(alarm.mRecurs), mDeferred(alarm.mDeferred) { } int KAAlarm::flags() const { return KAAlarmEventBase::flags() | (mDeferred ? KAEvent::DEFERRAL : 0); } #ifndef NDEBUG void KAAlarm::dumpDebug() const { kdDebug(5950) << "KAAlarm dump:\n"; KAAlarmEventBase::dumpDebug(); const char* altype = 0; switch (mType) { case MAIN__ALARM: altype = "MAIN"; break; case REMINDER__ALARM: altype = "REMINDER"; break; case DEFERRED_DATE__ALARM: altype = "DEFERRED(DATE)"; break; case DEFERRED_TIME__ALARM: altype = "DEFERRED(TIME)"; break; case DEFERRED_REMINDER_DATE__ALARM: altype = "DEFERRED_REMINDER(DATE)"; break; case DEFERRED_REMINDER_TIME__ALARM: altype = "DEFERRED_REMINDER(TIME)"; break; case AT_LOGIN__ALARM: altype = "LOGIN"; break; case DISPLAYING__ALARM: altype = "DISPLAYING"; break; case AUDIO__ALARM: altype = "AUDIO"; break; case PRE_ACTION__ALARM: altype = "PRE_ACTION"; break; case POST_ACTION__ALARM: altype = "POST_ACTION"; break; default: altype = "INVALID"; break; } kdDebug(5950) << "-- mType:" << altype << ":\n"; kdDebug(5950) << "-- mRecurs:" << (mRecurs ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mDeferred:" << (mDeferred ? "true" : "false") << ":\n"; kdDebug(5950) << "KAAlarm dump end\n"; } const char* KAAlarm::debugType(Type type) { switch (type) { case MAIN_ALARM: return "MAIN"; case REMINDER_ALARM: return "REMINDER"; case DEFERRED_ALARM: return "DEFERRED"; case DEFERRED_REMINDER_ALARM: return "DEFERRED_REMINDER"; case AT_LOGIN_ALARM: return "LOGIN"; case DISPLAYING_ALARM: return "DISPLAYING"; case AUDIO_ALARM: return "AUDIO"; case PRE_ACTION_ALARM: return "PRE_ACTION"; case POST_ACTION_ALARM: return "POST_ACTION"; default: return "INVALID"; } } #endif /*============================================================================= = Class KAAlarmEventBase =============================================================================*/ void KAAlarmEventBase::copy(const KAAlarmEventBase& rhs) { mEventID = rhs.mEventID; mText = rhs.mText; mNextMainDateTime = rhs.mNextMainDateTime; mBgColour = rhs.mBgColour; mFgColour = rhs.mFgColour; mFont = rhs.mFont; mEmailFromIdentity = rhs.mEmailFromIdentity; mEmailAddresses = rhs.mEmailAddresses; mEmailSubject = rhs.mEmailSubject; mEmailAttachments = rhs.mEmailAttachments; mSoundVolume = rhs.mSoundVolume; mFadeVolume = rhs.mFadeVolume; mFadeSeconds = rhs.mFadeSeconds; mActionType = rhs.mActionType; mCommandScript = rhs.mCommandScript; mRepeatCount = rhs.mRepeatCount; mRepeatInterval = rhs.mRepeatInterval; mNextRepeat = rhs.mNextRepeat; mBeep = rhs.mBeep; mSpeak = rhs.mSpeak; mRepeatSound = rhs.mRepeatSound; mRepeatAtLogin = rhs.mRepeatAtLogin; mDisplaying = rhs.mDisplaying; mLateCancel = rhs.mLateCancel; mAutoClose = rhs.mAutoClose; mEmailBcc = rhs.mEmailBcc; mConfirmAck = rhs.mConfirmAck; mDefaultFont = rhs.mDefaultFont; } void KAAlarmEventBase::set(int flags) { mSpeak = flags & KAEvent::SPEAK; mBeep = (flags & KAEvent::BEEP) && !mSpeak; mRepeatSound = flags & KAEvent::REPEAT_SOUND; mRepeatAtLogin = flags & KAEvent::REPEAT_AT_LOGIN; mAutoClose = (flags & KAEvent::AUTO_CLOSE) && mLateCancel; mEmailBcc = flags & KAEvent::EMAIL_BCC; mConfirmAck = flags & KAEvent::CONFIRM_ACK; mDisplaying = flags & KAEvent::DISPLAYING_; mDefaultFont = flags & KAEvent::DEFAULT_FONT; mCommandScript = flags & KAEvent::SCRIPT; } int KAAlarmEventBase::flags() const { return (mBeep && !mSpeak ? KAEvent::BEEP : 0) | (mSpeak ? KAEvent::SPEAK : 0) | (mRepeatSound ? KAEvent::REPEAT_SOUND : 0) | (mRepeatAtLogin ? KAEvent::REPEAT_AT_LOGIN : 0) | (mAutoClose ? KAEvent::AUTO_CLOSE : 0) | (mEmailBcc ? KAEvent::EMAIL_BCC : 0) | (mConfirmAck ? KAEvent::CONFIRM_ACK : 0) | (mDisplaying ? KAEvent::DISPLAYING_ : 0) | (mDefaultFont ? KAEvent::DEFAULT_FONT : 0) | (mCommandScript ? KAEvent::SCRIPT : 0); } const TQFont& KAAlarmEventBase::font() const { return mDefaultFont ? Preferences::messageFont() : mFont; } #ifndef NDEBUG void KAAlarmEventBase::dumpDebug() const { kdDebug(5950) << "-- mEventID:" << mEventID << ":\n"; kdDebug(5950) << "-- mActionType:" << (mActionType == T_MESSAGE ? "MESSAGE" : mActionType == T_FILE ? "FILE" : mActionType == T_COMMAND ? "COMMAND" : mActionType == T_EMAIL ? "EMAIL" : mActionType == T_AUDIO ? "AUDIO" : "??") << ":\n"; kdDebug(5950) << "-- mText:" << mText << ":\n"; if (mActionType == T_COMMAND) kdDebug(5950) << "-- mCommandScript:" << (mCommandScript ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mNextMainDateTime:" << mNextMainDateTime.toString() << ":\n"; if (mActionType == T_EMAIL) { kdDebug(5950) << "-- mEmail: FromKMail:" << mEmailFromIdentity << ":\n"; kdDebug(5950) << "-- Addresses:" << mEmailAddresses.join(", ") << ":\n"; kdDebug(5950) << "-- Subject:" << mEmailSubject << ":\n"; kdDebug(5950) << "-- Attachments:" << mEmailAttachments.join(", ") << ":\n"; kdDebug(5950) << "-- Bcc:" << (mEmailBcc ? "true" : "false") << ":\n"; } kdDebug(5950) << "-- mBgColour:" << TQString(mBgColour.name()) << ":\n"; kdDebug(5950) << "-- mFgColour:" << TQString(mFgColour.name()) << ":\n"; kdDebug(5950) << "-- mDefaultFont:" << (mDefaultFont ? "true" : "false") << ":\n"; if (!mDefaultFont) kdDebug(5950) << "-- mFont:" << TQString(mFont.toString()) << ":\n"; kdDebug(5950) << "-- mBeep:" << (mBeep ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mSpeak:" << (mSpeak ? "true" : "false") << ":\n"; if (mActionType == T_AUDIO) { if (mSoundVolume >= 0) { kdDebug(5950) << "-- mSoundVolume:" << mSoundVolume << ":\n"; if (mFadeVolume >= 0) { kdDebug(5950) << "-- mFadeVolume:" << mFadeVolume << ":\n"; kdDebug(5950) << "-- mFadeSeconds:" << mFadeSeconds << ":\n"; } else kdDebug(5950) << "-- mFadeVolume:-:\n"; } else kdDebug(5950) << "-- mSoundVolume:-:\n"; kdDebug(5950) << "-- mRepeatSound:" << (mRepeatSound ? "true" : "false") << ":\n"; } kdDebug(5950) << "-- mConfirmAck:" << (mConfirmAck ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mRepeatAtLogin:" << (mRepeatAtLogin ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mRepeatCount:" << mRepeatCount << ":\n"; kdDebug(5950) << "-- mRepeatInterval:" << mRepeatInterval << ":\n"; kdDebug(5950) << "-- mNextRepeat:" << mNextRepeat << ":\n"; kdDebug(5950) << "-- mDisplaying:" << (mDisplaying ? "true" : "false") << ":\n"; kdDebug(5950) << "-- mLateCancel:" << mLateCancel << ":\n"; kdDebug(5950) << "-- mAutoClose:" << (mAutoClose ? "true" : "false") << ":\n"; } #endif /*============================================================================= = Class EmailAddressList =============================================================================*/ /****************************************************************************** * Sets the list of email addresses, removing any empty addresses. * Reply = false if empty addresses were found. */ EmailAddressList& EmailAddressList::operator=(const TQValueList& addresses) { clear(); for (TQValueList::ConstIterator it = addresses.begin(); it != addresses.end(); ++it) { if (!(*it).email().isEmpty()) append(*it); } return *this; } /****************************************************************************** * Return the email address list as a string, each address being delimited by * the specified separator string. */ TQString EmailAddressList::join(const TQString& separator) const { TQString result; bool first = true; for (TQValueList::ConstIterator it = begin(); it != end(); ++it) { if (first) first = false; else result += separator; bool quote = false; TQString name = (*it).name(); if (!name.isEmpty()) { // Need to enclose the name in quotes if it has any special characters int len = name.length(); for (int i = 0; i < len; ++i) { TQChar ch = name[i]; if (!ch.isLetterOrNumber()) { quote = true; result += '\"'; break; } } result += (*it).name(); result += (quote ? "\" <" : " <"); quote = true; // need angle brackets round email address } result += (*it).email(); if (quote) result += '>'; } return result; } /*============================================================================= = Static functions =============================================================================*/ /****************************************************************************** * Set the specified alarm to be a procedure alarm with the given command line. * The command line is first split into its program file and arguments before * initialising the alarm. */ static void setProcedureAlarm(Alarm* alarm, const TQString& commandLine) { TQString command = TQString(); TQString arguments = TQString(); TQChar quoteChar; bool quoted = false; uint posMax = commandLine.length(); uint pos; for (pos = 0; pos < posMax; ++pos) { TQChar ch = commandLine[pos]; if (quoted) { if (ch == quoteChar) { ++pos; // omit the quote character break; } command += ch; } else { bool done = false; switch (ch) { case ' ': case ';': case '|': case '<': case '>': done = !command.isEmpty(); break; case '\'': case '"': if (command.isEmpty()) { // Start of a quoted string. Omit the quote character. quoted = true; quoteChar = ch; break; } // fall through to default default: command += ch; break; } if (done) break; } } // Skip any spaces after the command for ( ; pos < posMax && commandLine[pos] == ' '; ++pos) ; arguments = commandLine.mid(pos); alarm->setProcedureAlarm(command, arguments); }