You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3489 lines
113 KiB
3489 lines
113 KiB
/*
|
|
* alarmevent.cpp - represents calendar alarms and events
|
|
* Program: kalarm
|
|
* Copyright © 2001-2009 by David Jarvie <djarvie@kde.org>
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
#include <tqcolor.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include <klocale.h>
|
|
#include <kdebug.h>
|
|
|
|
#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::tqfromLatin1("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-KDE-KALARM- in the calendar file.
|
|
// - Event properties
|
|
static const TQCString NEXT_RECUR_PROPERTY("NEXTRECUR"); // X-KDE-KALARM-NEXTRECUR property
|
|
static const TQCString REPEAT_PROPERTY("REPEAT"); // X-KDE-KALARM-REPEAT property
|
|
// - General alarm properties
|
|
static const TQCString TYPE_PROPERTY("TYPE"); // X-KDE-KALARM-TYPE property
|
|
static const TQString FILE_TYPE = TQString::tqfromLatin1("FILE");
|
|
static const TQString AT_LOGIN_TYPE = TQString::tqfromLatin1("LOGIN");
|
|
static const TQString REMINDER_TYPE = TQString::tqfromLatin1("REMINDER");
|
|
static const TQString REMINDER_ONCE_TYPE = TQString::tqfromLatin1("REMINDER_ONCE");
|
|
static const TQString ARCHIVE_REMINDER_ONCE_TYPE = TQString::tqfromLatin1("ONCE");
|
|
static const TQString TIME_DEFERRAL_TYPE = TQString::tqfromLatin1("DEFERRAL");
|
|
static const TQString DATE_DEFERRAL_TYPE = TQString::tqfromLatin1("DATE_DEFERRAL");
|
|
static const TQString DISPLAYING_TYPE = TQString::tqfromLatin1("DISPLAYING"); // used only in displaying calendar
|
|
static const TQString PRE_ACTION_TYPE = TQString::tqfromLatin1("PRE");
|
|
static const TQString POST_ACTION_TYPE = TQString::tqfromLatin1("POST");
|
|
static const TQCString NEXT_REPEAT_PROPERTY("NEXTREPEAT"); // X-KDE-KALARM-NEXTREPEAT property
|
|
// - Display alarm properties
|
|
static const TQCString FONT_COLOUR_PROPERTY("FONTCOLOR"); // X-KDE-KALARM-FONTCOLOR property
|
|
// - Email alarm properties
|
|
static const TQCString EMAIL_ID_PROPERTY("EMAILID"); // X-KDE-KALARM-EMAILID property
|
|
// - Audio alarm properties
|
|
static const TQCString VOLUME_PROPERTY("VOLUME"); // X-KDE-KALARM-VOLUME property
|
|
static const TQCString SPEAK_PROPERTY("SPEAK"); // X-KDE-KALARM-SPEAK property
|
|
|
|
// Event categories
|
|
static const TQString DATE_ONLY_CATEGORY = TQString::tqfromLatin1("DATE");
|
|
static const TQString EMAIL_BCC_CATEGORY = TQString::tqfromLatin1("BCC");
|
|
static const TQString CONFIRM_ACK_CATEGORY = TQString::tqfromLatin1("ACKCONF");
|
|
static const TQString LATE_CANCEL_CATEGORY = TQString::tqfromLatin1("LATECANCEL;");
|
|
static const TQString AUTO_CLOSE_CATEGORY = TQString::tqfromLatin1("LATECLOSE;");
|
|
static const TQString TEMPL_AFTER_TIME_CATEGORY = TQString::tqfromLatin1("TMPLAFTTIME;");
|
|
static const TQString KMAIL_SERNUM_CATEGORY = TQString::tqfromLatin1("KMAIL:");
|
|
static const TQString KORGANIZER_CATEGORY = TQString::tqfromLatin1("KORG");
|
|
static const TQString DEFER_CATEGORY = TQString::tqfromLatin1("DEFER;");
|
|
static const TQString ARCHIVE_CATEGORY = TQString::tqfromLatin1("SAVE");
|
|
static const TQString ARCHIVE_CATEGORIES = TQString::tqfromLatin1("SAVE:");
|
|
static const TQString LOG_CATEGORY = TQString::tqfromLatin1("LOG:");
|
|
static const TQString xtermURL = TQString::tqfromLatin1("xterm:");
|
|
|
|
// Event status strings
|
|
static const TQString DISABLED_STATUS = TQString::tqfromLatin1("DISABLED");
|
|
|
|
static const TQString EXPIRED_UID = TQString::tqfromLatin1("-exp-");
|
|
static const TQString DISPLAYING_UID = TQString::tqfromLatin1("-disp-");
|
|
static const TQString TEMPLATE_UID = TQString::tqfromLatin1("-tmpl-");
|
|
static const TQString KORGANIZER_UID = TQString::tqfromLatin1("-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<KAAlarm::SubType, AlarmData> 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::null;
|
|
mLogFile = TQString::null;
|
|
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<int>(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<int>(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<int>(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<int>(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<int>(list[0].toUInt());
|
|
int count = static_cast<int>(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<int>(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<int>(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="<<alarm.text()<<", time="<<alarm.time().toString()<<", valid time="<<alarm.time().isValid()<<endl;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Initialise the KAEvent with the specified parameters.
|
|
*/
|
|
void KAEvent::set(const TQDateTime& dateTime, const TQString& text, const TQColor& bg, const TQColor& fg,
|
|
const TQFont& font, Action action, int lateCancel, int flags)
|
|
{
|
|
clearRecur();
|
|
mStartDateTime.set(dateTime, flags & ANY_TIME);
|
|
mNextMainDateTime = mStartDateTime;
|
|
switch (action)
|
|
{
|
|
case MESSAGE:
|
|
case FILE:
|
|
case COMMAND:
|
|
case EMAIL:
|
|
mActionType = (KAAlarmEventBase::Type)action;
|
|
break;
|
|
default:
|
|
mActionType = T_MESSAGE;
|
|
break;
|
|
}
|
|
mText = (mActionType == T_COMMAND) ? text.stripWhiteSpace() : text;
|
|
mEventID = TQString::null;
|
|
mTemplateName = TQString::null;
|
|
mPreAction = TQString::null;
|
|
mPostAction = TQString::null;
|
|
mAudioFile = "";
|
|
mSoundVolume = -1;
|
|
mFadeVolume = -1;
|
|
mTemplateAfterTime = -1;
|
|
mFadeSeconds = 0;
|
|
mBgColour = bg;
|
|
mFgColour = fg;
|
|
mFont = font;
|
|
mAlarmCount = 1;
|
|
mLateCancel = lateCancel; // do this before setting flags
|
|
mDeferral = NO_DEFERRAL; // do this before setting flags
|
|
|
|
KAAlarmEventBase::set(flags & ~READ_ONLY_FLAGS);
|
|
mStartDateTime.setDateOnly(flags & ANY_TIME);
|
|
set_deferral((flags & DEFERRAL) ? NORMAL_DEFERRAL : NO_DEFERRAL);
|
|
mCommandXterm = flags & EXEC_IN_XTERM;
|
|
mCopyToKOrganizer = flags & COPY_KORGANIZER;
|
|
mEnabled = !(flags & DISABLED);
|
|
|
|
mKMailSerialNumber = 0;
|
|
mReminderMinutes = 0;
|
|
mArchiveReminderMinutes = 0;
|
|
mDeferDefaultMinutes = 0;
|
|
mArchiveRepeatAtLogin = false;
|
|
mReminderOnceOnly = false;
|
|
mDisplaying = false;
|
|
mMainExpired = false;
|
|
mArchive = false;
|
|
mUpdated = false;
|
|
}
|
|
|
|
void KAEvent::setLogFile(const TQString& logfile)
|
|
{
|
|
mLogFile = logfile;
|
|
if (!logfile.isEmpty())
|
|
mCommandXterm = false;
|
|
}
|
|
|
|
void KAEvent::setEmail(uint from, const EmailAddressList& addresses, const TQString& subject, const TQStringList& attachments)
|
|
{
|
|
mEmailFromIdentity = from;
|
|
mEmailAddresses = addresses;
|
|
mEmailSubject = subject;
|
|
mEmailAttachments = attachments;
|
|
}
|
|
|
|
void KAEvent::setAudioFile(const TQString& filename, float volume, float fadeVolume, int fadeSeconds)
|
|
{
|
|
mAudioFile = filename;
|
|
mSoundVolume = filename.isEmpty() ? -1 : volume;
|
|
if (mSoundVolume >= 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 QMIN(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.tqreplace(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::tqfromLatin1(";"));
|
|
cats.append(cat);
|
|
}
|
|
else
|
|
cats.append(ARCHIVE_CATEGORY);
|
|
}
|
|
ev.setCategories(cats);
|
|
ev.setCustomStatus(mEnabled ? TQString::null : 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-KDE-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::tqcurrentDate().addDays(-1);
|
|
else
|
|
dtl = TQDateTime::tqcurrentDateTime();
|
|
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-KDE-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::tqfromLatin1("Y"));
|
|
if (mRepeatSound)
|
|
{
|
|
alarm->setRepeatCount(-1);
|
|
alarm->setSnoozeTime(0);
|
|
}
|
|
if (!mAudioFile.isEmpty() && mSoundVolume >= 0)
|
|
alarm->setCustomProperty(APPNAME, VOLUME_PROPERTY,
|
|
TQString::tqfromLatin1("%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::tqfromLatin1("%1;%2;%3").arg(mBgColour.name())
|
|
.arg(mFgColour.name())
|
|
.arg(mDefaultFont ? TQString::null : 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<KAAlarm::SubType>((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::tqcurrentDateTime();
|
|
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::tqcurrentDateTime(), 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::tqcurrentDateTime();
|
|
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::tqcurrentDateTime() < 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("<<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()<<endl;
|
|
KAAlarm al = event.alarm(alarmType);
|
|
if (al.valid())
|
|
{
|
|
*this = event;
|
|
setUid(DISPLAYING);
|
|
mDisplaying = true;
|
|
mDisplayingTime = (alarmType == KAAlarm::AT_LOGIN_ALARM) ? repeatAtLoginTime : al.dateTime();
|
|
switch (al.type())
|
|
{
|
|
case KAAlarm::AT_LOGIN__ALARM: mDisplayingFlags = REPEAT_AT_LOGIN; break;
|
|
case KAAlarm::REMINDER__ALARM: mDisplayingFlags = REMINDER; break;
|
|
case KAAlarm::DEFERRED_REMINDER_TIME__ALARM: mDisplayingFlags = REMINDER | TIME_DEFERRAL; break;
|
|
case KAAlarm::DEFERRED_REMINDER_DATE__ALARM: mDisplayingFlags = REMINDER | DATE_DEFERRAL; break;
|
|
case KAAlarm::DEFERRED_TIME__ALARM: mDisplayingFlags = TIME_DEFERRAL; break;
|
|
case KAAlarm::DEFERRED_DATE__ALARM: mDisplayingFlags = DATE_DEFERRAL; break;
|
|
default: mDisplayingFlags = 0; break;
|
|
}
|
|
++mAlarmCount;
|
|
mUpdated = true;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Return the original alarm which the displaying alarm refers to.
|
|
*/
|
|
KAAlarm KAEvent::convertDisplayingAlarm() const
|
|
{
|
|
KAAlarm al;
|
|
if (mDisplaying)
|
|
{
|
|
al = alarm(KAAlarm::DISPLAYING_ALARM);
|
|
if (mDisplayingFlags & REPEAT_AT_LOGIN)
|
|
{
|
|
al.mRepeatAtLogin = true;
|
|
al.mType = KAAlarm::AT_LOGIN__ALARM;
|
|
}
|
|
else if (mDisplayingFlags & DEFERRAL)
|
|
{
|
|
al.mDeferred = true;
|
|
al.mType = (mDisplayingFlags == (REMINDER | DATE_DEFERRAL)) ? KAAlarm::DEFERRED_REMINDER_DATE__ALARM
|
|
: (mDisplayingFlags == (REMINDER | TIME_DEFERRAL)) ? KAAlarm::DEFERRED_REMINDER_TIME__ALARM
|
|
: (mDisplayingFlags == DATE_DEFERRAL) ? KAAlarm::DEFERRED_DATE__ALARM
|
|
: KAAlarm::DEFERRED_TIME__ALARM;
|
|
}
|
|
else if (mDisplayingFlags & REMINDER)
|
|
al.mType = KAAlarm::REMINDER__ALARM;
|
|
else
|
|
al.mType = KAAlarm::MAIN__ALARM;
|
|
}
|
|
return al;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Reinstate the original event from the 'displaying' event.
|
|
*/
|
|
void KAEvent::reinstateFromDisplaying(const KAEvent& dispEvent)
|
|
{
|
|
if (dispEvent.mDisplaying)
|
|
{
|
|
*this = dispEvent;
|
|
setUid(ACTIVE);
|
|
mDisplaying = false;
|
|
--mAlarmCount;
|
|
mUpdated = true;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Determine whether the event will occur after the specified date/time.
|
|
* If 'includeRepetitions' is true and the alarm has a sub-repetition, it
|
|
* returns true if any repetitions occur after the specified date/time.
|
|
*/
|
|
bool KAEvent::occursAfter(const TQDateTime& preDateTime, bool includeRepetitions) const
|
|
{
|
|
TQDateTime dt;
|
|
if (checkRecur() != KARecurrence::NO_RECUR)
|
|
{
|
|
if (mRecurrence->duration() < 0)
|
|
return true; // infinite recurrence
|
|
dt = mRecurrence->endDateTime();
|
|
}
|
|
else
|
|
dt = mNextMainDateTime.dateTime();
|
|
if (mStartDateTime.isDateOnly())
|
|
{
|
|
TQDate pre = preDateTime.date();
|
|
if (preDateTime.time() < Preferences::startOfDay())
|
|
pre = pre.addDays(-1); // today's recurrence (if today recurs) is still to come
|
|
if (pre < dt.date())
|
|
return true;
|
|
}
|
|
else if (preDateTime < dt)
|
|
return true;
|
|
|
|
if (includeRepetitions && mRepeatCount)
|
|
{
|
|
if (preDateTime < dt.addSecs(mRepeatCount * mRepeatInterval * 60))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Get the date/time of the next occurrence of the event, after the specified
|
|
* date/time.
|
|
* 'result' = date/time of next occurrence, or invalid date/time if none.
|
|
*/
|
|
KAEvent::OccurType KAEvent::nextOccurrence(const TQDateTime& preDateTime, DateTime& result,
|
|
KAEvent::OccurOption includeRepetitions) const
|
|
{
|
|
int repeatSecs = 0;
|
|
TQDateTime pre = preDateTime;
|
|
if (includeRepetitions != IGNORE_REPETITION)
|
|
{
|
|
if (!mRepeatCount || !mRepeatInterval)
|
|
includeRepetitions = IGNORE_REPETITION;
|
|
else
|
|
{
|
|
repeatSecs = mRepeatInterval * 60;
|
|
pre = preDateTime.addSecs(-mRepeatCount * repeatSecs);
|
|
}
|
|
}
|
|
|
|
OccurType type;
|
|
bool recurs = (checkRecur() != KARecurrence::NO_RECUR);
|
|
if (recurs)
|
|
type = nextRecurrence(pre, result);
|
|
else if (pre < mNextMainDateTime.dateTime())
|
|
{
|
|
result = mNextMainDateTime;
|
|
type = FIRST_OR_ONLY_OCCURRENCE;
|
|
}
|
|
else
|
|
{
|
|
result = DateTime();
|
|
type = NO_OCCURRENCE;
|
|
}
|
|
|
|
if (type != NO_OCCURRENCE && result <= preDateTime && includeRepetitions != IGNORE_REPETITION)
|
|
{
|
|
// The next occurrence is a sub-repetition
|
|
int repetition = result.secsTo(preDateTime) / repeatSecs + 1;
|
|
DateTime repeatDT = result.addSecs(repetition * repeatSecs);
|
|
if (recurs)
|
|
{
|
|
// We've found a recurrence before the specified date/time, which has
|
|
// a sub-repetition after the date/time.
|
|
// However, if the intervals between recurrences vary, we could possibly
|
|
// have missed a later recurrence, which fits the criterion, so check again.
|
|
DateTime dt;
|
|
OccurType newType = previousOccurrence(repeatDT.dateTime(), dt, false);
|
|
if (dt > result)
|
|
{
|
|
type = newType;
|
|
result = dt;
|
|
if (includeRepetitions == RETURN_REPETITION && result <= preDateTime)
|
|
{
|
|
// The next occurrence is a sub-repetition
|
|
int repetition = result.secsTo(preDateTime) / repeatSecs + 1;
|
|
result = result.addSecs(repetition * repeatSecs);
|
|
type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
|
|
}
|
|
return type;
|
|
}
|
|
}
|
|
if (includeRepetitions == RETURN_REPETITION)
|
|
{
|
|
// The next occurrence is a sub-repetition
|
|
result = repeatDT;
|
|
type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Get the date/time of the last previous occurrence of the event, before the
|
|
* specified date/time.
|
|
* If 'includeRepetitions' is true and the alarm has a sub-repetition, the
|
|
* last previous repetition is returned if appropriate.
|
|
* 'result' = date/time of previous occurrence, or invalid date/time if none.
|
|
*/
|
|
KAEvent::OccurType KAEvent::previousOccurrence(const TQDateTime& afterDateTime, DateTime& result, bool includeRepetitions) const
|
|
{
|
|
if (mStartDateTime >= afterDateTime)
|
|
{
|
|
result = TQDateTime();
|
|
return NO_OCCURRENCE; // the event starts after the specified date/time
|
|
}
|
|
|
|
// Find the latest recurrence of the event
|
|
OccurType type;
|
|
if (checkRecur() == KARecurrence::NO_RECUR)
|
|
{
|
|
result = mStartDateTime;
|
|
type = FIRST_OR_ONLY_OCCURRENCE;
|
|
}
|
|
else
|
|
{
|
|
TQDateTime recurStart = mRecurrence->startDateTime();
|
|
TQDateTime after = afterDateTime;
|
|
if (mStartDateTime.isDateOnly() && afterDateTime.time() > Preferences::startOfDay())
|
|
after = after.addDays(1); // today's recurrence (if today recurs) has passed
|
|
TQDateTime dt = mRecurrence->getPreviousDateTime(after);
|
|
result.set(dt, mStartDateTime.isDateOnly());
|
|
if (!dt.isValid())
|
|
return NO_OCCURRENCE;
|
|
if (dt == recurStart)
|
|
type = FIRST_OR_ONLY_OCCURRENCE;
|
|
else if (mRecurrence->getNextDateTime(dt).isValid())
|
|
type = result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
|
|
else
|
|
type = LAST_RECURRENCE;
|
|
}
|
|
|
|
if (includeRepetitions && mRepeatCount)
|
|
{
|
|
// Find the latest repetition which is before the specified time.
|
|
// N.B. This is coded to avoid 32-bit integer overflow which occurs
|
|
// in TQDateTime::secsTo() for large enough time differences.
|
|
int repeatSecs = mRepeatInterval * 60;
|
|
DateTime lastRepetition = result.addSecs(mRepeatCount * repeatSecs);
|
|
if (lastRepetition < afterDateTime)
|
|
{
|
|
result = lastRepetition;
|
|
return static_cast<OccurType>(type | OCCURRENCE_REPEAT);
|
|
}
|
|
int repetition = (result.dateTime().secsTo(afterDateTime) - 1) / repeatSecs;
|
|
if (repetition > 0)
|
|
{
|
|
result = result.addSecs(repetition * repeatSecs);
|
|
return static_cast<OccurType>(type | OCCURRENCE_REPEAT);
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Set the date/time of the event to the next scheduled occurrence after the
|
|
* specified date/time, provided that this is later than its current date/time.
|
|
* Any reminder alarm is adjusted accordingly.
|
|
* If the alarm has a sub-repetition, and a repetition of a previous
|
|
* recurrence occurs after the specified date/time, that repetition is set as
|
|
* the next occurrence.
|
|
*/
|
|
KAEvent::OccurType KAEvent::setNextOccurrence(const TQDateTime& preDateTime)
|
|
{
|
|
if (preDateTime < mNextMainDateTime.dateTime())
|
|
return FIRST_OR_ONLY_OCCURRENCE; // it might not be the first recurrence - tant pis
|
|
TQDateTime pre = preDateTime;
|
|
// If there are repetitions, adjust the comparison date/time so that
|
|
// we find the earliest recurrence which has a repetition falling after
|
|
// the specified preDateTime.
|
|
if (mRepeatCount && mRepeatInterval)
|
|
pre = preDateTime.addSecs(-mRepeatCount * mRepeatInterval * 60);
|
|
|
|
DateTime dt;
|
|
OccurType type;
|
|
if (pre < mNextMainDateTime.dateTime())
|
|
{
|
|
dt = mNextMainDateTime;
|
|
type = FIRST_OR_ONLY_OCCURRENCE; // may not actually be the first occurrence
|
|
}
|
|
else if (checkRecur() != KARecurrence::NO_RECUR)
|
|
{
|
|
type = nextRecurrence(pre, dt);
|
|
if (type == NO_OCCURRENCE)
|
|
return NO_OCCURRENCE;
|
|
if (type != FIRST_OR_ONLY_OCCURRENCE && dt != mNextMainDateTime)
|
|
{
|
|
// Need to reschedule the next trigger date/time
|
|
mNextMainDateTime = dt;
|
|
// Reinstate the reminder (if any) for the rescheduled recurrence
|
|
if (mDeferral == REMINDER_DEFERRAL || mArchiveReminderMinutes)
|
|
{
|
|
if (mReminderOnceOnly)
|
|
{
|
|
if (mReminderMinutes)
|
|
set_archiveReminder();
|
|
}
|
|
else
|
|
set_reminder(mArchiveReminderMinutes);
|
|
}
|
|
if (mDeferral == REMINDER_DEFERRAL)
|
|
set_deferral(NO_DEFERRAL);
|
|
mUpdated = true;
|
|
}
|
|
}
|
|
else
|
|
return NO_OCCURRENCE;
|
|
|
|
if (mRepeatCount && mRepeatInterval)
|
|
{
|
|
int secs = dt.dateTime().secsTo(preDateTime);
|
|
if (secs >= 0)
|
|
{
|
|
// The next occurrence is a sub-repetition.
|
|
type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
|
|
mNextRepeat = (secs / (60 * mRepeatInterval)) + 1;
|
|
// Repetitions can't have a reminder, so remove any.
|
|
if (mReminderMinutes)
|
|
set_archiveReminder();
|
|
if (mDeferral == REMINDER_DEFERRAL)
|
|
set_deferral(NO_DEFERRAL);
|
|
mUpdated = true;
|
|
}
|
|
else if (mNextRepeat)
|
|
{
|
|
// The next occurrence is the main occurrence, not a repetition
|
|
mNextRepeat = 0;
|
|
mUpdated = true;
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Get the date/time of the next recurrence of the event, after the specified
|
|
* date/time.
|
|
* 'result' = date/time of next occurrence, or invalid date/time if none.
|
|
*/
|
|
KAEvent::OccurType KAEvent::nextRecurrence(const TQDateTime& preDateTime, DateTime& result) const
|
|
{
|
|
TQDateTime recurStart = mRecurrence->startDateTime();
|
|
TQDateTime pre = preDateTime;
|
|
if (mStartDateTime.isDateOnly() && preDateTime.time() < Preferences::startOfDay())
|
|
{
|
|
pre = pre.addDays(-1); // today's recurrence (if today recurs) is still to come
|
|
pre.setTime(Preferences::startOfDay());
|
|
}
|
|
TQDateTime dt = mRecurrence->getNextDateTime(pre);
|
|
result.set(dt, mStartDateTime.isDateOnly());
|
|
if (!dt.isValid())
|
|
return NO_OCCURRENCE;
|
|
if (dt == recurStart)
|
|
return FIRST_OR_ONLY_OCCURRENCE;
|
|
if (mRecurrence->duration() >= 0 && dt == mRecurrence->endDateTime())
|
|
return LAST_RECURRENCE;
|
|
return result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Return the recurrence interval as text suitable for display.
|
|
*/
|
|
TQString KAEvent::recurrenceText(bool brief) const
|
|
{
|
|
if (mRepeatAtLogin)
|
|
return brief ? i18n("Brief form of 'At Login'", "Login") : i18n("At login");
|
|
if (mRecurrence)
|
|
{
|
|
int frequency = mRecurrence->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::null : 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::null : 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<int>& days, int count, const TQDate& end)
|
|
{
|
|
if (!setRecur(RecurrenceRule::rMonthly, freq, count, end))
|
|
return false;
|
|
for (TQValueListConstIterator<int> 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<MonthPos>& posns, int count, const TQDate& end)
|
|
{
|
|
if (!setRecur(RecurrenceRule::rMonthly, freq, count, end))
|
|
return false;
|
|
for (TQValueListConstIterator<MonthPos> 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<int>& months, int day, KARecurrence::Feb29Type feb29, int count, const TQDate& end)
|
|
{
|
|
if (!setRecur(RecurrenceRule::rYearly, freq, count, end, feb29))
|
|
return false;
|
|
for (TQValueListConstIterator<int> 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<MonthPos>& posns, const TQValueList<int>& months, int count, const TQDate& end)
|
|
{
|
|
if (!setRecur(RecurrenceRule::rYearly, freq, count, end))
|
|
return false;
|
|
for (TQValueListConstIterator<int> it = months.begin(); it != months.end(); ++it)
|
|
mRecurrence->addYearlyMonth(*it);
|
|
for (TQValueListConstIterator<MonthPos> 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<KAEvent*>(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<KAEvent*>(this)->mRepeatCount = 0;
|
|
if (!mRepeatCount && mRepeatInterval)
|
|
const_cast<KAEvent*>(this)->mRepeatInterval = 0;
|
|
}
|
|
|
|
#if 0
|
|
/******************************************************************************
|
|
* Convert a TQValueList<WDayPos> to TQValueList<MonthPos>.
|
|
*/
|
|
TQValueList<KAEvent::MonthPos> KAEvent::convRecurPos(const TQValueList<KCal::RecurrenceRule::WDayPos>& wdaypos)
|
|
{
|
|
TQValueList<MonthPos> mposns;
|
|
for (TQValueList<KCal::RecurrenceRule::WDayPos>::ConstIterator it = wdaypos.begin(); it != wdaypos.end(); ++it)
|
|
{
|
|
int daybit = (*it).day() - 1;
|
|
int weeknum = (*it).pos();
|
|
bool found = false;
|
|
for (TQValueList<MonthPos>::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<Alarm*>(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<Alarm*>(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::tqfromLatin1("TEXT:");
|
|
static const TQString FILE_PREFIX = TQString::tqfromLatin1("FILE:");
|
|
static const TQString COMMAND_PREFIX = TQString::tqfromLatin1("CMD:");
|
|
|
|
// KAlarm pre-0.9.2 codes held in the event's CATEGORY property
|
|
static const TQString BEEP_CATEGORY = TQString::tqfromLatin1("BEEP");
|
|
|
|
// KAlarm pre-1.1.1 LATECANCEL category with no parameter
|
|
static const TQString LATE_CANCEL_CAT = TQString::tqfromLatin1("LATECANCEL");
|
|
|
|
// KAlarm pre-1.3.0 TMPLDEFTIME category with no parameter
|
|
static const TQString TEMPL_DEF_TIME_CAT = TQString::tqfromLatin1("TMPLDEFTIME");
|
|
|
|
// KAlarm pre-1.3.1 XTERM category
|
|
static const TQString EXEC_IN_XTERM_CAT = TQString::tqfromLatin1("XTERM");
|
|
|
|
// KAlarm pre-1.4.22 properties
|
|
static const TQCString KMAIL_ID_PROPERTY("KMAILID"); // X-KDE-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-KDE-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-KDE-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::tqfromLatin1("%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-KDE-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:" << mBgColour.name() << ":\n";
|
|
kdDebug(5950) << "-- mFgColour:" << mFgColour.name() << ":\n";
|
|
kdDebug(5950) << "-- mDefaultFont:" << (mDefaultFont ? "true" : "false") << ":\n";
|
|
if (!mDefaultFont)
|
|
kdDebug(5950) << "-- mFont:" << 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<Person>& addresses)
|
|
{
|
|
clear();
|
|
for (TQValueList<Person>::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<Person>::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::null;
|
|
TQString arguments = TQString::null;
|
|
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);
|
|
}
|