• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

kalarm

alarmevent.cpp

Go to the documentation of this file.
00001 /*
00002  *  alarmevent.cpp  -  represents calendar alarms and events
00003  *  Program:  kalarm
00004  *  Copyright © 2001-2008 by David Jarvie <djarvie@kde.org>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License along
00017  *  with this program; if not, write to the Free Software Foundation, Inc.,
00018  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  */
00020 
00021 #include "kalarm.h"   //krazy:exclude=includes (kalarm.h must be first)
00022 #include "alarmevent.h"
00023 
00024 #include "alarmcalendar.h"
00025 #include "alarmtext.h"
00026 #include "functions.h"
00027 #include "kalarmapp.h"
00028 #include "preferences.h"
00029 
00030 #include <stdlib.h>
00031 #include <time.h>
00032 #include <ctype.h>
00033 #include <QColor>
00034 #include <QRegExp>
00035 #include <QByteArray>
00036 
00037 #include <ksystemtimezone.h>
00038 #include <klocale.h>
00039 #include <kdebug.h>
00040 
00041 #include <kcal/calendarlocal.h>
00042 
00043 using namespace KCal;
00044 
00045 
00046 // KAlarm version which first used the current calendar/event format.
00047 // If this changes, KAEvent::convertKCalEvents() must be changed correspondingly.
00048 // The string version is the KAlarm version string used in the calendar file.
00049 QString KAEvent::calVersionString()  { return QString::fromLatin1("1.9.10"); }
00050 int     KAEvent::calVersion()        { return KAlarm::Version(1,9,10); }
00051 
00052 // Custom calendar properties.
00053 // Note that all custom property names are prefixed with X-KDE-KALARM- in the calendar file.
00054 
00055 // Event properties
00056 static const QByteArray FLAGS_PROPERTY("FLAGS");              // X-KDE-KALARM-FLAGS property
00057 static const QString DATE_ONLY_FLAG        = QLatin1String("DATE");
00058 static const QString EMAIL_BCC_FLAG        = QLatin1String("BCC");
00059 static const QString CONFIRM_ACK_FLAG      = QLatin1String("ACKCONF");
00060 static const QString KORGANIZER_FLAG       = QLatin1String("KORG");
00061 static const QString WORK_TIME_ONLY_FLAG   = QLatin1String("WORKTIME");
00062 static const QString DEFER_FLAG            = QLatin1String("DEFER");
00063 static const QString LATE_CANCEL_FLAG      = QLatin1String("LATECANCEL");
00064 static const QString AUTO_CLOSE_FLAG       = QLatin1String("LATECLOSE");
00065 static const QString TEMPL_AFTER_TIME_FLAG = QLatin1String("TMPLAFTTIME");
00066 static const QString KMAIL_SERNUM_FLAG     = QLatin1String("KMAIL");
00067 
00068 static const QByteArray NEXT_RECUR_PROPERTY("NEXTRECUR");     // X-KDE-KALARM-NEXTRECUR property
00069 static const QByteArray REPEAT_PROPERTY("REPEAT");            // X-KDE-KALARM-REPEAT property
00070 static const QByteArray ARCHIVE_PROPERTY("ARCHIVE");          // X-KDE-KALARM-ARCHIVE property
00071 static const QString ARCHIVE_REMINDER_ONCE_TYPE = QLatin1String("ONCE");
00072 static const QByteArray LOG_PROPERTY("LOG");                  // X-KDE-KALARM-LOG property
00073 static const QString xtermURL = QLatin1String("xterm:");
00074 static const QString displayURL = QLatin1String("display:");
00075 
00076 // - General alarm properties
00077 static const QByteArray TYPE_PROPERTY("TYPE");                // X-KDE-KALARM-TYPE property
00078 static const QString FILE_TYPE                  = QLatin1String("FILE");
00079 static const QString AT_LOGIN_TYPE              = QLatin1String("LOGIN");
00080 static const QString REMINDER_TYPE              = QLatin1String("REMINDER");
00081 static const QString REMINDER_ONCE_TYPE         = QLatin1String("REMINDER_ONCE");
00082 static const QString TIME_DEFERRAL_TYPE         = QLatin1String("DEFERRAL");
00083 static const QString DATE_DEFERRAL_TYPE         = QLatin1String("DATE_DEFERRAL");
00084 static const QString DISPLAYING_TYPE            = QLatin1String("DISPLAYING");   // used only in displaying calendar
00085 static const QString PRE_ACTION_TYPE            = QLatin1String("PRE");
00086 static const QString POST_ACTION_TYPE           = QLatin1String("POST");
00087 static const QByteArray NEXT_REPEAT_PROPERTY("NEXTREPEAT");   // X-KDE-KALARM-NEXTREPEAT property
00088 // - Display alarm properties
00089 static const QByteArray FONT_COLOUR_PROPERTY("FONTCOLOR");    // X-KDE-KALARM-FONTCOLOR property
00090 // - Email alarm properties
00091 static const QByteArray EMAIL_ID_PROPERTY("EMAILID");         // X-KDE-KALARM-EMAILID property
00092 // - Audio alarm properties
00093 static const QByteArray VOLUME_PROPERTY("VOLUME");            // X-KDE-KALARM-VOLUME property
00094 static const QByteArray SPEAK_PROPERTY("SPEAK");              // X-KDE-KALARM-SPEAK property
00095 // - Command alarm properties
00096 static const QByteArray CANCEL_ON_ERROR_PROPERTY("ERRCANCEL");// X-KDE-KALARM-ERRCANCEL property
00097 
00098 // Event status strings
00099 static const QString DISABLED_STATUS            = QLatin1String("DISABLED");
00100 
00101 // Displaying event ID identifier
00102 static const QString DISP_DEFER = QLatin1String("DEFER");
00103 static const QString DISP_EDIT  = QLatin1String("EDIT");
00104 
00105 static const QString SC = QLatin1String(";");
00106 
00107 struct AlarmData
00108 {
00109     const Alarm*           alarm;
00110     QString                cleanText;       // text or audio file name
00111     uint                   emailFromId;
00112     QFont                  font;
00113     QColor                 bgColour, fgColour;
00114     float                  soundVolume;
00115     float                  fadeVolume;
00116     int                    fadeSeconds;
00117     int                    nextRepeat;
00118     bool                   speak;
00119     KAAlarm::SubType       type;
00120     KAAlarmEventBase::Type action;
00121     int                    displayingFlags;
00122     bool                   defaultFont;
00123     bool                   reminderOnceOnly;
00124     bool                   isEmailText;
00125     bool                   commandScript;
00126     bool                   cancelOnPreActErr;
00127 };
00128 typedef QMap<KAAlarm::SubType, AlarmData> AlarmMap;
00129 
00130 static void setProcedureAlarm(Alarm*, const QString& commandLine);
00131 
00132 
00133 /*=============================================================================
00134 = Class KAEvent
00135 = Corresponds to a KCal::Event instance.
00136 =============================================================================*/
00137 
00138 inline void KAEvent::set_deferral(DeferType type)
00139 {
00140     if (type)
00141     {
00142         if (!mDeferral)
00143             ++mAlarmCount;
00144     }
00145     else
00146     {
00147         if (mDeferral)
00148             --mAlarmCount;
00149     }
00150     mDeferral = type;
00151 }
00152 
00153 inline void KAEvent::set_reminder(int minutes)
00154 {
00155     if (!mReminderMinutes)
00156         ++mAlarmCount;
00157     mReminderMinutes        = minutes;
00158     mArchiveReminderMinutes = 0;
00159 }
00160 
00161 inline void KAEvent::set_archiveReminder()
00162 {
00163     if (mReminderMinutes)
00164         --mAlarmCount;
00165     mArchiveReminderMinutes = mReminderMinutes;
00166     mReminderMinutes        = 0;
00167 }
00168 
00169 
00170 void KAEvent::copy(const KAEvent& event)
00171 {
00172     KAAlarmEventBase::copy(event);
00173     mTemplateName            = event.mTemplateName;
00174     mResource                = event.mResource;
00175     mResourceId              = event.mResourceId;
00176     mAudioFile               = event.mAudioFile;
00177     mPreAction               = event.mPreAction;
00178     mPostAction              = event.mPostAction;
00179     mStartDateTime           = event.mStartDateTime;
00180     mSaveDateTime            = event.mSaveDateTime;
00181     mAtLoginDateTime         = event.mAtLoginDateTime;
00182     mDeferralTime            = event.mDeferralTime;
00183     mAllTrigger              = event.mAllTrigger;
00184     mMainTrigger             = event.mMainTrigger;
00185     mAllWorkTrigger          = event.mAllWorkTrigger;
00186     mMainWorkTrigger         = event.mMainWorkTrigger;
00187     mDisplayingTime          = event.mDisplayingTime;
00188     mDisplayingFlags         = event.mDisplayingFlags;
00189     mReminderMinutes         = event.mReminderMinutes;
00190     mArchiveReminderMinutes  = event.mArchiveReminderMinutes;
00191     mDeferDefaultMinutes     = event.mDeferDefaultMinutes;
00192     mRevision                = event.mRevision;
00193     mAlarmCount              = event.mAlarmCount;
00194     mDeferral                = event.mDeferral;
00195     mLogFile                 = event.mLogFile;
00196     mCategory                = event.mCategory;
00197     mCancelOnPreActErr       = event.mCancelOnPreActErr;
00198     mCommandXterm            = event.mCommandXterm;
00199     mCommandDisplay          = event.mCommandDisplay;
00200     mSpeak                   = event.mSpeak;
00201     mKMailSerialNumber       = event.mKMailSerialNumber;
00202     mCopyToKOrganizer        = event.mCopyToKOrganizer;
00203     mWorkTimeOnly            = event.mWorkTimeOnly;
00204     mReminderOnceOnly        = event.mReminderOnceOnly;
00205     mMainExpired             = event.mMainExpired;
00206     mArchiveRepeatAtLogin    = event.mArchiveRepeatAtLogin;
00207     mArchive                 = event.mArchive;
00208     mTemplateAfterTime       = event.mTemplateAfterTime;
00209     mDisplayingDefer         = event.mDisplayingDefer;
00210     mDisplayingEdit          = event.mDisplayingEdit;
00211     mEnabled                 = event.mEnabled;
00212     mUpdated                 = event.mUpdated;
00213     mChangeCount             = 0;
00214     mChanged                 = false;
00215     delete mRecurrence;
00216     if (event.mRecurrence)
00217         mRecurrence = new KARecurrence(*event.mRecurrence);
00218     else
00219         mRecurrence = 0;
00220     if (event.mChanged)
00221         calcTriggerTimes();
00222 }
00223 
00224 /******************************************************************************
00225 * Initialise the KAEvent from a KCal::Event.
00226 */
00227 void KAEvent::set(const Event* event)
00228 {
00229     startChanges();
00230     // Extract status from the event
00231     mEventID                = event->uid();
00232     mRevision               = event->revision();
00233     mTemplateName.clear();
00234     mLogFile.clear();
00235     mResourceId.clear();
00236     mTemplateAfterTime      = -1;
00237     mBeep                   = false;
00238     mSpeak                  = false;
00239     mEmailBcc               = false;
00240     mCommandXterm           = false;
00241     mCommandDisplay         = false;
00242     mCopyToKOrganizer       = false;
00243     mWorkTimeOnly           = false;
00244     mConfirmAck             = false;
00245     mArchive                = false;
00246     mReminderOnceOnly       = false;
00247     mAutoClose              = false;
00248     mArchiveRepeatAtLogin   = false;
00249     mDisplayingDefer        = false;
00250     mDisplayingEdit         = false;
00251     mResource               = 0;
00252     mArchiveReminderMinutes = 0;
00253     mDeferDefaultMinutes    = 0;
00254     mLateCancel             = 0;
00255     mKMailSerialNumber      = 0;
00256     mChangeCount            = 0;
00257     mChanged                = false;
00258     mBgColour               = QColor(255, 255, 255);    // missing/invalid colour - return white background
00259     mFgColour               = QColor(0, 0, 0);          // and black foreground
00260     mDefaultFont            = true;
00261     mEnabled                = true;
00262     clearRecur();
00263     QString param;
00264     mCategory               = KCalEvent::status(event, &param);
00265     if (mCategory == KCalEvent::DISPLAYING)
00266     {
00267         // It's a displaying calendar event - set values specific to displaying alarms
00268         QStringList params = param.split(SC, QString::KeepEmptyParts);
00269         int n = params.count();
00270         if (n)
00271         {
00272             mResourceId = params[0];
00273             for (int i = 1;  i < n;  ++i)
00274             {
00275                 if (params[i] == DISP_DEFER)
00276                     mDisplayingDefer = true;
00277                 if (params[i] == DISP_EDIT)
00278                     mDisplayingEdit = true;
00279             }
00280         }
00281     }
00282     bool ok;
00283     bool dateOnly = false;
00284     QStringList flags = event->customProperty(KCalendar::APPNAME, FLAGS_PROPERTY).split(SC, QString::SkipEmptyParts);
00285     flags += QString();    // to avoid having to check for end of list
00286     for (int i = 0, end = flags.count() - 1;  i < end;  ++i)
00287     {
00288         if (flags[i] == DATE_ONLY_FLAG)
00289             dateOnly = true;
00290         else if (flags[i] == CONFIRM_ACK_FLAG)
00291             mConfirmAck = true;
00292         else if (flags[i] == EMAIL_BCC_FLAG)
00293             mEmailBcc = true;
00294         else if (flags[i] == KORGANIZER_FLAG)
00295             mCopyToKOrganizer = true;
00296         else if (flags[i] == WORK_TIME_ONLY_FLAG)
00297             mWorkTimeOnly = true;
00298         else if (flags[i]== KMAIL_SERNUM_FLAG)
00299         {
00300             unsigned long n = flags[i + 1].toULong(&ok);
00301             if (!ok)
00302                 continue;
00303             mKMailSerialNumber = n;
00304             ++i;
00305         }
00306         else if (flags[i] == DEFER_FLAG)
00307         {
00308             int n = static_cast<int>(flags[i + 1].toUInt(&ok));
00309             if (!ok)
00310                 continue;
00311             mDeferDefaultMinutes = n;
00312             ++i;
00313         }
00314         else if (flags[i] == TEMPL_AFTER_TIME_FLAG)
00315         {
00316             int n = static_cast<int>(flags[i + 1].toUInt(&ok));
00317             if (!ok)
00318                 continue;
00319             mTemplateAfterTime = n;
00320             ++i;
00321         }
00322         else if (flags[i] == LATE_CANCEL_FLAG)
00323         {
00324             mLateCancel = static_cast<int>(flags[i + 1].toUInt(&ok));
00325             if (ok)
00326                 ++i;
00327             if (!ok  ||  !mLateCancel)
00328                 mLateCancel = 1;    // invalid parameter defaults to 1 minute
00329         }
00330         else if (flags[i] == AUTO_CLOSE_FLAG)
00331         {
00332             mLateCancel = static_cast<int>(flags[i + 1].toUInt(&ok));
00333             if (ok)
00334                 ++i;
00335             if (!ok  ||  !mLateCancel)
00336                 mLateCancel = 1;    // invalid parameter defaults to 1 minute
00337             mAutoClose = true;
00338         }
00339     }
00340 
00341     QString prop = event->customProperty(KCalendar::APPNAME, LOG_PROPERTY);
00342     if (!prop.isEmpty())
00343     {
00344         if (prop == xtermURL)
00345             mCommandXterm = true;
00346         else if (prop == displayURL)
00347             mCommandDisplay = true;
00348         else
00349             mLogFile = prop;
00350     }
00351     prop = event->customProperty(KCalendar::APPNAME, REPEAT_PROPERTY);
00352     if (!prop.isEmpty())
00353     {
00354         // This property is used when the main alarm has expired
00355         QStringList list = prop.split(QLatin1Char(':'));
00356         if (list.count() >= 2)
00357         {
00358             int interval = static_cast<int>(list[0].toUInt());
00359             int count = static_cast<int>(list[1].toUInt());
00360             if (interval && count)
00361             {
00362                 if (interval % (24*60))
00363                     mRepeatInterval = Duration(interval * 60, Duration::Seconds);
00364                 else
00365                     mRepeatInterval = Duration(interval / (24*60), Duration::Days);
00366                 mRepeatCount = count;
00367             }
00368         }
00369     }
00370     prop = event->customProperty(KCalendar::APPNAME, ARCHIVE_PROPERTY);
00371     if (!prop.isEmpty())
00372     {
00373         mArchive = true;
00374         if (prop != QLatin1String("0"))
00375         {
00376             // It's the archive property containing a reminder time and/or repeat-at-login flag
00377             QStringList list = prop.split(SC, QString::SkipEmptyParts);
00378             for (int j = 0;  j < list.count();  ++j)
00379             {
00380                 if (list[j] == AT_LOGIN_TYPE)
00381                     mArchiveRepeatAtLogin = true;
00382                 else if (list[j] == ARCHIVE_REMINDER_ONCE_TYPE)
00383                     mReminderOnceOnly = true;
00384                 else
00385                 {
00386                     char ch;
00387                     const char* cat = list[j].toLatin1().constData();
00388                     while ((ch = *cat) != 0  &&  (ch < '0' || ch > '9'))
00389                         ++cat;
00390                     if (ch)
00391                     {
00392                         mArchiveReminderMinutes = ch - '0';
00393                         while ((ch = *++cat) >= '0'  &&  ch <= '9')
00394                             mArchiveReminderMinutes = mArchiveReminderMinutes * 10 + ch - '0';
00395                         switch (ch)
00396                         {
00397                             case 'M':  break;
00398                             case 'H':  mArchiveReminderMinutes *= 60;    break;
00399                             case 'D':  mArchiveReminderMinutes *= 1440;  break;
00400                         }
00401                     }
00402                 }
00403             }
00404         }
00405     }
00406     mNextMainDateTime = readDateTime(event, dateOnly, mStartDateTime);
00407     mSaveDateTime = event->created();
00408     if (dateOnly  &&  !mRepeatInterval.isDaily())
00409         mRepeatInterval = Duration(mRepeatInterval.asDays(), Duration::Days);
00410     if (category() == KCalEvent::TEMPLATE)
00411         mTemplateName = event->summary();
00412     if (event->statusStr() == DISABLED_STATUS)
00413         mEnabled = false;
00414 
00415     // Extract status from the event's alarms.
00416     // First set up defaults.
00417     mActionType        = T_MESSAGE;
00418     mMainExpired       = true;
00419     mRepeatAtLogin     = false;
00420     mDisplaying        = false;
00421     mRepeatSound       = false;
00422     mCommandScript     = false;
00423     mCancelOnPreActErr = false;
00424     mDeferral          = NO_DEFERRAL;
00425     mSoundVolume       = -1;
00426     mFadeVolume        = -1;
00427     mFadeSeconds       = 0;
00428     mReminderMinutes   = 0;
00429     mEmailFromIdentity = 0;
00430     mText              = "";
00431     mAudioFile         = "";
00432     mPreAction         = "";
00433     mPostAction        = "";
00434     mEmailSubject      = "";
00435     mEmailAddresses.clear();
00436     mEmailAttachments.clear();
00437 
00438     // Extract data from all the event's alarms and index the alarms by sequence number
00439     AlarmMap alarmMap;
00440     readAlarms(event, &alarmMap, mCommandDisplay);
00441 
00442     // Incorporate the alarms' details into the overall event
00443     mAlarmCount = 0;       // initialise as invalid
00444     DateTime alTime;
00445     bool set = false;
00446     bool isEmailText = false;
00447     bool setDeferralTime = false;
00448     Duration deferralOffset;
00449     for (AlarmMap::ConstIterator it = alarmMap.begin();  it != alarmMap.end();  ++it)
00450     {
00451         const AlarmData& data = it.value();
00452         DateTime dateTime = data.alarm->hasStartOffset() ? data.alarm->startOffset().end(mNextMainDateTime.effectiveKDateTime()) : data.alarm->time();
00453         switch (data.type)
00454         {
00455             case KAAlarm::MAIN__ALARM:
00456                 mMainExpired = false;
00457                 alTime = dateTime;
00458                 alTime.setDateOnly(mStartDateTime.isDateOnly());
00459                 if (data.alarm->repeatCount()  &&  data.alarm->snoozeTime())
00460                 {
00461                     mRepeatInterval = data.alarm->snoozeTime();   // values may be adjusted in setRecurrence()
00462                     mRepeatCount    = data.alarm->repeatCount();
00463                     mNextRepeat     = data.nextRepeat;
00464                 }
00465                 break;
00466             case KAAlarm::AT_LOGIN__ALARM:
00467                 mRepeatAtLogin   = true;
00468                 mAtLoginDateTime = dateTime.kDateTime();
00469                 alTime = mAtLoginDateTime;
00470                 break;
00471             case KAAlarm::REMINDER__ALARM:
00472                 // N.B. there can be a start offset but no valid date/time (e.g. in template)
00473                 mReminderMinutes = -(data.alarm->startOffset().asSeconds() / 60);
00474                 if (mReminderMinutes)
00475                     mArchiveReminderMinutes = 0;
00476                 break;
00477             case KAAlarm::DEFERRED_REMINDER_DATE__ALARM:
00478             case KAAlarm::DEFERRED_DATE__ALARM:
00479                 mDeferral = (data.type == KAAlarm::DEFERRED_REMINDER_DATE__ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL;
00480                 mDeferralTime = dateTime;
00481                 mDeferralTime.setDateOnly(true);
00482                 if (data.alarm->hasStartOffset())
00483                     deferralOffset = data.alarm->startOffset();
00484                 break;
00485             case KAAlarm::DEFERRED_REMINDER_TIME__ALARM:
00486             case KAAlarm::DEFERRED_TIME__ALARM:
00487                 mDeferral = (data.type == KAAlarm::DEFERRED_REMINDER_TIME__ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL;
00488                 mDeferralTime = dateTime;
00489                 if (data.alarm->hasStartOffset())
00490                     deferralOffset = data.alarm->startOffset();
00491                 break;
00492             case KAAlarm::DISPLAYING__ALARM:
00493             {
00494                 mDisplaying      = true;
00495                 mDisplayingFlags = data.displayingFlags;
00496                 bool dateOnly = (mDisplayingFlags & DEFERRAL) ? !(mDisplayingFlags & TIMED_FLAG)
00497                               : mStartDateTime.isDateOnly();
00498                 mDisplayingTime = dateTime;
00499                 mDisplayingTime.setDateOnly(dateOnly);
00500                 alTime = mDisplayingTime;
00501                 break;
00502             }
00503             case KAAlarm::AUDIO__ALARM:
00504                 mAudioFile   = data.cleanText;
00505                 mSpeak       = data.speak  &&  mAudioFile.isEmpty();
00506                 mBeep        = !mSpeak  &&  mAudioFile.isEmpty();
00507                 mSoundVolume = (!mBeep && !mSpeak) ? data.soundVolume : -1;
00508                 mFadeVolume  = (mSoundVolume >= 0  &&  data.fadeSeconds > 0) ? data.fadeVolume : -1;
00509                 mFadeSeconds = (mFadeVolume >= 0) ? data.fadeSeconds : 0;
00510                 mRepeatSound = (!mBeep && !mSpeak)  &&  (data.alarm->repeatCount() < 0);
00511                 break;
00512             case KAAlarm::PRE_ACTION__ALARM:
00513                 mPreAction         = data.cleanText;
00514                 mCancelOnPreActErr = data.cancelOnPreActErr;
00515                 break;
00516             case KAAlarm::POST_ACTION__ALARM:
00517                 mPostAction = data.cleanText;
00518                 break;
00519             case KAAlarm::INVALID__ALARM:
00520             default:
00521                 break;
00522         }
00523 
00524         if (data.reminderOnceOnly)
00525             mReminderOnceOnly = true;
00526         bool noSetNextTime = false;
00527         switch (data.type)
00528         {
00529             case KAAlarm::DEFERRED_REMINDER_DATE__ALARM:
00530             case KAAlarm::DEFERRED_DATE__ALARM:
00531             case KAAlarm::DEFERRED_REMINDER_TIME__ALARM:
00532             case KAAlarm::DEFERRED_TIME__ALARM:
00533                 if (!set)
00534                 {
00535                     // The recurrence has to be evaluated before we can
00536                     // calculate the time of a deferral alarm.
00537                     setDeferralTime = true;
00538                     noSetNextTime = true;
00539                 }
00540                 // fall through to AT_LOGIN__ALARM etc.
00541             case KAAlarm::AT_LOGIN__ALARM:
00542             case KAAlarm::REMINDER__ALARM:
00543             case KAAlarm::DISPLAYING__ALARM:
00544                 if (!set  &&  !noSetNextTime)
00545                     mNextMainDateTime = alTime;
00546                 // fall through to MAIN__ALARM
00547             case KAAlarm::MAIN__ALARM:
00548                 // Ensure that the basic fields are set up even if there is no main
00549                 // alarm in the event (if it has expired and then been deferred)
00550                 if (!set)
00551                 {
00552                     mActionType = data.action;
00553                     mText = (mActionType == T_COMMAND) ? data.cleanText.trimmed() : data.cleanText;
00554                     switch (data.action)
00555                     {
00556                         case T_COMMAND:
00557                             mCommandScript = data.commandScript;
00558                             if (!mCommandDisplay)
00559                                 break;
00560                             // fall through to T_MESSAGE
00561                         case T_MESSAGE:
00562                             mFont        = data.font;
00563                             mDefaultFont = data.defaultFont;
00564                             if (data.isEmailText)
00565                                 isEmailText = true;
00566                             // fall through to T_FILE
00567                         case T_FILE:
00568                             mBgColour    = data.bgColour;
00569                             mFgColour    = data.fgColour;
00570                             break;
00571                         case T_EMAIL:
00572                             mEmailFromIdentity = data.emailFromId;
00573                             mEmailAddresses    = data.alarm->mailAddresses();
00574                             mEmailSubject      = data.alarm->mailSubject();
00575                             mEmailAttachments  = data.alarm->mailAttachments();
00576                             break;
00577                         default:
00578                             break;
00579                     }
00580                     set = true;
00581                 }
00582                 if (data.action == T_FILE  &&  mActionType == T_MESSAGE)
00583                     mActionType = T_FILE;
00584                 ++mAlarmCount;
00585                 break;
00586             case KAAlarm::AUDIO__ALARM:
00587             case KAAlarm::PRE_ACTION__ALARM:
00588             case KAAlarm::POST_ACTION__ALARM:
00589             case KAAlarm::INVALID__ALARM:
00590             default:
00591                 break;
00592         }
00593     }
00594     if (!isEmailText)
00595         mKMailSerialNumber = 0;
00596     if (mRepeatAtLogin)
00597         mArchiveRepeatAtLogin = false;
00598 
00599     Recurrence* recur = event->recurrence();
00600     if (recur  &&  recur->recurs())
00601     {
00602         int nextRepeat = mNextRepeat;    // setRecurrence() clears mNextRepeat
00603         setRecurrence(*recur);
00604         if (nextRepeat <= mRepeatCount)
00605             mNextRepeat = nextRepeat;
00606     }
00607     else
00608         checkRepetition();
00609 
00610     if (mMainExpired  &&  deferralOffset  &&  checkRecur() != KARecurrence::NO_RECUR)
00611     {
00612         // Adjust the deferral time for an expired recurrence, since the
00613         // offset is relative to the first actual occurrence.
00614         DateTime dt = mRecurrence->getNextDateTime(mStartDateTime.addDays(-1).kDateTime());
00615         dt.setDateOnly(mStartDateTime.isDateOnly());
00616         if (mDeferralTime.isDateOnly())
00617         {
00618             mDeferralTime = deferralOffset.end(dt.kDateTime());
00619             mDeferralTime.setDateOnly(true);
00620         }
00621         else
00622             mDeferralTime = deferralOffset.end(dt.effectiveKDateTime());
00623     }
00624     if (mDeferral)
00625     {
00626         if (mNextMainDateTime == mDeferralTime)
00627             mDeferral = CANCEL_DEFERRAL;     // it's a cancelled deferral
00628         if (setDeferralTime)
00629             mNextMainDateTime = mDeferralTime;
00630     }
00631     mChanged = true;
00632     endChanges();
00633 
00634     mUpdated = false;
00635 }
00636 
00637 /******************************************************************************
00638 * Fetch the start and next date/time for a KCal::Event.
00639 * Reply = next main date/time.
00640 */
00641 DateTime KAEvent::readDateTime(const Event* event, bool dateOnly, DateTime& start)
00642 {
00643     start = event->dtStart();
00644     if (dateOnly)
00645     {
00646         // A date-only event is indicated by the X-KDE-KALARM-FLAGS:DATE property, not
00647         // by a date-only start date/time (for the reasons given in updateKCalEvent()).
00648         start.setDateOnly(true);
00649     }
00650     DateTime next = start;
00651     QString prop = event->customProperty(KCalendar::APPNAME, NEXT_RECUR_PROPERTY);
00652     if (prop.length() >= 8)
00653     {
00654         // The next due recurrence time is specified
00655         QDate d(prop.left(4).toInt(), prop.mid(4,2).toInt(), prop.mid(6,2).toInt());
00656         if (d.isValid())
00657         {
00658             if (dateOnly  &&  prop.length() == 8)
00659                 next.setDate(d);
00660             else if (!dateOnly  &&  prop.length() == 15  &&  prop[8] == QChar('T'))
00661             {
00662                 QTime t(prop.mid(9,2).toInt(), prop.mid(11,2).toInt(), prop.mid(13,2).toInt());
00663                 if (t.isValid())
00664                 {
00665                     next.setDate(d);
00666                     next.setTime(t);
00667                 }
00668             }
00669         }
00670     }
00671     return next;
00672 }
00673 
00674 /******************************************************************************
00675 * Parse the alarms for a KCal::Event.
00676 * Reply = map of alarm data, indexed by KAAlarm::Type
00677 */
00678 void KAEvent::readAlarms(const Event* event, void* almap, bool cmdDisplay)
00679 {
00680     AlarmMap* alarmMap = (AlarmMap*)almap;
00681     Alarm::List alarms = event->alarms();
00682     for (Alarm::List::ConstIterator it = alarms.begin();  it != alarms.end();  ++it)
00683     {
00684         // Parse the next alarm's text
00685         AlarmData data;
00686         readAlarm(*it, data, cmdDisplay);
00687         if (data.type != KAAlarm::INVALID__ALARM)
00688             alarmMap->insert(data.type, data);
00689     }
00690 }
00691 
00692 /******************************************************************************
00693 * Parse a KCal::Alarm.
00694 * Reply = alarm ID (sequence number)
00695 */
00696 void KAEvent::readAlarm(const Alarm* alarm, AlarmData& data, bool cmdDisplay)
00697 {
00698     // Parse the next alarm's text
00699     data.alarm           = alarm;
00700     data.displayingFlags = 0;
00701     data.isEmailText     = false;
00702     data.nextRepeat      = 0;
00703     if (alarm->repeatCount())
00704     {
00705         bool ok;
00706         QString property = alarm->customProperty(KCalendar::APPNAME, NEXT_REPEAT_PROPERTY);
00707         int n = static_cast<int>(property.toUInt(&ok));
00708         if (ok)
00709             data.nextRepeat = n;
00710     }
00711     switch (alarm->type())
00712     {
00713         case Alarm::Procedure:
00714             data.action        = T_COMMAND;
00715             data.cleanText     = alarm->programFile();
00716             data.commandScript = data.cleanText.isEmpty();   // blank command indicates a script
00717             if (!alarm->programArguments().isEmpty())
00718             {
00719                 if (!data.commandScript)
00720                     data.cleanText += ' ';
00721                 data.cleanText += alarm->programArguments();
00722             }
00723             data.cancelOnPreActErr = !alarm->customProperty(KCalendar::APPNAME, CANCEL_ON_ERROR_PROPERTY).isNull();
00724             if (!cmdDisplay)
00725                 break;
00726             // fall through to Display
00727         case Alarm::Display:
00728         {
00729             if (alarm->type() == Alarm::Display)
00730             {
00731                 data.action    = T_MESSAGE;
00732                 data.cleanText = AlarmText::fromCalendarText(alarm->text(), data.isEmailText);
00733             }
00734             QString property = alarm->customProperty(KCalendar::APPNAME, FONT_COLOUR_PROPERTY);
00735             QStringList list = property.split(QLatin1Char(';'), QString::KeepEmptyParts);
00736             data.bgColour = QColor(255, 255, 255);   // white
00737             data.fgColour = QColor(0, 0, 0);         // black
00738             int n = list.count();
00739             if (n > 0)
00740             {
00741                 if (!list[0].isEmpty())
00742                 {
00743                     QColor c(list[0]);
00744                     if (c.isValid())
00745                         data.bgColour = c;
00746                 }
00747                 if (n > 1  &&  !list[1].isEmpty())
00748                 {
00749                     QColor c(list[1]);
00750                     if (c.isValid())
00751                         data.fgColour = c;
00752                 }
00753             }
00754             data.defaultFont = (n <= 2 || list[2].isEmpty());
00755             if (!data.defaultFont)
00756                 data.font.fromString(list[2]);
00757             break;
00758         }
00759         case Alarm::Email:
00760             data.action      = T_EMAIL;
00761             data.emailFromId = alarm->customProperty(KCalendar::APPNAME, EMAIL_ID_PROPERTY).toUInt();
00762             data.cleanText   = alarm->mailText();
00763             break;
00764         case Alarm::Audio:
00765         {
00766             data.action      = T_AUDIO;
00767             data.cleanText   = alarm->audioFile();
00768             data.type        = KAAlarm::AUDIO__ALARM;
00769             data.soundVolume = -1;
00770             data.fadeVolume  = -1;
00771             data.fadeSeconds = 0;
00772             data.speak       = !alarm->customProperty(KCalendar::APPNAME, SPEAK_PROPERTY).isNull();
00773             QString property = alarm->customProperty(KCalendar::APPNAME, VOLUME_PROPERTY);
00774             if (!property.isEmpty())
00775             {
00776                 bool ok;
00777                 float fadeVolume;
00778                 int   fadeSecs = 0;
00779                 QStringList list = property.split(QLatin1Char(';'), QString::KeepEmptyParts);
00780                 data.soundVolume = list[0].toFloat(&ok);
00781                 if (!ok)
00782                     data.soundVolume = -1;
00783                 if (data.soundVolume >= 0  &&  list.count() >= 3)
00784                 {
00785                     fadeVolume = list[1].toFloat(&ok);
00786                     if (ok)
00787                         fadeSecs = static_cast<int>(list[2].toUInt(&ok));
00788                     if (ok  &&  fadeVolume >= 0  &&  fadeSecs > 0)
00789                     {
00790                         data.fadeVolume  = fadeVolume;
00791                         data.fadeSeconds = fadeSecs;
00792                     }
00793                 }
00794             }
00795             return;
00796         }
00797         case Alarm::Invalid:
00798             data.type = KAAlarm::INVALID__ALARM;
00799             return;
00800     }
00801 
00802     bool atLogin          = false;
00803     bool reminder         = false;
00804     bool deferral         = false;
00805     bool dateDeferral     = false;
00806     data.reminderOnceOnly = false;
00807     data.type = KAAlarm::MAIN__ALARM;
00808     QString property = alarm->customProperty(KCalendar::APPNAME, TYPE_PROPERTY);
00809     QStringList types = property.split(QLatin1Char(','), QString::SkipEmptyParts);
00810     for (int i = 0, end = types.count();  i < end;  ++i)
00811     {
00812         QString type = types[i];
00813         if (type == AT_LOGIN_TYPE)
00814             atLogin = true;
00815         else if (type == FILE_TYPE  &&  data.action == T_MESSAGE)
00816             data.action = T_FILE;
00817         else if (type == REMINDER_TYPE)
00818             reminder = true;
00819         else if (type == REMINDER_ONCE_TYPE)
00820             reminder = data.reminderOnceOnly = true;
00821         else if (type == TIME_DEFERRAL_TYPE)
00822             deferral = true;
00823         else if (type == DATE_DEFERRAL_TYPE)
00824             dateDeferral = deferral = true;
00825         else if (type == DISPLAYING_TYPE)
00826             data.type = KAAlarm::DISPLAYING__ALARM;
00827         else if (type == PRE_ACTION_TYPE  &&  data.action == T_COMMAND)
00828             data.type = KAAlarm::PRE_ACTION__ALARM;
00829         else if (type == POST_ACTION_TYPE  &&  data.action == T_COMMAND)
00830             data.type = KAAlarm::POST_ACTION__ALARM;
00831     }
00832 
00833     if (reminder)
00834     {
00835         if (data.type == KAAlarm::MAIN__ALARM)
00836             data.type = dateDeferral ? KAAlarm::DEFERRED_REMINDER_DATE__ALARM
00837                       : deferral ? KAAlarm::DEFERRED_REMINDER_TIME__ALARM : KAAlarm::REMINDER__ALARM;
00838         else if (data.type == KAAlarm::DISPLAYING__ALARM)
00839             data.displayingFlags = dateDeferral ? REMINDER | DATE_DEFERRAL
00840                                  : deferral ? REMINDER | TIME_DEFERRAL : REMINDER;
00841     }
00842     else if (deferral)
00843     {
00844         if (data.type == KAAlarm::MAIN__ALARM)
00845             data.type = dateDeferral ? KAAlarm::DEFERRED_DATE__ALARM : KAAlarm::DEFERRED_TIME__ALARM;
00846         else if (data.type == KAAlarm::DISPLAYING__ALARM)
00847             data.displayingFlags = dateDeferral ? DATE_DEFERRAL : TIME_DEFERRAL;
00848     }
00849     if (atLogin)
00850     {
00851         if (data.type == KAAlarm::MAIN__ALARM)
00852             data.type = KAAlarm::AT_LOGIN__ALARM;
00853         else if (data.type == KAAlarm::DISPLAYING__ALARM)
00854             data.displayingFlags = REPEAT_AT_LOGIN;
00855     }
00856 //kDebug()<<"text="<<alarm->text()<<", time="<<alarm->time().toString()<<", valid time="<<alarm->time().isValid();
00857 }
00858 
00859 /******************************************************************************
00860 * Initialise the KAEvent with the specified parameters.
00861 */
00862 void KAEvent::set(const KDateTime& dateTime, const QString& text, const QColor& bg, const QColor& fg,
00863                   const QFont& font, Action action, int lateCancel, int flags, bool changesPending)
00864 {
00865     clearRecur();
00866     mStartDateTime = dateTime;
00867     mStartDateTime.setDateOnly(flags & ANY_TIME);
00868     mNextMainDateTime = mStartDateTime;
00869     switch (action)
00870     {
00871         case MESSAGE:
00872         case FILE:
00873         case COMMAND:
00874         case EMAIL:
00875