kalarm

alarmevent.cpp

00001 /*
00002  *  alarmevent.cpp  -  represents calendar alarms and events
00003  *  Program:  kalarm
00004  *  Copyright (c) 2001-2006 by David Jarvie <software@astrojar.org.uk>
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"
00022 
00023 #include <stdlib.h>
00024 #include <time.h>
00025 #include <ctype.h>
00026 #include <qcolor.h>
00027 #include <qregexp.h>
00028 
00029 #include <klocale.h>
00030 #include <kdebug.h>
00031 
00032 #include "alarmtext.h"
00033 #include "functions.h"
00034 #include "kalarmapp.h"
00035 #include "preferences.h"
00036 #include "alarmcalendar.h"
00037 #include "alarmevent.h"
00038 using namespace KCal;
00039 
00040 
00041 const QCString APPNAME("KALARM");
00042 
00043 // Custom calendar properties.
00044 // Note that all custom property names are prefixed with X-KDE-KALARM- in the calendar file.
00045 // - General alarm properties
00046 static const QCString TYPE_PROPERTY("TYPE");    // X-KDE-KALARM-TYPE property
00047 static const QString FILE_TYPE                  = QString::fromLatin1("FILE");
00048 static const QString AT_LOGIN_TYPE              = QString::fromLatin1("LOGIN");
00049 static const QString REMINDER_TYPE              = QString::fromLatin1("REMINDER");
00050 static const QString REMINDER_ONCE_TYPE         = QString::fromLatin1("REMINDER_ONCE");
00051 static const QString ARCHIVE_REMINDER_ONCE_TYPE = QString::fromLatin1("ONCE");
00052 static const QString TIME_DEFERRAL_TYPE         = QString::fromLatin1("DEFERRAL");
00053 static const QString DATE_DEFERRAL_TYPE         = QString::fromLatin1("DATE_DEFERRAL");
00054 static const QString DISPLAYING_TYPE            = QString::fromLatin1("DISPLAYING");   // used only in displaying calendar
00055 static const QString PRE_ACTION_TYPE            = QString::fromLatin1("PRE");
00056 static const QString POST_ACTION_TYPE           = QString::fromLatin1("POST");
00057 // - Display alarm properties
00058 static const QCString FONT_COLOUR_PROPERTY("FONTCOLOR");    // X-KDE-KALARM-FONTCOLOR property
00059 // - Email alarm properties
00060 static const QCString KMAIL_ID_PROPERTY("KMAILID");         // X-KDE-KALARM-KMAILID property
00061 // - Audio alarm properties
00062 static const QCString VOLUME_PROPERTY("VOLUME");            // X-KDE-KALARM-VOLUME property
00063 static const QCString SPEAK_PROPERTY("SPEAK");              // X-KDE-KALARM-SPEAK property
00064 
00065 // Event categories
00066 static const QString DATE_ONLY_CATEGORY        = QString::fromLatin1("DATE");
00067 static const QString EMAIL_BCC_CATEGORY        = QString::fromLatin1("BCC");
00068 static const QString CONFIRM_ACK_CATEGORY      = QString::fromLatin1("ACKCONF");
00069 static const QString LATE_CANCEL_CATEGORY      = QString::fromLatin1("LATECANCEL;");
00070 static const QString AUTO_CLOSE_CATEGORY       = QString::fromLatin1("LATECLOSE;");
00071 static const QString TEMPL_AFTER_TIME_CATEGORY = QString::fromLatin1("TMPLAFTTIME;");
00072 static const QString KMAIL_SERNUM_CATEGORY     = QString::fromLatin1("KMAIL:");
00073 static const QString KORGANIZER_CATEGORY       = QString::fromLatin1("KORG");
00074 static const QString ARCHIVE_CATEGORY          = QString::fromLatin1("SAVE");
00075 static const QString ARCHIVE_CATEGORIES        = QString::fromLatin1("SAVE:");
00076 static const QString LOG_CATEGORY              = QString::fromLatin1("LOG:");
00077 static const QString xtermURL = QString::fromLatin1("xterm:");
00078 
00079 // Event status strings
00080 static const QString DISABLED_STATUS           = QString::fromLatin1("DISABLED");
00081 
00082 static const QString EXPIRED_UID    = QString::fromLatin1("-exp-");
00083 static const QString DISPLAYING_UID = QString::fromLatin1("-disp-");
00084 static const QString TEMPLATE_UID   = QString::fromLatin1("-tmpl-");
00085 static const QString KORGANIZER_UID = QString::fromLatin1("-korg-");
00086 
00087 struct AlarmData
00088 {
00089     const Alarm*           alarm;
00090     QString                cleanText;       // text or audio file name
00091     QString                emailFromKMail;
00092     EmailAddressList       emailAddresses;
00093     QString                emailSubject;
00094     QStringList            emailAttachments;
00095     QDateTime              dateTime;
00096     QFont                  font;
00097     QColor                 bgColour, fgColour;
00098     float                  soundVolume;
00099     float                  fadeVolume;
00100     int                    fadeSeconds;
00101     int                    startOffsetSecs;
00102     bool                   speak;
00103     KAAlarm::SubType       type;
00104     KAAlarmEventBase::Type action;
00105     int                    displayingFlags;
00106     bool                   defaultFont;
00107     bool                   reminderOnceOnly;
00108     bool                   isEmailText;
00109     bool                   commandScript;
00110     int                    repeatCount;
00111     int                    repeatInterval;
00112 };
00113 typedef QMap<KAAlarm::SubType, AlarmData> AlarmMap;
00114 
00115 static void setProcedureAlarm(Alarm*, const QString& commandLine);
00116 
00117 
00118 /*=============================================================================
00119 = Class KAEvent
00120 = Corresponds to a KCal::Event instance.
00121 =============================================================================*/
00122 
00123 inline void KAEvent::set_deferral(DeferType type)
00124 {
00125     if (type)
00126     {
00127         if (!mDeferral)
00128             ++mAlarmCount;
00129     }
00130     else
00131     {
00132         if (mDeferral)
00133             --mAlarmCount;
00134     }
00135     mDeferral = type;
00136 }
00137 
00138 inline void KAEvent::set_reminder(int minutes)
00139 {
00140     if (!mReminderMinutes)
00141         ++mAlarmCount;
00142     mReminderMinutes        = minutes;
00143     mArchiveReminderMinutes = 0;
00144 }
00145 
00146 inline void KAEvent::set_archiveReminder()
00147 {
00148     if (mReminderMinutes)
00149         --mAlarmCount;
00150     mArchiveReminderMinutes = mReminderMinutes;
00151     mReminderMinutes        = 0;
00152 }
00153 
00154 
00155 void KAEvent::copy(const KAEvent& event)
00156 {
00157     KAAlarmEventBase::copy(event);
00158     mTemplateName            = event.mTemplateName;
00159     mAudioFile               = event.mAudioFile;
00160     mPreAction               = event.mPreAction;
00161     mPostAction              = event.mPostAction;
00162     mStartDateTime           = event.mStartDateTime;
00163     mSaveDateTime            = event.mSaveDateTime;
00164     mAtLoginDateTime         = event.mAtLoginDateTime;
00165     mDeferralTime            = event.mDeferralTime;
00166     mDisplayingTime          = event.mDisplayingTime;
00167     mDisplayingFlags         = event.mDisplayingFlags;
00168     mReminderMinutes         = event.mReminderMinutes;
00169     mArchiveReminderMinutes  = event.mArchiveReminderMinutes;
00170     mRevision                = event.mRevision;
00171     mRemainingRecurrences    = event.mRemainingRecurrences;
00172     mAlarmCount              = event.mAlarmCount;
00173     mDeferral                = event.mDeferral;
00174     mLogFile                 = event.mLogFile;
00175     mCommandXterm            = event.mCommandXterm;
00176     mKMailSerialNumber       = event.mKMailSerialNumber;
00177     mCopyToKOrganizer        = event.mCopyToKOrganizer;
00178     mReminderOnceOnly        = event.mReminderOnceOnly;
00179     mMainExpired             = event.mMainExpired;
00180     mArchiveRepeatAtLogin    = event.mArchiveRepeatAtLogin;
00181     mArchive                 = event.mArchive;
00182     mTemplateAfterTime       = event.mTemplateAfterTime;
00183     mEnabled                 = event.mEnabled;
00184     mUpdated                 = event.mUpdated;
00185     delete mRecurrence;
00186     if (event.mRecurrence)
00187         mRecurrence = new KARecurrence(*event.mRecurrence);
00188     else
00189         mRecurrence = 0;
00190 }
00191 
00192 /******************************************************************************
00193  * Initialise the KAEvent from a KCal::Event.
00194  */
00195 void KAEvent::set(const Event& event)
00196 {
00197     // Extract status from the event
00198     mEventID                = event.uid();
00199     mRevision               = event.revision();
00200     mTemplateName           = QString::null;
00201     mLogFile                = QString::null;
00202     mTemplateAfterTime      = -1;
00203     mBeep                   = false;
00204     mSpeak                  = false;
00205     mEmailBcc               = false;
00206     mCommandXterm           = false;
00207     mCopyToKOrganizer       = false;
00208     mConfirmAck             = false;
00209     mArchive                = false;
00210     mReminderOnceOnly       = false;
00211     mAutoClose              = false;
00212     mArchiveRepeatAtLogin   = false;
00213     mArchiveReminderMinutes = 0;
00214     mLateCancel             = 0;
00215     mKMailSerialNumber      = 0;
00216     mBgColour               = QColor(255, 255, 255);    // missing/invalid colour - return white background
00217     mFgColour               = QColor(0, 0, 0);          // and black foreground
00218     mDefaultFont            = true;
00219     mEnabled                = true;
00220     bool floats = false;
00221     const QStringList& cats = event.categories();
00222     for (unsigned int i = 0;  i < cats.count();  ++i)
00223     {
00224         if (cats[i] == DATE_ONLY_CATEGORY)
00225             floats = true;
00226         else if (cats[i] == CONFIRM_ACK_CATEGORY)
00227             mConfirmAck = true;
00228         else if (cats[i] == EMAIL_BCC_CATEGORY)
00229             mEmailBcc = true;
00230         else if (cats[i] == ARCHIVE_CATEGORY)
00231             mArchive = true;
00232         else if (cats[i] == KORGANIZER_CATEGORY)
00233             mCopyToKOrganizer = true;
00234         else if (cats[i].startsWith(KMAIL_SERNUM_CATEGORY))
00235             mKMailSerialNumber = cats[i].mid(KMAIL_SERNUM_CATEGORY.length()).toULong();
00236         else if (cats[i].startsWith(LOG_CATEGORY))
00237         {
00238             QString logUrl = cats[i].mid(LOG_CATEGORY.length());
00239             if (logUrl == xtermURL)
00240                 mCommandXterm = true;
00241             else
00242                 mLogFile = logUrl;
00243         }
00244         else if (cats[i].startsWith(ARCHIVE_CATEGORIES))
00245         {
00246             // It's the archive flag plus a reminder time and/or repeat-at-login flag
00247             mArchive = true;
00248             QStringList list = QStringList::split(';', cats[i].mid(ARCHIVE_CATEGORIES.length()));
00249             for (unsigned int j = 0;  j < list.count();  ++j)
00250             {
00251                 if (list[j] == AT_LOGIN_TYPE)
00252                     mArchiveRepeatAtLogin = true;
00253                 else if (list[j] == ARCHIVE_REMINDER_ONCE_TYPE)
00254                     mReminderOnceOnly = true;
00255                 else
00256                 {
00257                     char ch;
00258                     const char* cat = list[j].latin1();
00259                     while ((ch = *cat) != 0  &&  (ch < '0' || ch > '9'))
00260                         ++cat;
00261                     if (ch)
00262                     {
00263                         mArchiveReminderMinutes = ch - '0';
00264                         while ((ch = *++cat) >= '0'  &&  ch <= '9')
00265                             mArchiveReminderMinutes = mArchiveReminderMinutes * 10 + ch - '0';
00266                         switch (ch)
00267                         {
00268                             case 'M':  break;
00269                             case 'H':  mArchiveReminderMinutes *= 60;    break;
00270                             case 'D':  mArchiveReminderMinutes *= 1440;  break;
00271                         }
00272                     }
00273                 }
00274             }
00275         }
00276         else if (cats[i].startsWith(TEMPL_AFTER_TIME_CATEGORY))
00277         {
00278             bool ok;
00279             mTemplateAfterTime = static_cast<int>(cats[i].mid(TEMPL_AFTER_TIME_CATEGORY.length()).toUInt(&ok));
00280             if (!ok)
00281                 mTemplateAfterTime = -1;    // invalid parameter
00282         }
00283         else if (cats[i].startsWith(LATE_CANCEL_CATEGORY))
00284         {
00285             bool ok;
00286             mLateCancel = static_cast<int>(cats[i].mid(LATE_CANCEL_CATEGORY.length()).toUInt(&ok));
00287             if (!ok  ||  !mLateCancel)
00288                 mLateCancel = 1;    // invalid parameter defaults to 1 minute
00289         }
00290         else if (cats[i].startsWith(AUTO_CLOSE_CATEGORY))
00291         {
00292             bool ok;
00293             mLateCancel = static_cast<int>(cats[i].mid(AUTO_CLOSE_CATEGORY.length()).toUInt(&ok));
00294             if (!ok  ||  !mLateCancel)
00295                 mLateCancel = 1;    // invalid parameter defaults to 1 minute
00296             mAutoClose = true;
00297         }
00298     }
00299     mStartDateTime.set(event.dtStart(), floats);
00300     mNextMainDateTime = mStartDateTime;
00301     mSaveDateTime     = event.created();
00302     if (uidStatus() == TEMPLATE)
00303         mTemplateName = event.summary();
00304     if (event.statusStr() == DISABLED_STATUS)
00305         mEnabled = false;
00306 
00307     // Extract status from the event's alarms.
00308     // First set up defaults.
00309     mActionType       = T_MESSAGE;
00310     mMainExpired      = true;
00311     mRepeatAtLogin    = false;
00312     mDisplaying       = false;
00313     mRepeatSound      = false;
00314     mCommandScript    = false;
00315     mDeferral         = NO_DEFERRAL;
00316     mSoundVolume      = -1;
00317     mFadeVolume       = -1;
00318     mFadeSeconds      = 0;
00319     mReminderMinutes  = 0;
00320     mRepeatInterval   = 0;
00321     mRepeatCount      = 0;
00322     mText             = "";
00323     mAudioFile        = "";
00324     mPreAction        = "";
00325     mPostAction       = "";
00326     mEmailFromKMail   = "";
00327     mEmailSubject     = "";
00328     mEmailAddresses.clear();
00329     mEmailAttachments.clear();
00330     clearRecur();
00331 
00332     // Extract data from all the event's alarms and index the alarms by sequence number
00333     AlarmMap alarmMap;
00334     readAlarms(event, &alarmMap);
00335 
00336     // Incorporate the alarms' details into the overall event
00337     AlarmMap::ConstIterator it = alarmMap.begin();
00338     mAlarmCount = 0;       // initialise as invalid
00339     DateTime alTime;
00340     bool set = false;
00341     bool isEmailText = false;
00342     for (  ;  it != alarmMap.end();  ++it)
00343     {
00344         const AlarmData& data = it.data();
00345         switch (data.type)
00346         {
00347             case KAAlarm::MAIN__ALARM:
00348                 mMainExpired = false;
00349                 alTime.set(data.dateTime, mStartDateTime.isDateOnly());
00350                 if (data.repeatCount  &&  data.repeatInterval)
00351                 {
00352                     mRepeatInterval = data.repeatInterval;   // values may be adjusted in setRecurrence()
00353                     mRepeatCount    = data.repeatCount;
00354                 }
00355                 break;
00356             case KAAlarm::AT_LOGIN__ALARM:
00357                 mRepeatAtLogin   = true;
00358                 mAtLoginDateTime = data.dateTime;
00359                 alTime = mAtLoginDateTime;
00360                 break;
00361             case KAAlarm::REMINDER__ALARM:
00362                 mReminderMinutes = -(data.startOffsetSecs / 60);
00363                 if (mReminderMinutes)
00364                     mArchiveReminderMinutes = 0;
00365                 break;
00366             case KAAlarm::DEFERRED_REMINDER_DATE__ALARM:
00367             case KAAlarm::DEFERRED_DATE__ALARM:
00368                 mDeferral = (data.type == KAAlarm::DEFERRED_REMINDER_DATE__ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL;
00369                 mDeferralTime.set(data.dateTime, false);
00370                 break;
00371             case KAAlarm::DEFERRED_REMINDER_TIME__ALARM:
00372             case KAAlarm::DEFERRED_TIME__ALARM:
00373                 mDeferral = (data.type == KAAlarm::DEFERRED_REMINDER_TIME__ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL;
00374                 mDeferralTime.set(data.dateTime);
00375                 break;
00376             case KAAlarm::DISPLAYING__ALARM:
00377             {
00378                 mDisplaying      = true;
00379                 mDisplayingFlags = data.displayingFlags;
00380                 bool dateOnly = (mDisplayingFlags & DEFERRAL) ? !(mDisplayingFlags & TIMED_FLAG)
00381                               : mStartDateTime.isDateOnly();
00382                 mDisplayingTime.set(data.dateTime, dateOnly);
00383                 alTime = mDisplayingTime;
00384                 break;
00385             }
00386             case KAAlarm::AUDIO__ALARM:
00387                 mAudioFile   = data.cleanText;
00388                 mSpeak       = data.speak  &&  mAudioFile.isEmpty();
00389                 mBeep        = !mSpeak  &&  mAudioFile.isEmpty();
00390                 mSoundVolume = (!mBeep && !mSpeak) ? data.soundVolume : -1;
00391                 mFadeVolume  = (mSoundVolume >= 0  &&  data.fadeSeconds > 0) ? data.fadeVolume : -1;
00392                 mFadeSeconds = (mFadeVolume >= 0) ? data.fadeSeconds : 0;
00393                 mRepeatSound = (!mBeep && !mSpeak)  &&  (data.repeatCount < 0);
00394                 break;
00395             case KAAlarm::PRE_ACTION__ALARM:
00396                 mPreAction = data.cleanText;
00397                 break;
00398             case KAAlarm::POST_ACTION__ALARM:
00399                 mPostAction = data.cleanText;
00400                 break;
00401             case KAAlarm::INVALID__ALARM:
00402             default:
00403                 break;
00404         }
00405 
00406         if (data.reminderOnceOnly)
00407             mReminderOnceOnly = true;
00408         switch (data.type)
00409         {
00410             case KAAlarm::DEFERRED_REMINDER_DATE__ALARM:
00411             case KAAlarm::DEFERRED_DATE__ALARM:
00412             case KAAlarm::DEFERRED_REMINDER_TIME__ALARM:
00413             case KAAlarm::DEFERRED_TIME__ALARM:
00414                 alTime = mDeferralTime;
00415                 if (mNextMainDateTime == mDeferralTime)
00416                     mDeferral = CANCEL_DEFERRAL;     // it's a cancelled deferral
00417                 // fall through to MAIN__ALARM
00418             case KAAlarm::MAIN__ALARM:
00419             case KAAlarm::AT_LOGIN__ALARM:
00420             case KAAlarm::REMINDER__ALARM:
00421             case KAAlarm::DISPLAYING__ALARM:
00422                 // Ensure that the basic fields are set up even if there is no main
00423                 // alarm in the event (if it has expired and then been deferred)
00424                 if (!set)
00425                 {
00426                     mNextMainDateTime = alTime;
00427                     mActionType = data.action;
00428                     mText = (mActionType == T_COMMAND) ? data.cleanText.stripWhiteSpace() : data.cleanText;
00429                     switch (data.action)
00430                     {
00431                         case T_MESSAGE:
00432                             mFont        = data.font;
00433                             mDefaultFont = data.defaultFont;
00434                             if (data.isEmailText)
00435                                 isEmailText = true;
00436                             // fall through to T_FILE
00437                         case T_FILE:
00438                             mBgColour    = data.bgColour;
00439                             mFgColour    = data.fgColour;
00440                             break;
00441                         case T_COMMAND:
00442                             mCommandScript = data.commandScript;
00443                             break;
00444                         case T_EMAIL:
00445                             mEmailFromKMail   = data.emailFromKMail;
00446                             mEmailAddresses   = data.emailAddresses;
00447                             mEmailSubject     = data.emailSubject;
00448                             mEmailAttachments = data.emailAttachments;
00449                             break;
00450                         default:
00451                             break;
00452                     }
00453                     set = true;
00454                 }
00455                 if (data.action == T_FILE  &&  mActionType == T_MESSAGE)
00456                     mActionType = T_FILE;
00457                 ++mAlarmCount;
00458                 break;
00459             case KAAlarm::AUDIO__ALARM:
00460             case KAAlarm::PRE_ACTION__ALARM:
00461             case KAAlarm::POST_ACTION__ALARM:
00462             case KAAlarm::INVALID__ALARM:
00463             default:
00464                 break;
00465         }
00466     }
00467     if (!isEmailText)
00468         mKMailSerialNumber = 0;
00469     if (mRepeatAtLogin)
00470         mArchiveRepeatAtLogin = false;
00471 
00472     Recurrence* recur = event.recurrence();
00473     if (recur  &&  recur->doesRecur())
00474         setRecurrence(*recur);
00475 
00476     mUpdated = false;
00477 }
00478 
00479 /******************************************************************************
00480  * Parse the alarms for a KCal::Event.
00481  * Reply = map of alarm data, indexed by KAAlarm::Type
00482  */
00483 void KAEvent::readAlarms(const Event& event, void* almap)
00484 {
00485     AlarmMap* alarmMap = (AlarmMap*)almap;
00486     Alarm::List alarms = event.alarms();
00487     for (Alarm::List::ConstIterator it = alarms.begin();  it != alarms.end();  ++it)
00488     {
00489         // Parse the next alarm's text
00490         AlarmData data;
00491         readAlarm(**it, data);
00492         if (data.type != KAAlarm::INVALID__ALARM)
00493             alarmMap->insert(data.type, data);
00494     }
00495 }
00496 
00497 /******************************************************************************
00498  * Parse a KCal::Alarm.
00499  * Reply = alarm ID (sequence number)
00500  */
00501 void KAEvent::readAlarm(const Alarm& alarm, AlarmData& data)
00502 {
00503     // Parse the next alarm's text
00504     data.alarm           = &alarm;
00505     data.dateTime        = alarm.time();
00506     data.startOffsetSecs = alarm.startOffset().asSeconds();    // can have start offset but no valid date/time (e.g. reminder in template)
00507     data.displayingFlags = 0;
00508     data.isEmailText     = false;
00509     data.repeatCount     = alarm.repeatCount();
00510     data.repeatInterval  = alarm.snoozeTime();
00511     switch (alarm.type())
00512     {
00513         case Alarm::Procedure:
00514             data.action        = T_COMMAND;
00515             data.cleanText     = alarm.programFile();
00516             data.commandScript = data.cleanText.isEmpty();   // blank command indicates a script
00517             if (!alarm.programArguments().isEmpty())
00518             {
00519                 if (!data.commandScript)
00520                     data.cleanText += " ";
00521                 data.cleanText += alarm.programArguments();
00522             }
00523             break;
00524         case Alarm::Email:
00525             data.action           = T_EMAIL;
00526             data.emailFromKMail   = alarm.customProperty(APPNAME, KMAIL_ID_PROPERTY);
00527             data.emailAddresses   = alarm.mailAddresses();
00528             data.emailSubject     = alarm.mailSubject();
00529             data.emailAttachments = alarm.mailAttachments();
00530             data.cleanText        = alarm.mailText();
00531             break;
00532         case Alarm::Display:
00533         {
00534             data.action    = T_MESSAGE;
00535             data.cleanText = AlarmText::fromCalendarText(alarm.text(), data.isEmailText);
00536             QString property = alarm.customProperty(APPNAME, FONT_COLOUR_PROPERTY);
00537             QStringList list = QStringList::split(QChar(';'), property, true);
00538             data.bgColour = QColor(255, 255, 255);   // white
00539             data.fgColour = QColor(0, 0, 0);         // black
00540             int n = list.count();
00541             if (n > 0)
00542             {
00543                 if (!list[0].isEmpty())
00544                 {
00545                     QColor c(list[0]);
00546                     if (c.isValid())
00547                         data.bgColour = c;
00548                 }
00549                 if (n > 1  &&  !list[1].isEmpty())
00550                 {
00551                     QColor c(list[1]);
00552                     if (c.isValid())
00553                         data.fgColour = c;
00554                 }
00555             }
00556             data.defaultFont = (n <= 2 || list[2].isEmpty());
00557             if (!data.defaultFont)
00558                 data.font.fromString(list[2]);
00559             break;
00560         }
00561         case Alarm::Audio:
00562         {
00563             data.action      = T_AUDIO;
00564             data.cleanText   = alarm.audioFile();
00565             data.type        = KAAlarm::AUDIO__ALARM;
00566             data.soundVolume = -1;
00567             data.fadeVolume  = -1;
00568             data.fadeSeconds = 0;
00569             data.speak       = !alarm.customProperty(APPNAME, SPEAK_PROPERTY).isNull();
00570             QString property = alarm.customProperty(APPNAME, VOLUME_PROPERTY);
00571             if (!property.isEmpty())
00572             {
00573                 bool ok;
00574                 float fadeVolume;
00575                 int   fadeSecs = 0;
00576                 QStringList list = QStringList::split(QChar(';'), property, true);
00577                 data.soundVolume = list[0].toFloat(&ok);
00578                 if (!ok)
00579                     data.soundVolume = -1;
00580                 if (data.soundVolume >= 0  &&  list.count() >= 3)
00581                 {
00582                     fadeVolume = list[1].toFloat(&ok);
00583                     if (ok)
00584                         fadeSecs = static_cast<int>(list[2].toUInt(&ok));
00585                     if (ok  &&  fadeVolume >= 0  &&  fadeSecs > 0)
00586                     {
00587                         data.fadeVolume  = fadeVolume;
00588                         data.fadeSeconds = fadeSecs;
00589                     }
00590                 }
00591             }
00592             return;
00593         }
00594         case Alarm::Invalid:
00595             data.type = KAAlarm::INVALID__ALARM;
00596             return;
00597     }
00598 
00599     bool atLogin          = false;
00600     bool reminder         = false;
00601     bool deferral         = false;
00602     bool dateDeferral     = false;
00603     data.reminderOnceOnly = false;
00604     data.type = KAAlarm::MAIN__ALARM;
00605     QString property = alarm.customProperty(APPNAME, TYPE_PROPERTY);
00606     QStringList types = QStringList::split(QChar(','), property);
00607     for (unsigned int i = 0;  i < types.count();  ++i)
00608     {
00609         QString type = types[i];
00610         if (type == AT_LOGIN_TYPE)
00611             atLogin = true;
00612         else if (type == FILE_TYPE  &&  data.action == T_MESSAGE)
00613             data.action = T_FILE;
00614         else if (type == REMINDER_TYPE)
00615             reminder = true;
00616         else if (type == REMINDER_ONCE_TYPE)
00617             reminder = data.reminderOnceOnly = true;
00618         else if (type == TIME_DEFERRAL_TYPE)
00619             deferral = true;
00620         else if (type == DATE_DEFERRAL_TYPE)
00621             dateDeferral = deferral = true;
00622         else if (type == DISPLAYING_TYPE)
00623             data.type = KAAlarm::DISPLAYING__ALARM;
00624         else if (type == PRE_ACTION_TYPE  &&  data.action == T_COMMAND)
00625             data.type = KAAlarm::PRE_ACTION__ALARM;
00626         else if (type == POST_ACTION_TYPE  &&  data.action == T_COMMAND)
00627             data.type = KAAlarm::POST_ACTION__ALARM;
00628     }
00629 
00630     if (reminder)
00631     {
00632         if (data.type == KAAlarm::MAIN__ALARM)
00633             data.type = dateDeferral ? KAAlarm::DEFERRED_REMINDER_DATE__ALARM
00634                       : deferral ? KAAlarm::DEFERRED_REMINDER_TIME__ALARM : KAAlarm::REMINDER__ALARM;
00635         else if (data.type == KAAlarm::DISPLAYING__ALARM)
00636             data.displayingFlags = dateDeferral ? REMINDER | DATE_DEFERRAL
00637                                  : deferral ? REMINDER | TIME_DEFERRAL : REMINDER;
00638     }
00639     else if (deferral)
00640     {
00641         if (data.type == KAAlarm::MAIN__ALARM)
00642             data.type = dateDeferral ? KAAlarm::DEFERRED_DATE__ALARM : KAAlarm::DEFERRED_TIME__ALARM;
00643         else if (data.type == KAAlarm::DISPLAYING__ALARM)
00644             data.displayingFlags = dateDeferral ? DATE_DEFERRAL : TIME_DEFERRAL;
00645     }
00646     if (atLogin)
00647     {
00648         if (data.type == KAAlarm::MAIN__ALARM)
00649             data.type = KAAlarm::AT_LOGIN__ALARM;
00650         else if (data.type == KAAlarm::DISPLAYING__ALARM)
00651             data.displayingFlags = REPEAT_AT_LOGIN;
00652     }
00653 //kdDebug(5950)<<"ReadAlarm(): text="<<alarm.text()<<", time="<<alarm.time().toString()<<", valid time="<<alarm.time().isValid()<<endl;
00654 }
00655 
00656 /******************************************************************************
00657  * Initialise the KAEvent with the specified parameters.
00658  */
00659 void KAEvent::set(const QDateTime& dateTime, const QString& text, const QColor& bg, const QColor& fg,
00660                   const QFont& font, Action action, int lateCancel, int flags)
00661 {
00662     clearRecur();
00663     mStartDateTime.set(dateTime, flags & ANY_TIME);
00664     mNextMainDateTime = mStartDateTime;
00665     switch (action)
00666     {
00667         case MESSAGE:
00668         case FILE:
00669         case COMMAND:
00670         case EMAIL:
00671             mActionType = (KAAlarmEventBase::Type)action;
00672             break;
00673         default:
00674             mActionType = T_MESSAGE;
00675             break;
00676     }
00677     mText                   = (mActionType == T_COMMAND) ? text.stripWhiteSpace() : text;
00678     mEventID                = QString::null;
00679     mTemplateName           = QString::null;
00680     mPreAction              = QString::null;
00681     mPostAction             = QString::null;
00682     mAudioFile              = "";
00683     mSoundVolume            = -1;
00684     mFadeVolume             = -1;
00685     mTemplateAfterTime      = -1;
00686     mFadeSeconds            = 0;
00687     mBgColour               = bg;
00688     mFgColour               = fg;
00689     mFont                   = font;
00690     mAlarmCount             = 1;
00691     mLateCancel             = lateCancel;     // do this before set(flags)
00692     mDeferral               = NO_DEFERRAL;    // do this before set(flags)
00693     set(flags);
00694     mKMailSerialNumber      = 0;
00695     mReminderMinutes        = 0;
00696     mArchiveReminderMinutes = 0;
00697     mRepeatInterval         = 0;
00698     mRepeatCount            = 0;
00699     mArchiveRepeatAtLogin   = false;
00700     mReminderOnceOnly       = false;
00701     mDisplaying             = false;
00702     mMainExpired            = false;
00703     mArchive                = false;
00704     mUpdated                = false;
00705 }
00706 
00707 /******************************************************************************
00708  * Initialise a command KAEvent.
00709  */
00710 void KAEvent::setCommand(const QDate& d, const QString& command, int lateCancel, int flags, const QString& logfile)
00711 {
00712     if (!logfile.isEmpty())
00713         flags &= ~EXEC_IN_XTERM;
00714     set(d, command, QColor(), QColor(), QFont(), COMMAND, lateCancel, flags | ANY_TIME);
00715     mLogFile = logfile;
00716 }
00717 
00718 void KAEvent::setCommand(const QDateTime& dt, const QString& command, int lateCancel, int flags, const QString& logfile)
00719 {
00720     if (!logfile.isEmpty())
00721         flags &= ~EXEC_IN_XTERM;
00722     set(dt, command, QColor(), QColor(), QFont(), COMMAND, lateCancel, flags);
00723     mLogFile = logfile;
00724 }
00725 
00726 void KAEvent::setLogFile(const QString& logfile)
00727 {
00728     mLogFile = logfile;
00729     if (!logfile.isEmpty())
00730         mCommandXterm = false;
00731 }
00732 
00733 /******************************************************************************
00734  * Initialise an email KAEvent.
00735  */
00736 void KAEvent::setEmail(const QDate& d, const QString& from, const EmailAddressList& addresses, const QString& subject,
00737                 const QString& message, const QStringList& attachments, int lateCancel, int flags)
00738 {
00739     set(d, message, QColor(), QColor(), QFont(), EMAIL, lateCancel, flags | ANY_TIME);
00740     mEmailFromKMail   = from;
00741     mEmailAddresses   = addresses;
00742     mEmailSubject     = subject;
00743     mEmailAttachments = attachments;
00744 }
00745 
00746 void KAEvent::setEmail(const QDateTime& dt, const QString& from, const EmailAddressList& addresses, const QString& subject,
00747                 const QString& message, const QStringList& attachments, int lateCancel, int flags)
00748 {
00749     set(dt, message, QColor(), QColor(), QFont(), EMAIL, lateCancel, flags);
00750     mEmailFromKMail   = from;
00751     mEmailAddresses   = addresses;
00752     mEmailSubject     = subject;
00753     mEmailAttachments = attachments;
00754 }
00755 
00756 void KAEvent::setEmail(const QString& from, const EmailAddressList& addresses, const QString& subject, const QStringList& attachments)
00757 {
00758     mEmailFromKMail   = from;
00759     mEmailAddresses   = addresses;
00760     mEmailSubject     = subject;
00761     mEmailAttachments = attachments;
00762 }
00763 
00764 void KAEvent::setAudioFile(const QString& filename, float volume, float fadeVolume, int fadeSeconds)
00765 {
00766     mAudioFile = filename;
00767     mSoundVolume = filename.isEmpty() ? -1 : volume;
00768     if (mSoundVolume >= 0)
00769     {
00770         mFadeVolume  = (fadeSeconds > 0) ? fadeVolume : -1;
00771         mFadeSeconds = (mFadeVolume >= 0) ? fadeSeconds : 0;
00772     }
00773     else
00774     {
00775         mFadeVolume  = -1;
00776         mFadeSeconds = 0;
00777     }
00778     mUpdated = true;
00779 }
00780 
00781 void KAEvent::setReminder(int minutes, bool onceOnly)
00782 {
00783     set_reminder(minutes);
00784     mReminderOnceOnly = onceOnly;
00785     mUpdated          = true;
00786 }
00787 
00788 /******************************************************************************
00789  * Reinitialise the start date/time byt adjusting its date part, and setting
00790  * the next scheduled alarm to the new start date/time.
00791  */
00792 void KAEvent::adjustStartDate(const QDate& d)
00793 {
00794     if (mStartDateTime.isDateOnly())
00795     {
00796         mStartDateTime = d;
00797         if (mRecurrence)
00798             mRecurrence->setStartDate(d);
00799     }
00800     else
00801     {
00802         mStartDateTime.set(d, mStartDateTime.time());
00803         if (mRecurrence)
00804             mRecurrence->setStartDateTime(mStartDateTime.dateTime());
00805     }
00806     mNextMainDateTime = mStartDateTime;
00807 }
00808 
00809 /******************************************************************************
00810  * Return the time of the next scheduled occurrence of the event.
00811  * Reminders and deferred reminders can optionally be ignored.
00812  */
00813 DateTime KAEvent::nextDateTime(bool includeReminders) const
00814 {
00815     if (includeReminders  &&  mReminderMinutes)
00816     {
00817         if (!mReminderOnceOnly  ||  mNextMainDateTime == mStartDateTime)
00818             return mNextMainDateTime.addSecs(-mReminderMinutes * 60);
00819     }
00820     DateTime dt = mNextMainDateTime;
00821     if (mRepeatCount)
00822     {
00823         // N.B. This is coded to avoid 32-bit integer overflow which occurs
00824         //      in QDateTime::secsTo() for large enough time differences.
00825         QDateTime now = QDateTime::currentDateTime();
00826         if (now > mNextMainDateTime)
00827         {
00828             dt = mainEndRepeatTime();    // get the last repetition time
00829             if (dt > now)
00830             {
00831                 int repeatSecs = mRepeatInterval * 60;
00832                 int repetition = (mNextMainDateTime.secsTo(now) + repeatSecs - 1) / repeatSecs;
00833                 dt = mNextMainDateTime.addSecs(repetition * repeatSecs);
00834             }
00835         }
00836     }
00837     if (mDeferral > 0
00838     &&  (includeReminders  ||  mDeferral != REMINDER_DEFERRAL))
00839     {
00840         if (mMainExpired)
00841             return mDeferralTime;
00842         return QMIN(mDeferralTime, dt);
00843     }
00844     return dt;
00845 }
00846 
00847 /******************************************************************************
00848  * Convert a unique ID to indicate that the event is in a specified calendar file.
00849  */
00850 QString KAEvent::uid(const QString& id, Status status)
00851 {
00852     QString result = id;
00853     Status oldStatus;
00854     int i, len;
00855     if ((i = result.find(EXPIRED_UID)) > 0)
00856     {
00857         oldStatus = EXPIRED;
00858         len = EXPIRED_UID.length();
00859     }
00860     else if ((i = result.find(DISPLAYING_UID)) > 0)
00861     {
00862         oldStatus = DISPLAYING;
00863         len = DISPLAYING_UID.length();
00864     }
00865     else if ((i = result.find(TEMPLATE_UID)) > 0)
00866     {
00867         oldStatus = TEMPLATE;
00868         len = TEMPLATE_UID.length();
00869     }
00870     else if ((i = result.find(KORGANIZER_UID)) > 0)
00871     {
00872         oldStatus = KORGANIZER;
00873         len = KORGANIZER_UID.length();
00874     }
00875     else
00876     {
00877         oldStatus = ACTIVE;
00878         i = result.findRev('-');
00879         len = 1;
00880     }
00881     if (status != oldStatus  &&  i > 0)
00882     {
00883         QString part;
00884         switch (status)
00885         {
00886             case ACTIVE:      part = "-";  break;
00887             case EXPIRED:     part = EXPIRED_UID;  break;
00888             case DISPLAYING:  part = DISPLAYING_UID;  break;
00889             case TEMPLATE:    part = TEMPLATE_UID;  break;
00890             case KORGANIZER:  part = KORGANIZER_UID;  break;
00891         }
00892         result.replace(i, len, part);
00893     }
00894     return result;
00895 }
00896 
00897 /******************************************************************************
00898  * Get the calendar type for a unique ID.
00899  */
00900 KAEvent::Status KAEvent::uidStatus(const QString& uid)
00901 {
00902     if (uid.find(EXPIRED_UID) > 0)
00903         return EXPIRED;
00904     if (uid.find(DISPLAYING_UID) > 0)
00905         return DISPLAYING;
00906     if (uid.find(TEMPLATE_UID) > 0)
00907         return TEMPLATE;
00908     if (uid.find(KORGANIZER_UID) > 0)
00909         return KORGANIZER;
00910     return ACTIVE;
00911 }
00912 
00913 void KAEvent::set(int flags)
00914 {
00915     KAAlarmEventBase::set(flags & ~READ_ONLY_FLAGS);
00916     mStartDateTime.setDateOnly(flags & ANY_TIME);
00917     set_deferral((flags & DEFERRAL) ? NORMAL_DEFERRAL : NO_DEFERRAL);
00918     mCommandXterm     = flags & EXEC_IN_XTERM;
00919     mCopyToKOrganizer = flags & COPY_KORGANIZER;
00920     mEnabled          = !(flags & DISABLED);
00921     mUpdated          = true;
00922 }
00923 
00924 int KAEvent::flags() const
00925 {
00926     return KAAlarmEventBase::flags()
00927          | (mStartDateTime.isDateOnly() ? ANY_TIME : 0)
00928          | (mDeferral > 0               ? DEFERRAL : 0)
00929          | (mCommandXterm               ? EXEC_IN_XTERM : 0)
00930          | (mCopyToKOrganizer           ? COPY_KORGANIZER : 0)
00931          | (mEnabled                    ? 0 : DISABLED);
00932 }
00933 
00934 /******************************************************************************
00935  * Create a new Event from the KAEvent data.
00936  */
00937 Event* KAEvent::event() const
00938 {
00939     KCal::Event* ev = new KCal::Event;
00940     ev->setUid(mEventID);
00941     updateKCalEvent(*ev, false);
00942     return ev;
00943 }
00944 
00945 /******************************************************************************
00946  * Update an existing KCal::Event with the KAEvent data.
00947  * If 'original' is true, the event start date/time is adjusted to its original
00948  * value instead of its next occurrence, and the expired main alarm is
00949  * reinstated.
00950  */
00951 bool KAEvent::updateKCalEvent(Event& ev, bool checkUid, bool original, bool cancelCancelledDefer) const
00952 {
00953     if (checkUid  &&  !mEventID.isEmpty()  &&  mEventID != ev.uid()
00954     ||  !mAlarmCount  &&  (!original || !mMainExpired))
00955         return false;
00956 
00957     checkRecur();     // ensure recurrence/repetition data is consistent
00958     bool readOnly = ev.isReadOnly();
00959     ev.setReadOnly(false);
00960     ev.setTransparency(Event::Transparent);
00961 
00962     // Set up event-specific data
00963     QStringList cats;
00964     if (mStartDateTime.isDateOnly())
00965         cats.append(DATE_ONLY_CATEGORY);
00966     if (mConfirmAck)
00967         cats.append(CONFIRM_ACK_CATEGORY);
00968     if (mEmailBcc)
00969         cats.append(EMAIL_BCC_CATEGORY);
00970     if (mKMailSerialNumber)
00971         cats.append(QString("%1%2").arg(KMAIL_SERNUM_CATEGORY).arg(mKMailSerialNumber));
00972     if (mCopyToKOrganizer)
00973         cats.append(KORGANIZER_CATEGORY);
00974     if (mCommandXterm)
00975         cats.append(LOG_CATEGORY + xtermURL);
00976     else if (!mLogFile.isEmpty())
00977         cats.append(LOG_CATEGORY + mLogFile);
00978     if (mLateCancel)
00979         cats.append(QString("%1%2").arg(mAutoClose ? AUTO_CLOSE_CATEGORY : LATE_CANCEL_CATEGORY).arg(mLateCancel));
00980     if (!mTemplateName.isEmpty()  &&  mTemplateAfterTime >= 0)
00981         cats.append(QString("%1%2").arg(TEMPL_AFTER_TIME_CATEGORY).arg(mTemplateAfterTime));
00982     if (mArchive  &&  !original)
00983     {
00984         QStringList params;
00985         if (mArchiveReminderMinutes)
00986         {
00987             if (mReminderOnceOnly)
00988                 params += ARCHIVE_REMINDER_ONCE_TYPE;
00989             char unit = 'M';
00990             int count = mArchiveReminderMinutes;
00991             if (count % 1440 == 0)
00992             {
00993                 unit = 'D';
00994                 count /= 1440;
00995             }
00996             else if (count % 60 == 0)
00997             {
00998                 unit = 'H';
00999                 count /= 60;
01000             }
01001             params += QString("%1%2").arg(count).arg(unit);
01002         }
01003         if (mArchiveRepeatAtLogin)
01004             params += AT_LOGIN_TYPE;
01005         if (params.count() > 0)
01006         {
01007             QString cat = ARCHIVE_CATEGORIES;
01008             cat += params.join(QString::fromLatin1(";"));
01009             cats.append(cat);
01010         }
01011         else
01012             cats.append(ARCHIVE_CATEGORY);
01013     }
01014     ev.setCategories(cats);
01015     ev.setCustomStatus(mEnabled ? QString::null : DISABLED_STATUS);
01016     ev.setRevision(mRevision);
01017     ev.clearAlarms();
01018 
01019     // Always set DTSTART as date/time, since alarm times can only be specified
01020     // in local time (instead of UTC) if they are relative to a DTSTART or DTEND
01021     // which is also specified in local time. Instead of calling setFloats() to
01022     // indicate a date-only event, the category "DATE" is included.
01023     ev.setDtStart(mStartDateTime.dateTime());
01024     ev.setFloats(false);
01025     ev.setHasEndDate(false);
01026 
01027     DateTime dtMain = original ? mStartDateTime : mNextMainDateTime;
01028     int      ancillaryType = 0;   // 0 = invalid, 1 = time, 2 = offset
01029     DateTime ancillaryTime;       // time for ancillary alarms (audio, pre-action, etc)
01030     int      ancillaryOffset = 0; // start offset for ancillary alarms
01031     if (!mMainExpired  ||  original)
01032     {
01033         // Add the main alarm
01034         initKcalAlarm(ev, dtMain, QStringList(), KAAlarm::MAIN_ALARM);
01035         ancillaryTime = dtMain;
01036         ancillaryType = dtMain.isValid() ? 1 : 0;
01037     }
01038 
01039     // Add subsidiary alarms
01040     if (mRepeatAtLogin  ||  mArchiveRepeatAtLogin && original)
01041     {
01042         DateTime dtl;
01043         if (mArchiveRepeatAtLogin)
01044             dtl = mStartDateTime.dateTime().addDays(-1);
01045         else if (mAtLoginDateTime.isValid())
01046             dtl = mAtLoginDateTime;
01047         else if (mStartDateTime.isDateOnly())
01048             dtl = QDate::currentDate().addDays(-1);
01049         else
01050             dtl = QDateTime::currentDateTime();
01051         initKcalAlarm(ev, dtl, AT_LOGIN_TYPE);
01052         if (!ancillaryType  &&  dtl.isValid())
01053         {
01054             ancillaryTime = dtl;
01055             ancillaryType = 1;
01056         }
01057     }
01058     if (mReminderMinutes  ||  mArchiveReminderMinutes && original)
01059     {
01060         int minutes = mReminderMinutes ? mReminderMinutes : mArchiveReminderMinutes;
01061         initKcalAlarm(ev, -minutes * 60, QStringList(mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE));
01062         if (!ancillaryType)
01063         {
01064             ancillaryOffset = -minutes * 60;
01065             ancillaryType = 2;
01066         }
01067     }
01068     if (mDeferral > 0  ||  mDeferral == CANCEL_DEFERRAL && !cancelCancelledDefer)
01069     {
01070         QStringList list;
01071         if (mDeferralTime.isDateOnly())
01072             list += DATE_DEFERRAL_TYPE;
01073         else
01074             list += TIME_DEFERRAL_TYPE;
01075         if (mDeferral == REMINDER_DEFERRAL)
01076             list += mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE;
01077         initKcalAlarm(ev, mDeferralTime, list);
01078         if (!ancillaryType  &&  mDeferralTime.isValid())
01079         {
01080             ancillaryTime = mDeferralTime;
01081             ancillaryType = 1;
01082         }
01083     }
01084     if (!mTemplateName.isEmpty())
01085         ev.setSummary(mTemplateName);
01086     else if (mDisplaying)
01087     {
01088         QStringList list(DISPLAYING_TYPE);
01089         if (mDisplayingFlags & REPEAT_AT_LOGIN)
01090             list += AT_LOGIN_TYPE;
01091         else if (mDisplayingFlags & DEFERRAL)
01092         {
01093             if (mDisplayingFlags & TIMED_FLAG)
01094                 list += TIME_DEFERRAL_TYPE;
01095             else
01096                 list += DATE_DEFERRAL_TYPE;
01097         }
01098         if (mDisplayingFlags & REMINDER)
01099             list += mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE;
01100         initKcalAlarm(ev, mDisplayingTime, list);
01101         if (!ancillaryType  &&  mDisplayingTime.isValid())
01102         {
01103             ancillaryTime = mDisplayingTime;
01104             ancillaryType = 1;
01105         }
01106     }
01107     if (mBeep  ||  mSpeak  ||  !mAudioFile.isEmpty())
01108     {
01109         // A sound is specified
01110         if (ancillaryType == 2)
01111             initKcalAlarm(ev, ancillaryOffset, QStringList(), KAAlarm::AUDIO_ALARM);
01112         else
01113             initKcalAlarm(ev, ancillaryTime, QStringList(), KAAlarm::AUDIO_ALARM);
01114     }
01115     if (!mPreAction.isEmpty())
01116     {
01117         // A pre-display action is specified
01118         if (ancillaryType == 2)
01119             initKcalAlarm(ev, ancillaryOffset, QStringList(PRE_ACTION_TYPE), KAAlarm::PRE_ACTION_ALARM);
01120         else
01121             initKcalAlarm(ev, ancillaryTime, QStringList(PRE_ACTION_TYPE), KAAlarm::PRE_ACTION_ALARM);
01122     }
01123     if (!mPostAction.isEmpty())
01124     {
01125         // A post-display action is specified
01126         if (ancillaryType == 2)
01127             initKcalAlarm(ev, ancillaryOffset, QStringList(POST_ACTION_TYPE), KAAlarm::POST_ACTION_ALARM);
01128         else
01129             initKcalAlarm(ev, ancillaryTime, QStringList(POST_ACTION_TYPE), KAAlarm::POST_ACTION_ALARM);
01130     }
01131 
01132     if (mRecurrence)
01133         mRecurrence->writeRecurrence(*ev.recurrence());
01134     else
01135         ev.clearRecurrence();
01136     if (mSaveDateTime.isValid())
01137         ev.setCreated(mSaveDateTime);
01138     ev.setReadOnly(readOnly);
01139     return true;
01140 }
01141 
01142 /******************************************************************************
01143  * Create a new alarm for a libkcal event, and initialise it according to the
01144  * alarm action. If 'types' is non-null, it is appended to the X-KDE-KALARM-TYPE
01145  * property value list.
01146  */
01147 Alarm* KAEvent::initKcalAlarm(Event& event, const DateTime& dt, const QStringList& types, KAAlarm::Type type) const
01148 {
01149     int startOffset = dt.isDateOnly() ? mStartDateTime.secsTo(dt)
01150                                       : mStartDateTime.dateTime().secsTo(dt.dateTime());
01151     return initKcalAlarm(event, startOffset, types, type);
01152 }
01153 
01154 Alarm* KAEvent::initKcalAlarm(Event& event, int startOffsetSecs, const QStringList& types, KAAlarm::Type type) const
01155 {
01156     QStringList alltypes;
01157     Alarm* alarm = event.newAlarm();
01158     alarm->setEnabled(true);
01159     // RFC2445 specifies that absolute alarm times must be stored as UTC.
01160     // So, in order to store local times, set the alarm time as an offset to DTSTART.
01161     alarm->setStartOffset(startOffsetSecs);
01162 
01163     switch (type)
01164     {
01165         case KAAlarm::AUDIO_ALARM:
01166             alarm->setAudioAlarm(mAudioFile);  // empty for a beep or for speaking
01167             if (mSpeak)
01168                 alarm->setCustomProperty(APPNAME, SPEAK_PROPERTY, QString::fromLatin1("Y"));
01169             if (mRepeatSound)
01170             {
01171                 alarm->setRepeatCount(-1);
01172                 alarm->setSnoozeTime(0);
01173             }
01174             if (!mAudioFile.isEmpty()  &&  mSoundVolume >= 0)
01175                 alarm->setCustomProperty(APPNAME, VOLUME_PROPERTY,
01176                               QString::fromLatin1("%1;%2;%3").arg(QString::number(mSoundVolume, 'f', 2))
01177                                                              .arg(QString::number(mFadeVolume, 'f', 2))
01178                                                              .arg(mFadeSeconds));
01179             break;
01180         case KAAlarm::PRE_ACTION_ALARM:
01181             setProcedureAlarm(alarm, mPreAction);
01182             break;
01183         case KAAlarm::POST_ACTION_ALARM:
01184             setProcedureAlarm(alarm, mPostAction);
01185             break;
01186         case KAAlarm::MAIN_ALARM:
01187             alarm->setSnoozeTime(mRepeatInterval);
01188             alarm->setRepeatCount(mRepeatCount);
01189             // fall through to INVALID_ALARM
01190         case KAAlarm::INVALID_ALARM:
01191             switch (mActionType)
01192             {
01193                 case T_FILE:
01194                     alltypes += FILE_TYPE;
01195                     // fall through to T_MESSAGE
01196                 case T_MESSAGE:
01197                     alarm->setDisplayAlarm(AlarmText::toCalendarText(mText));
01198                     alarm->setCustomProperty(APPNAME, FONT_COLOUR_PROPERTY,
01199                               QString::fromLatin1("%1;%2;%3").arg(mBgColour.name())
01200                                              .arg(mFgColour.name())
01201                                              .arg(mDefaultFont ? QString::null : mFont.toString()));
01202                     break;
01203                 case T_COMMAND:
01204                     if (mCommandScript)
01205                         alarm->setProcedureAlarm("", mText);
01206                     else
01207                         setProcedureAlarm(alarm, mText);
01208                     break;
01209                 case T_EMAIL:
01210                     alarm->setEmailAlarm(mEmailSubject, mText, mEmailAddresses, mEmailAttachments);
01211                     if (!mEmailFromKMail.isEmpty())
01212                         alarm->setCustomProperty(APPNAME, KMAIL_ID_PROPERTY, mEmailFromKMail);
01213                     break;
01214                 case T_AUDIO:
01215                     break;
01216             }
01217             break;
01218         case KAAlarm::REMINDER_ALARM:
01219         case KAAlarm::DEFERRED_ALARM:
01220         case KAAlarm::DEFERRED_REMINDER_ALARM:
01221         case KAAlarm::AT_LOGIN_ALARM:
01222         case KAAlarm::DISPLAYING_ALARM:
01223             break;
01224     }
01225     alltypes += types;
01226     if (alltypes.count() > 0)
01227         alarm->setCustomProperty(APPNAME, TYPE_PROPERTY, alltypes.join(","));
01228     return alarm;
01229 }
01230 
01231 /******************************************************************************
01232  * Return the alarm of the specified type.
01233  */
01234 KAAlarm KAEvent::alarm(KAAlarm::Type type) const
01235 {
01236     checkRecur();     // ensure recurrence/repetition data is consistent
01237     KAAlarm al;       // this sets type to INVALID_ALARM
01238     if (mAlarmCount)
01239     {
01240         al.mEventID       = mEventID;
01241         al.mActionType    = mActionType;
01242         al.mText          = mText;
01243         al.mBgColour      = mBgColour;
01244         al.mFgColour      = mFgColour;
01245         al.mFont          = mFont;
01246         al.mDefaultFont   = mDefaultFont;
01247         al.mBeep          = mBeep;
01248         al.mSpeak         = mSpeak;
01249         al.mSoundVolume   = mSoundVolume;
01250         al.mFadeVolume    = mFadeVolume;
01251         al.mFadeSeconds   = mFadeSeconds;
01252         al.mRepeatSound   = mRepeatSound;
01253         al.mConfirmAck    = mConfirmAck;
01254         al.mRepeatAtLogin = false;
01255         al.mDeferred      = false;
01256         al.mLateCancel    = mLateCancel;
01257         al.mAutoClose     = mAutoClose;
01258         al.mEmailBcc      = mEmailBcc;
01259         al.mCommandScript = mCommandScript;
01260         if (mActionType == T_EMAIL)
01261         {
01262             al.mEmailFromKMail   = mEmailFromKMail;
01263             al.mEmailAddresses   = mEmailAddresses;
01264             al.mEmailSubject     = mEmailSubject;
01265             al.mEmailAttachments = mEmailAttachments;
01266         }
01267         switch (type)
01268         {
01269             case KAAlarm::MAIN_ALARM:
01270                 if (!mMainExpired)
01271                 {
01272                     al.mType             = KAAlarm::MAIN__ALARM;
01273                     al.mNextMainDateTime = mNextMainDateTime;
01274                     al.mRepeatCount      = mRepeatCount;
01275                     al.mRepeatInterval   = mRepeatInterval;
01276                 }
01277                 break;
01278             case KAAlarm::REMINDER_ALARM:
01279                 if (mReminderMinutes)
01280                 {
01281                     al.mType = KAAlarm::REMINDER__ALARM;
01282                     if (mReminderOnceOnly)
01283                         al.mNextMainDateTime = mStartDateTime.addMins(-mReminderMinutes);
01284                     else
01285                         al.mNextMainDateTime = mNextMainDateTime.addMins(-mReminderMinutes);
01286                 }
01287                 break;
01288             case KAAlarm::DEFERRED_REMINDER_ALARM:
01289                 if (mDeferral != REMINDER_DEFERRAL)
01290                     break;
01291                 // fall through to DEFERRED_ALARM
01292             case KAAlarm::DEFERRED_ALARM:
01293                 if (mDeferral > 0)
01294                 {
01295                     al.mType = static_cast<KAAlarm::SubType>((mDeferral == REMINDER_DEFERRAL ? KAAlarm::DEFERRED_REMINDER_ALARM : KAAlarm::DEFERRED_ALARM)
01296                                                              | (mDeferralTime.isDateOnly() ? 0 : KAAlarm::TIMED_DEFERRAL_FLAG));
01297                     al.mNextMainDateTime = mDeferralTime;
01298                     al.mDeferred         = true;
01299                 }
01300                 break;
01301             case KAAlarm::AT_LOGIN_ALARM:
01302                 if (mRepeatAtLogin)
01303                 {
01304                     al.mType             = KAAlarm::AT_LOGIN__ALARM;
01305                     al.mNextMainDateTime = mAtLoginDateTime;
01306                     al.mRepeatAtLogin    = true;
01307                     al.mLateCancel       = 0;
01308                     al.mAutoClose        = false;
01309                 }
01310                 break;
01311             case KAAlarm::DISPLAYING_ALARM:
01312                 if (mDisplaying)
01313                 {
01314                     al.mType             = KAAlarm::DISPLAYING__ALARM;
01315                     al.mNextMainDateTime = mDisplayingTime;
01316                     al.mDisplaying       = true;
01317                 }
01318                 break;
01319             case KAAlarm::AUDIO_ALARM:
01320             case KAAlarm::PRE_ACTION_ALARM:
01321             case KAAlarm::POST_ACTION_ALARM:
01322             case KAAlarm::INVALID_ALARM:
01323             default:
01324                 break;
01325         }
01326     }
01327     return al;
01328 }
01329 
01330 /******************************************************************************
01331  * Return the main alarm for the event.
01332  * If the main alarm does not exist, one of the subsidiary ones is returned if
01333  * possible.
01334  * N.B. a repeat-at-login alarm can only be returned if it has been read from/
01335  * written to the calendar file.
01336  */
01337 KAAlarm KAEvent::firstAlarm() const
01338 {
01339     if (mAlarmCount)
01340     {
01341         if (!mMainExpired)
01342             return alarm(KAAlarm::MAIN_ALARM);
01343         return nextAlarm(KAAlarm::MAIN_ALARM);
01344     }
01345     return KAAlarm();
01346 }
01347 
01348 /******************************************************************************
01349  * Return the next alarm for the event, after the specified alarm.
01350  * N.B. a repeat-at-login alarm can only be returned if it has been read from/
01351  * written to the calendar file.
01352  */
01353 KAAlarm KAEvent::nextAlarm(KAAlarm::Type prevType) const
01354 {
01355     switch (prevType)
01356     {
01357         case KAAlarm::MAIN_ALARM:
01358             if (mReminderMinutes)
01359                 return alarm(KAAlarm::REMINDER_ALARM);
01360             // fall through to REMINDER_ALARM
01361         case KAAlarm::REMINDER_ALARM:
01362             // There can only be one deferral alarm
01363             if (mDeferral == REMINDER_DEFERRAL)
01364                 return alarm(KAAlarm::DEFERRED_REMINDER_ALARM);
01365             if (mDeferral == NORMAL_DEFERRAL)
01366                 return alarm(KAAlarm::DEFERRED_ALARM);
01367             // fall through to DEFERRED_ALARM
01368         case KAAlarm::DEFERRED_REMINDER_ALARM:
01369         case KAAlarm::DEFERRED_ALARM:
01370             if (mRepeatAtLogin)
01371                 return alarm(KAAlarm::AT_LOGIN_ALARM);
01372             // fall through to AT_LOGIN_ALARM
01373         case KAAlarm::AT_LOGIN_ALARM:
01374             if (mDisplaying)
01375                 return alarm(KAAlarm::DISPLAYING_ALARM);
01376             // fall through to DISPLAYING_ALARM
01377         case KAAlarm::DISPLAYING_ALARM:
01378             // fall through to default
01379         case KAAlarm::AUDIO_ALARM:
01380         case KAAlarm::PRE_ACTION_ALARM:
01381         case KAAlarm::POST_ACTION_ALARM:
01382         case KAAlarm::INVALID_ALARM:
01383         default:
01384             break;
01385     }
01386     return KAAlarm();
01387 }
01388 
01389 /******************************************************************************
01390  * Remove the alarm of the specified type from the event.
01391  * This must only be called to remove an alarm which has expired, not to
01392  * reconfigure the event.
01393  */
01394 void KAEvent::removeExpiredAlarm(KAAlarm::Type type)
01395 {
01396     int count = mAlarmCount;
01397     switch (type)
01398     {
01399         case KAAlarm::MAIN_ALARM:
01400             mAlarmCount = 0;    // removing main alarm - also remove subsidiary alarms
01401             break;
01402         case KAAlarm::AT_LOGIN_ALARM:
01403             if (mRepeatAtLogin)
01404             {
01405                 // Remove the at-login alarm, but keep a note of it for archiving purposes
01406                 mArchiveRepeatAtLogin = true;
01407                 mRepeatAtLogin = false;
01408                 --mAlarmCount;
01409             }
01410             break;
01411         case KAAlarm::REMINDER_ALARM:
01412             // Remove any reminder alarm, but keep a note of it for archiving purposes
01413             set_archiveReminder();
01414             break;
01415         case KAAlarm::DEFERRED_REMINDER_ALARM:
01416         case KAAlarm::DEFERRED_ALARM:
01417             set_deferral(NO_DEFERRAL);
01418             break;
01419         case KAAlarm::DISPLAYING_ALARM:
01420             if (mDisplaying)
01421             {
01422                 mDisplaying = false;
01423                 --mAlarmCount;
01424             }
01425             break;
01426         case KAAlarm::AUDIO_ALARM:
01427         case KAAlarm::PRE_ACTION_ALARM:
01428         case KAAlarm::POST_ACTION_ALARM:
01429         case KAAlarm::INVALID_ALARM:
01430         default:
01431             break;
01432     }
01433     if (mAlarmCount != count)
01434         mUpdated = true;
01435 }
01436 
01437 /******************************************************************************
01438  * Defer the event to the specified time.
01439  * If the main alarm time has passed, the main alarm is marked as expired.
01440  * If 'adjustRecurrence' is true, ensure that the next scheduled recurrence is
01441  * after the current time.
01442  * Reply = true if a repetition has been deferred.
01443  */
01444 bool KAEvent::defer(const DateTime& dateTime, bool reminder, bool adjustRecurrence)
01445 {
01446     bool result = false;
01447     cancelCancelledDeferral();
01448     if (checkRecur() == KARecurrence::NO_RECUR)
01449     {
01450         if (mReminderMinutes  ||  mDeferral == REMINDER_DEFERRAL  ||  mArchiveReminderMinutes)
01451         {
01452             if (dateTime < mNextMainDateTime.dateTime())
01453             {
01454                 set_deferral(REMINDER_DEFERRAL);   // defer reminder alarm
01455                 mDeferralTime = dateTime;
01456             }
01457             else
01458             {
01459                 // Deferring past the main alarm time, so adjust any existing deferral
01460                 if (mReminderMinutes  ||  mDeferral == REMINDER_DEFERRAL)
01461                     set_deferral(NO_DEFERRAL);
01462             }
01463             // Remove any reminder alarm, but keep a note of it for archiving purposes
01464             set_archiveReminder();
01465         }
01466         if (mDeferral != REMINDER_DEFERRAL)
01467         {
01468             // We're deferring the main alarm, not a reminder
01469             if (mRepeatCount  &&  dateTime < mainEndRepeatTime())
01470             {
01471                 // The alarm is repeated, and we're deferring to a time before the last repetition
01472                 set_deferral(NORMAL_DEFERRAL);
01473                 mDeferralTime = dateTime;
01474                 result = true;
01475             }
01476             else
01477             {
01478                 // Main alarm has now expired
01479                 mNextMainDateTime = mDeferralTime = dateTime;
01480                 set_deferral(NORMAL_DEFERRAL);
01481                 if (!mMainExpired)
01482                 {
01483                     // Mark the alarm as expired now
01484                     mMainExpired = true;
01485                     --mAlarmCount;
01486                     if (mRepeatAtLogin)
01487                     {
01488                         // Remove the repeat-at-login alarm, but keep a note of it for archiving purposes
01489                         mArchiveRepeatAtLogin = true;
01490                         mRepeatAtLogin = false;
01491                         --mAlarmCount;
01492                     }
01493                 }
01494             }
01495         }
01496     }
01497     else if (reminder)
01498     {
01499         // Deferring a reminder for a recurring alarm
01500         if (dateTime >= mNextMainDateTime.dateTime())
01501             set_deferral(NO_DEFERRAL);    // (error)
01502         else
01503         {
01504             set_deferral(REMINDER_DEFERRAL);
01505             mDeferralTime = dateTime;
01506         }
01507     }
01508     else
01509     {
01510         mDeferralTime = dateTime;
01511         if (mDeferral <= 0)
01512             set_deferral(NORMAL_DEFERRAL);
01513         if (adjustRecurrence)
01514         {
01515             QDateTime now = QDateTime::currentDateTime();
01516             if (mainEndRepeatTime() < now)
01517             {
01518                 // The last repetition (if any) of the current recurrence has already passed.
01519                 // Adjust to the next scheduled recurrence after now.
01520                 if (!mMainExpired  &&  setNextOccurrence(now, true) == NO_OCCURRENCE)
01521                 {
01522                     mMainExpired = true;
01523                     --mAlarmCount;
01524                 }
01525             }
01526         }
01527     }
01528     mUpdated = true;
01529     return result;
01530 }
01531 
01532 /******************************************************************************
01533  * Cancel any deferral alarm.
01534  */
01535 void KAEvent::cancelDefer()
01536 {
01537     if (mDeferral > 0)
01538     {
01539         // Set the deferral time to be the same as the next recurrence/repetition.
01540         // This prevents an immediate retriggering of the alarm.
01541         if (mMainExpired
01542         ||  nextOccurrence(QDateTime::currentDateTime(), mDeferralTime, RETURN_REPETITION) == NO_OCCURRENCE)
01543         {
01544             // The main alarm has expired, so simply delete the deferral
01545             mDeferralTime = DateTime();
01546             set_deferral(NO_DEFERRAL);
01547         }
01548         else
01549             set_deferral(CANCEL_DEFERRAL);
01550         mUpdated = true;
01551     }
01552 }
01553 
01554 /******************************************************************************
01555  * Cancel any cancelled deferral alarm.
01556  */
01557 void KAEvent::cancelCancelledDeferral()
01558 {
01559     if (mDeferral == CANCEL_DEFERRAL)
01560     {
01561         mDeferralTime = DateTime();
01562         set_deferral(NO_DEFERRAL);
01563     }
01564 }
01565 
01566 /******************************************************************************
01567 *  Find the latest time which the alarm can currently be deferred to.
01568 */
01569 DateTime KAEvent::deferralLimit(KAEvent::DeferLimitType* limitType) const
01570 {
01571     DeferLimitType ltype;
01572     DateTime endTime;
01573     bool recurs = (checkRecur() != KARecurrence::NO_RECUR);
01574     if (recurs  ||  mRepeatCount)
01575     {
01576         // It's a repeated alarm. Don't allow it to be deferred past its
01577         // next occurrence or repetition.
01578         DateTime reminderTime;
01579         QDateTime now = QDateTime::currentDateTime();
01580         OccurType type = nextOccurrence(now, endTime, RETURN_REPETITION);
01581         if (type & OCCURRENCE_REPEAT)
01582             ltype = LIMIT_REPETITION;
01583         else if (type == NO_OCCURRENCE)
01584             ltype = LIMIT_NONE;
01585         else if (mReminderMinutes  &&  (now < (reminderTime = endTime.addMins(-mReminderMinutes))))
01586         {
01587             endTime = reminderTime;
01588             ltype = LIMIT_REMINDER;
01589         }
01590         else if (type == FIRST_OR_ONLY_OCCURRENCE  &&  !recurs)
01591             ltype = LIMIT_REPETITION;
01592         else
01593             ltype = LIMIT_RECURRENCE;
01594     }
01595     else if ((mReminderMinutes  ||  mDeferral == REMINDER_DEFERRAL  ||  mArchiveReminderMinutes)
01596          &&  QDateTime::currentDateTime() < mNextMainDateTime.dateTime())
01597     {
01598         // It's an reminder alarm. Don't allow it to be deferred past its main alarm time.
01599         endTime = mNextMainDateTime;
01600         ltype = LIMIT_REMINDER;
01601     }
01602     else
01603         ltype = LIMIT_NONE;
01604     if (ltype != LIMIT_NONE)
01605         endTime = endTime.addMins(-1);
01606     if (limitType)
01607         *limitType = ltype;
01608     return endTime;
01609 }
01610 
01611 /******************************************************************************
01612  * Set the event to be a copy of the specified event, making the specified
01613  * alarm the 'displaying' alarm.
01614  * The purpose of setting up a 'displaying' alarm is to be able to reinstate
01615  * the alarm message in case of a crash, or to reinstate it should the user
01616  * choose to defer the alarm. Note that even repeat-at-login alarms need to be
01617  * saved in case their end time expires before the next login.
01618  * Reply = true if successful, false if alarm was not copied.
01619  */
01620 bool KAEvent::setDisplaying(const KAEvent& event, KAAlarm::Type alarmType, const QDateTime& repeatAtLoginTime)
01621 {
01622     if (!mDisplaying
01623     &&  (alarmType == KAAlarm::MAIN_ALARM
01624       || alarmType == KAAlarm::REMINDER_ALARM
01625       || alarmType == KAAlarm::DEFERRED_REMINDER_ALARM
01626       || alarmType == KAAlarm::DEFERRED_ALARM
01627       || alarmType == KAAlarm::AT_LOGIN_ALARM))
01628     {
01629 //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;
01630         KAAlarm al = event.alarm(alarmType);
01631         if (al.valid())
01632         {
01633             *this = event;
01634             setUid(DISPLAYING);
01635             mDisplaying     = true;
01636             mDisplayingTime = (alarmType == KAAlarm::AT_LOGIN_ALARM) ? repeatAtLoginTime : al.dateTime();
01637             switch (al.type())
01638             {
01639                 case KAAlarm::AT_LOGIN__ALARM:                mDisplayingFlags = REPEAT_AT_LOGIN;  break;
01640                 case KAAlarm::REMINDER__ALARM:                mDisplayingFlags = REMINDER;  break;
01641                 case KAAlarm::DEFERRED_REMINDER_TIME__ALARM:  mDisplayingFlags = REMINDER | TIME_DEFERRAL;  break;
01642                 case KAAlarm::DEFERRED_REMINDER_DATE__ALARM:  mDisplayingFlags = REMINDER | DATE_DEFERRAL;  break;
01643                 case KAAlarm::DEFERRED_TIME__ALARM:           mDisplayingFlags = TIME_DEFERRAL;  break;
01644                 case KAAlarm::DEFERRED_DATE__ALARM:           mDisplayingFlags = DATE_DEFERRAL;  break;
01645                 default:                                      mDisplayingFlags = 0;  break;
01646             }
01647             ++mAlarmCount;
01648             mUpdated = true;
01649             return true;
01650         }
01651     }
01652     return false;
01653 }
01654 
01655 /******************************************************************************
01656  * Return the original alarm which the displaying alarm refers to.
01657  */
01658 KAAlarm KAEvent::convertDisplayingAlarm() const
01659 {
01660     KAAlarm al;
01661     if (mDisplaying)
01662     {
01663         al = alarm(KAAlarm::DISPLAYING_ALARM);
01664         if (mDisplayingFlags & REPEAT_AT_LOGIN)
01665         {
01666             al.mRepeatAtLogin = true;
01667             al.mType = KAAlarm::AT_LOGIN__ALARM;
01668         }
01669         else if (mDisplayingFlags & DEFERRAL)
01670         {
01671             al.mDeferred = true;
01672             al.mType = (mDisplayingFlags == (REMINDER | DATE_DEFERRAL)) ? KAAlarm::DEFERRED_REMINDER_DATE__ALARM
01673                      : (mDisplayingFlags == (REMINDER | TIME_DEFERRAL)) ? KAAlarm::DEFERRED_REMINDER_TIME__ALARM
01674                      : (mDisplayingFlags == DATE_DEFERRAL) ? KAAlarm::DEFERRED_DATE__ALARM
01675                      : KAAlarm::DEFERRED_TIME__ALARM;
01676         }
01677         else if (mDisplayingFlags & REMINDER)
01678             al.mType = KAAlarm::REMINDER__ALARM;
01679         else
01680             al.mType = KAAlarm::MAIN__ALARM;
01681     }
01682     return al;
01683 }
01684 
01685 /******************************************************************************
01686  * Reinstate the original event from the 'displaying' event.
01687  */
01688 void KAEvent::reinstateFromDisplaying(const KAEvent& dispEvent)
01689 {
01690     if (dispEvent.mDisplaying)
01691     {
01692         *this = dispEvent;
01693         setUid(ACTIVE);
01694         mDisplaying = false;
01695         --mAlarmCount;
01696         mUpdated = true;
01697     }
01698 }
01699 
01700 /******************************************************************************
01701  * Determine whether the event will occur after the specified date/time.
01702  * If 'includeRepetitions' is true and the alarm has a simple repetition, it
01703  * returns true if any repetitions occur after the specified date/time.
01704  */
01705 bool KAEvent::occursAfter(const QDateTime& preDateTime, bool includeRepetitions) const
01706 {
01707     QDateTime dt;
01708     if (checkRecur() != KARecurrence::NO_RECUR)
01709     {
01710         if (mRecurrence->duration() < 0)
01711             return true;    // infinite recurrence
01712         dt = mRecurrence->endDateTime();
01713     }
01714     else
01715         dt = mNextMainDateTime.dateTime();
01716     if (mStartDateTime.isDateOnly())
01717     {
01718         QDate pre = preDateTime.date();
01719         if (preDateTime.time() < Preferences::startOfDay())
01720             pre = pre.addDays(-1);    // today's recurrence (if today recurs) is still to come
01721         if (pre < dt.date())
01722             return true;
01723     }
01724     else if (preDateTime < dt)
01725         return true;
01726 
01727     if (includeRepetitions  &&  mRepeatCount)
01728     {
01729         dt.addSecs(mRepeatCount * mRepeatInterval * 60);
01730         if (preDateTime < dt)
01731             return true;
01732     }
01733     return false;
01734 }
01735 
01736 /******************************************************************************
01737  * Get the date/time of the next occurrence of the event, after the specified
01738  * date/time.
01739  * 'result' = date/time of next occurrence, or invalid date/time if none.
01740  */
01741 KAEvent::OccurType KAEvent::nextOccurrence(const QDateTime& preDateTime, DateTime& result,
01742                                            KAEvent::OccurOption includeRepetitions) const
01743 {
01744     int repeatSecs = 0;
01745     QDateTime pre = preDateTime;
01746     if (includeRepetitions != IGNORE_REPETITION)
01747     {
01748         if (!mRepeatCount)
01749             includeRepetitions = IGNORE_REPETITION;
01750         else
01751         {
01752             repeatSecs = mRepeatInterval * 60;
01753             pre = preDateTime.addSecs(-mRepeatCount * repeatSecs);
01754         }
01755     }
01756 
01757     OccurType type;
01758     bool recurs = (checkRecur() != KARecurrence::NO_RECUR);
01759     if (recurs)
01760     {
01761         int remainingCount;
01762         type = nextRecurrence(pre, result, remainingCount);
01763     }
01764     else if (pre < mNextMainDateTime.dateTime())
01765     {
01766         result = mNextMainDateTime;
01767         type = FIRST_OR_ONLY_OCCURRENCE;
01768     }
01769     else
01770     {
01771         result = DateTime();
01772         type = NO_OCCURRENCE;
01773     }
01774 
01775     if (type != NO_OCCURRENCE  &&  result <= preDateTime)
01776     {
01777         // The next occurrence is a simple repetition
01778         int repetition = result.secsTo(preDateTime) / repeatSecs + 1;
01779         DateTime repeatDT = result.addSecs(repetition * repeatSecs);
01780         if (recurs)
01781         {
01782             // We've found a recurrence before the specified date/time, which has
01783             // a simple repetition after the date/time.
01784             // However, if the intervals between recurrences vary, we could possibly
01785             // have missed a later recurrence, which fits the criterion, so check again.
01786             DateTime dt;
01787             OccurType newType = previousOccurrence(repeatDT.dateTime(), dt, false);
01788             if (dt > result)
01789             {
01790                 type = newType;
01791                 result = dt;
01792                 if (includeRepetitions == RETURN_REPETITION  &&  result <= preDateTime)
01793                 {
01794                     // The next occurrence is a simple repetition
01795                     int repetition = result.secsTo(preDateTime) / repeatSecs + 1;
01796                     result = result.addSecs(repetition * repeatSecs);
01797                     type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01798                 }
01799                 return type;
01800             }
01801         }
01802         if (includeRepetitions == RETURN_REPETITION)
01803         {
01804             // The next occurrence is a simple repetition
01805             result = repeatDT;
01806             type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01807         }
01808     }
01809     return type;
01810 }
01811 
01812 /******************************************************************************
01813  * Get the date/time of the last previous occurrence of the event, before the
01814  * specified date/time.
01815  * If 'includeRepetitions' is true and the alarm has a simple repetition, the
01816  * last previous repetition is returned if appropriate.
01817  * 'result' = date/time of previous occurrence, or invalid date/time if none.
01818  */
01819 KAEvent::OccurType KAEvent::previousOccurrence(const QDateTime& afterDateTime, DateTime& result, bool includeRepetitions) const
01820 {
01821     if (mStartDateTime >= afterDateTime)
01822     {
01823         result = QDateTime();
01824         return NO_OCCURRENCE;     // the event starts after the specified date/time
01825     }
01826 
01827     // Find the latest recurrence of the event
01828     OccurType type;
01829     if (checkRecur() == KARecurrence::NO_RECUR)
01830     {
01831         result = mStartDateTime;
01832         type = FIRST_OR_ONLY_OCCURRENCE;
01833     }
01834     else
01835     {
01836         QDateTime recurStart = mRecurrence->startDateTime();
01837         QDateTime after = afterDateTime;
01838         if (mStartDateTime.isDateOnly()  &&  afterDateTime.time() > Preferences::startOfDay())
01839             after = after.addDays(1);    // today's recurrence (if today recurs) has passed
01840         QDateTime dt = mRecurrence->getPreviousDateTime(after);
01841         result.set(dt, mStartDateTime.isDateOnly());
01842         if (!dt.isValid())
01843             return NO_OCCURRENCE;
01844         if (dt == recurStart)
01845             type = FIRST_OR_ONLY_OCCURRENCE;
01846         else if (mRecurrence->getNextDateTime(dt).isValid())
01847             type = result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
01848         else
01849             type = LAST_RECURRENCE;
01850     }
01851 
01852     if (includeRepetitions  &&  mRepeatCount)
01853     {
01854         // Find the latest repetition which is before the specified time.
01855         // N.B. This is coded to avoid 32-bit integer overflow which occurs
01856         //      in QDateTime::secsTo() for large enough time differences.
01857         int repeatSecs = mRepeatInterval * 60;
01858         DateTime lastRepetition = result.addSecs(mRepeatCount * repeatSecs);
01859         if (lastRepetition < afterDateTime)
01860         {
01861             result = lastRepetition;
01862             return static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01863         }
01864         int repetition = (result.dateTime().secsTo(afterDateTime) - 1) / repeatSecs;
01865         if (repetition > 0)
01866         {
01867             result = result.addSecs(repetition * repeatSecs);
01868             return static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01869         }
01870     }
01871     return type;
01872 }
01873 
01874 /******************************************************************************
01875  * Set the date/time of the event to the next scheduled occurrence after the
01876  * specified date/time, provided that this is later than its current date/time.
01877  * Any reminder alarm is adjusted accordingly.
01878  * If 'includeRepetitions' is true and the alarm has a simple repetition, and
01879  * a repetition of a previous recurrence occurs after the specified date/time,
01880  * that previous recurrence is returned instead.
01881  */
01882 KAEvent::OccurType KAEvent::setNextOccurrence(const QDateTime& preDateTime, bool includeRepetitions)
01883 {
01884     if (preDateTime < mNextMainDateTime.dateTime())
01885         return FIRST_OR_ONLY_OCCURRENCE;    // it might not be the first recurrence - tant pis
01886     QDateTime pre = preDateTime;
01887     if (includeRepetitions)
01888     {
01889         if (!mRepeatCount)
01890             includeRepetitions = false;
01891         else
01892             pre = preDateTime.addSecs(-mRepeatCount * mRepeatInterval * 60);
01893     }
01894 
01895     DateTime dt;
01896     OccurType type;
01897     if (pre < mNextMainDateTime.dateTime())
01898     {
01899         dt = mNextMainDateTime;
01900         type = FIRST_OR_ONLY_OCCURRENCE;   // may not actually be the first occurrence
01901     }
01902     else if (checkRecur() != KARecurrence::NO_RECUR)
01903     {
01904         int remainingCount;
01905         type = nextRecurrence(pre, dt, remainingCount);
01906         if (type == NO_OCCURRENCE)
01907             return NO_OCCURRENCE;
01908         if (type != FIRST_OR_ONLY_OCCURRENCE  &&  dt != mNextMainDateTime)
01909         {
01910             // Need to reschedule the next trigger date/time
01911             mNextMainDateTime = dt;
01912             if (mRecurrence->duration() > 0)
01913                 mRemainingRecurrences = remainingCount;
01914             // Reinstate the reminder (if any) for the rescheduled recurrence
01915             if (mDeferral == REMINDER_DEFERRAL  ||  mArchiveReminderMinutes)
01916             {
01917                 if (mReminderOnceOnly)
01918                 {
01919                     if (mReminderMinutes)
01920                         set_archiveReminder();
01921                 }
01922                 else
01923                     set_reminder(mArchiveReminderMinutes);
01924             }
01925             if (mDeferral == REMINDER_DEFERRAL)
01926                 set_deferral(NO_DEFERRAL);
01927             mUpdated = true;
01928         }
01929     }
01930     else
01931         return NO_OCCURRENCE;
01932 
01933     if (includeRepetitions  &&  dt.dateTime() <= preDateTime)
01934     {
01935         // The next occurrence is a simple repetition.
01936         type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01937         // Repetitions can't have a reminder, so remove any.
01938         if (mReminderMinutes)
01939             set_archiveReminder();
01940         if (mDeferral == REMINDER_DEFERRAL)
01941             set_deferral(NO_DEFERRAL);
01942         mUpdated = true;
01943     }
01944     return type;
01945 }
01946 
01947 /******************************************************************************
01948  * Get the date/time of the next recurrence of the event, after the specified
01949  * date/time.
01950  * 'result' = date/time of next occurrence, or invalid date/time if none.
01951  * 'remainingCount' = number of repetitions due, including the next occurrence.
01952  */
01953 KAEvent::OccurType KAEvent::nextRecurrence(const QDateTime& preDateTime, DateTime& result, int& remainingCount) const
01954 {
01955     QDateTime recurStart = mRecurrence->startDateTime();
01956     QDateTime pre = preDateTime;
01957     if (mStartDateTime.isDateOnly()  &&  preDateTime.time() < Preferences::startOfDay())
01958         pre = pre.addDays(-1);    // today's recurrence (if today recurs) is still to come
01959     remainingCount = 0;
01960     QDateTime dt = mRecurrence->getNextDateTime(pre);
01961     result.set(dt, mStartDateTime.isDateOnly());
01962     if (!dt.isValid())
01963         return NO_OCCURRENCE;
01964     if (dt == recurStart)
01965     {
01966         remainingCount = mRecurrence->duration();
01967         return FIRST_OR_ONLY_OCCURRENCE;
01968     }
01969     remainingCount = mRecurrence->duration() - mRecurrence->durationTo(dt) + 1;
01970     if (remainingCount == 1)
01971         return LAST_RECURRENCE;
01972     return result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
01973 }
01974 
01975 /******************************************************************************
01976  * Return the recurrence interval as text suitable for display.
01977  */
01978 QString KAEvent::recurrenceText(bool brief) const
01979 {
01980     if (mRepeatAtLogin)
01981         return brief ? i18n("Brief form of 'At Login'", "Login") : i18n("At login");
01982     if (mRecurrence)
01983     {
01984         int frequency = mRecurrence->frequency();
01985         switch (mRecurrence->defaultRRuleConst()->recurrenceType())
01986         {
01987             case RecurrenceRule::rMinutely:
01988                 if (frequency < 60)
01989                     return i18n("1 Minute", "%n Minutes", frequency);
01990                 else if (frequency % 60 == 0)
01991                     return i18n("1 Hour", "%n Hours", frequency/60);
01992                 else
01993                 {
01994                     QString mins;
01995                     return i18n("Hours and Minutes", "%1H %2M").arg(QString::number(frequency/60)).arg(mins.sprintf("%02d", frequency%60));
01996                 }
01997             case RecurrenceRule::rDaily:
01998                 return i18n("1 Day", "%n Days", frequency);
01999             case RecurrenceRule::rWeekly:
02000                 return i18n("1 Week", "%n Weeks", frequency);
02001             case RecurrenceRule::rMonthly:
02002                 return i18n("1 Month", "%n Months", frequency);
02003             case RecurrenceRule::rYearly:
02004                 return i18n("1 Year", "%n Years", frequency);
02005             case RecurrenceRule::rNone:
02006             default:
02007                 break;
02008         }
02009     }
02010     return brief ? QString::null : i18n("None");
02011 }
02012 
02013 /******************************************************************************
02014  * Return the repetition interval as text suitable for display.
02015  */
02016 QString KAEvent::repetitionText(bool brief) const
02017 {
02018     if (mRepeatCount)
02019     {
02020         if (mRepeatInterval % 1440)
02021         {
02022             if (mRepeatInterval < 60)
02023                 return i18n("1 Minute", "%n Minutes", mRepeatInterval);
02024             if (mRepeatInterval % 60 == 0)
02025                 return i18n("1 Hour", "%n Hours", mRepeatInterval/60);
02026             QString mins;
02027             return i18n("Hours and Minutes", "%1H %2M").arg(QString::number(mRepeatInterval/60)).arg(mins.sprintf("%02d", mRepeatInterval%60));
02028         }
02029         if (mRepeatInterval % (7*1440))
02030             return i18n("1 Day", "%n Days", mRepeatInterval/1440);
02031         return i18n("1 Week", "%n Weeks", mRepeatInterval/(7*1440));
02032     }
02033     return brief ? QString::null : i18n("None");
02034 }
02035 
02036 /******************************************************************************
02037  * Adjust the event date/time to the first recurrence of the event, on or after
02038  * start date/time. The event start date may not be a recurrence date, in which
02039  * case a later date will be set.
02040  */
02041 void KAEvent::setFirstRecurrence()
02042 {
02043     switch (checkRecur())
02044     {
02045         case KARecurrence::NO_RECUR:
02046         case KARecurrence::MINUTELY:
02047             return;
02048         case KARecurrence::ANNUAL_DATE:
02049         case KARecurrence::ANNUAL_POS:
02050             if (mRecurrence->yearMonths().isEmpty())
02051                 return;    // (presumably it's a template)
02052             break;
02053         case KARecurrence::DAILY:
02054         case KARecurrence::WEEKLY:
02055         case KARecurrence::MONTHLY_POS:
02056         case KARecurrence::MONTHLY_DAY:
02057             break;
02058     }
02059     QDateTime recurStart = mRecurrence->startDateTime();
02060     if (mRecurrence->recursOn(recurStart.date()))
02061         return;           // it already recurs on the start date
02062 
02063     // Set the frequency to 1 to find the first possible occurrence
02064     int frequency = mRecurrence->frequency();
02065     mRecurrence->setFrequency(1);
02066     int remainingCount;
02067     DateTime next;
02068     nextRecurrence(mNextMainDateTime.dateTime(), next, remainingCount);
02069     if (!next.isValid())
02070         mRecurrence->setStartDateTime(recurStart);   // reinstate the old value
02071     else
02072     {
02073         mRecurrence->setStartDateTime(next.dateTime());
02074         mStartDateTime = mNextMainDateTime = next;
02075         mUpdated = true;
02076     }
02077     mRecurrence->setFrequency(frequency);    // restore the frequency
02078 }
02079 
02080 /******************************************************************************
02081 *  Initialise the event's recurrence from a KCal::Recurrence.
02082 *  The event's start date/time is not changed.
02083 */
02084 void KAEvent::setRecurrence(const KARecurrence& recurrence)
02085 {
02086     mUpdated = true;
02087     delete mRecurrence;
02088     if (recurrence.doesRecur())
02089     {
02090         mRecurrence = new KARecurrence(recurrence);
02091         mRecurrence->setStartDateTime(mStartDateTime.dateTime());
02092         mRecurrence->setFloats(mStartDateTime.isDateOnly());
02093         mRemainingRecurrences = mRecurrence->duration();
02094         if (mRemainingRecurrences > 0  &&  !isTemplate())
02095             mRemainingRecurrences -= mRecurrence->durationTo(mNextMainDateTime.dateTime()) - 1;
02096     }
02097     else
02098     {
02099         mRecurrence = 0;
02100         mRemainingRecurrences = 0;
02101     }
02102 
02103     // Adjust simple repetition values to fit the recurrence
02104     setRepetition(mRepeatInterval, mRepeatCount);
02105 }
02106 
02107 /******************************************************************************
02108 *  Initialise the event's simple repetition.
02109 *  The repetition length is adjusted if necessary to fit any recurrence interval.
02110 *  Reply = false if a non-daily interval was specified for a date-only recurrence.
02111 */
02112 bool KAEvent::setRepetition(int interval, int count)
02113 {
02114     mUpdated        = true;
02115     mRepeatInterval = 0;
02116     mRepeatCount    = 0;
02117     if (interval > 0  &&  count > 0  &&  !mRepeatAtLogin)
02118     {
02119         if (interval % 1440  &&  mStartDateTime.isDateOnly())
02120             return false;    // interval must be in units of days for date-only alarms
02121         if (checkRecur() != KARecurrence::NO_RECUR)
02122         {
02123             int longestInterval = mRecurrence->longestInterval() - 1;
02124             if (interval * count > longestInterval)
02125                 count = longestInterval / interval;
02126         }
02127         mRepeatInterval = interval;
02128         mRepeatCount    = count;
02129     }
02130     return true;
02131 }
02132 
02133 /******************************************************************************
02134  * Set the recurrence to recur at a minutes interval.
02135  * Parameters:
02136  *    freq  = how many minutes between recurrences.
02137  *    count = number of occurrences, including first and last.
02138  *          = -1 to recur indefinitely.
02139  *          = 0 to use 'end' instead.
02140  *    end   = end date/time (invalid to use 'count' instead).
02141  * Reply = false if no recurrence was set up.
02142  */
02143 bool KAEvent::setRecurMinutely(int freq, int count, const QDateTime& end)
02144 {
02145     return setRecur(RecurrenceRule::rMinutely, freq, count, end);
02146 }
02147 
02148 /******************************************************************************
02149  * Set the recurrence to recur daily.
02150  * Parameters:
02151  *    freq  = how many days between recurrences.
02152  *    days  = which days of the week alarms are allowed to occur on.
02153  *    count = number of occurrences, including first and last.
02154  *          = -1 to recur indefinitely.
02155  *          = 0 to use 'end' instead.
02156  *    end   = end date (invalid to use 'count' instead).
02157  * Reply = false if no recurrence was set up.
02158  */
02159 bool KAEvent::setRecurDaily(int freq, const QBitArray& days, int count, const QDate& end)
02160 {
02161     if (!setRecur(RecurrenceRule::rDaily, freq, count, end))
02162         return false;
02163     int n = 0;
02164     for (int i = 0;  i < 7;  ++i)
02165     {
02166         if (days.testBit(i))
02167             ++n;
02168     }
02169     if (n < 7)
02170         mRecurrence->addWeeklyDays(days);
02171     return true;
02172 }
02173 
02174 /******************************************************************************
02175  * Set the recurrence to recur weekly, on the specified weekdays.
02176  * Parameters:
02177  *    freq  = how many weeks between recurrences.
02178  *    days  = which days of the week alarms should occur on.
02179  *    count = number of occurrences, including first and last.
02180  *          = -1 to recur indefinitely.
02181  *          = 0 to use 'end' instead.
02182  *    end   = end date (invalid to use 'count' instead).
02183  * Reply = false if no recurrence was set up.
02184  */
02185 bool KAEvent::setRecurWeekly(int freq, const QBitArray& days, int count, const QDate& end)
02186 {
02187     if (!setRecur(RecurrenceRule::rWeekly, freq, count, end))
02188         return false;
02189     mRecurrence->addWeeklyDays(days);
02190     return true;
02191 }
02192 
02193 /******************************************************************************
02194  * Set the recurrence to recur monthly, on the specified days within the month.
02195  * Parameters:
02196  *    freq  = how many months between recurrences.
02197  *    days  = which days of the month alarms should occur on.
02198  *    count = number of occurrences, including first and last.
02199  *          = -1 to recur indefinitely.
02200  *          = 0 to use 'end' instead.
02201  *    end   = end date (invalid to use 'count' instead).
02202  * Reply = false if no recurrence was set up.
02203  */
02204 bool KAEvent::setRecurMonthlyByDate(int freq, const QValueList<int>& days, int count, const QDate& end)
02205 {
02206     if (!setRecur(RecurrenceRule::rMonthly, freq, count, end))
02207         return false;
02208     for (QValueListConstIterator<int> it = days.begin();  it != days.end();  ++it)
02209         mRecurrence->addMonthlyDate(*it);
02210     return true;
02211 }
02212 
02213 /******************************************************************************
02214  * Set the recurrence to recur monthly, on the specified weekdays in the
02215  * specified weeks of the month.
02216  * Parameters:
02217  *    freq  = how many months between recurrences.
02218  *    posns = which days of the week/weeks of the month alarms should occur on.
02219  *    count = number of occurrences, including first and last.
02220  *          = -1 to recur indefinitely.
02221  *          = 0 to use 'end' instead.
02222  *    end   = end date (invalid to use 'count' instead).
02223  * Reply = false if no recurrence was set up.
02224  */
02225 bool KAEvent::setRecurMonthlyByPos(int freq, const QValueList<MonthPos>& posns, int count, const QDate& end)
02226 {
02227     if (!setRecur(RecurrenceRule::rMonthly, freq, count, end))
02228         return false;
02229     for (QValueListConstIterator<MonthPos> it = posns.begin();  it != posns.end();  ++it)
02230         mRecurrence->addMonthlyPos((*it).weeknum, (*it).days);
02231     return true;
02232 }
02233 
02234 /******************************************************************************
02235  * Set the recurrence to recur annually, on the specified start date in each
02236  * of the specified months.
02237  * Parameters:
02238  *    freq   = how many years between recurrences.
02239  *    months = which months of the year alarms should occur on.
02240  *    day    = day of month, or 0 to use start date
02241  *    feb29  = when February 29th should recur in non-leap years.
02242  *    count  = number of occurrences, including first and last.
02243  *           = -1 to recur indefinitely.
02244  *           = 0 to use 'end' instead.
02245  *    end    = end date (invalid to use 'count' instead).
02246  * Reply = false if no recurrence was set up.
02247  */
02248 bool KAEvent::setRecurAnnualByDate(int freq, const QValueList<int>& months, int day, KARecurrence::Feb29Type feb29, int count, const QDate& end)
02249 {
02250     if (!setRecur(RecurrenceRule::rYearly, freq, count, end, feb29))
02251         return false;
02252     for (QValueListConstIterator<int> it = months.begin();  it != months.end();  ++it)
02253         mRecurrence->addYearlyMonth(*it);
02254     if (day)
02255         mRecurrence->addMonthlyDate(day);
02256     return true;
02257 }
02258 
02259 /******************************************************************************
02260  * Set the recurrence to recur annually, on the specified weekdays in the
02261  * specified weeks of the specified months.
02262  * Parameters:
02263  *    freq   = how many years between recurrences.
02264  *    posns  = which days of the week/weeks of the month alarms should occur on.
02265  *    months = which months of the year alarms should occur on.
02266  *    count  = number of occurrences, including first and last.
02267  *           = -1 to recur indefinitely.
02268  *           = 0 to use 'end' instead.
02269  *    end    = end date (invalid to use 'count' instead).
02270  * Reply = false if no recurrence was set up.
02271  */
02272 bool KAEvent::setRecurAnnualByPos(int freq, const QValueList<MonthPos>& posns, const QValueList<int>& months, int count, const QDate& end)
02273 {
02274     if (!setRecur(RecurrenceRule::rYearly, freq, count, end))
02275         return false;
02276     for (QValueListConstIterator<int> it = months.begin();  it != months.end();  ++it)
02277         mRecurrence->addYearlyMonth(*it);
02278     for (QValueListConstIterator<MonthPos> it = posns.begin();  it != posns.end();  ++it)
02279         mRecurrence->addYearlyPos((*it).weeknum, (*it).days);
02280     return true;
02281 }
02282 
02283 /******************************************************************************
02284  * Initialise the event's recurrence data.
02285  * Parameters:
02286  *    freq  = how many intervals between recurrences.
02287  *    count = number of occurrences, including first and last.
02288  *          = -1 to recur indefinitely.
02289  *          = 0 to use 'end' instead.
02290  *    end   = end date/time (invalid to use 'count' instead).
02291  * Reply = false if no recurrence was set up.
02292  */
02293 bool KAEvent::setRecur(RecurrenceRule::PeriodType recurType, int freq, int count, const QDateTime& end, KARecurrence::Feb29Type feb29)
02294 {
02295     if (count >= -1  &&  (count || end.date().isValid()))
02296     {
02297         if (!mRecurrence)
02298             mRecurrence = new KARecurrence;
02299         if (mRecurrence->init(recurType, freq, count, mNextMainDateTime, end, feb29))
02300         {
02301             mUpdated = true;
02302             mRemainingRecurrences = count;
02303             return true;
02304         }
02305     }
02306     clearRecur();
02307     return false;
02308 }
02309 
02310 /******************************************************************************
02311  * Clear the event's recurrence and alarm repetition data.
02312  */
02313 void KAEvent::clearRecur()
02314 {
02315     mUpdated = true;
02316     delete mRecurrence;
02317     mRecurrence = 0;
02318     mRemainingRecurrences = 0;
02319 }
02320 
02321 /******************************************************************************
02322  * Validate the event's recurrence and alarm repetition data, correcting any
02323  * inconsistencies (which should never occur!).
02324  * Reply = true if a recurrence (as opposed to a login repetition) exists.
02325  */
02326 KARecurrence::Type KAEvent::checkRecur() const
02327 {
02328     if (mRecurrence)
02329     {
02330         KARecurrence::Type type = mRecurrence->type();
02331         switch (type)
02332         {
02333             case KARecurrence::MINUTELY:     // hourly      
02334             case KARecurrence::DAILY:        // daily
02335             case KARecurrence::WEEKLY:       // weekly on multiple days of week
02336             case KARecurrence::MONTHLY_DAY:  // monthly on multiple dates in month
02337             case KARecurrence::MONTHLY_POS:  // monthly on multiple nth day of week
02338             case KARecurrence::ANNUAL_DATE:  // annually on multiple months (day of month = start date)
02339             case KARecurrence::ANNUAL_POS:   // annually on multiple nth day of week in multiple months
02340                 return type;
02341             default:
02342                 if (mRecurrence)
02343                 {
02344                     delete mRecurrence;     // this shouldn't exist!!
02345                     const_cast<KAEvent*>(this)->mRecurrence = 0;
02346                 }
02347                 break;
02348         }
02349     }
02350     return KARecurrence::NO_RECUR;
02351 }
02352 
02353 
02354 /******************************************************************************
02355  * Return the recurrence interval in units of the recurrence period type.
02356  */
02357 int KAEvent::recurInterval() const
02358 {
02359     if (mRecurrence)
02360     {
02361         switch (mRecurrence->type())
02362         {
02363             case KARecurrence::MINUTELY:
02364             case KARecurrence::DAILY:
02365             case KARecurrence::WEEKLY:
02366             case KARecurrence::MONTHLY_DAY:
02367             case KARecurrence::MONTHLY_POS:
02368             case KARecurrence::ANNUAL_DATE:
02369             case KARecurrence::ANNUAL_POS:
02370                 return mRecurrence->frequency();
02371             default:
02372                 break;
02373         }
02374     }
02375     return 0;
02376 }
02377 
02378 #if 0
02379 /******************************************************************************
02380  * Convert a QValueList<WDayPos> to QValueList<MonthPos>.
02381  */
02382 QValueList<KAEvent::MonthPos> KAEvent::convRecurPos(const QValueList<KCal::RecurrenceRule::WDayPos>& wdaypos)
02383 {
02384     QValueList<MonthPos> mposns;
02385     for (QValueList<KCal::RecurrenceRule::WDayPos>::ConstIterator it = wdaypos.begin();  it != wdaypos.end();  ++it)
02386     {
02387         int daybit  = (*it).day() - 1;
02388         int weeknum = (*it).pos();
02389         bool found = false;
02390         for (QValueList<MonthPos>::Iterator mit = mposns.begin();  mit != mposns.end();  ++mit)
02391         {
02392             if ((*mit).weeknum == weeknum)
02393             {
02394                 (*mit).days.setBit(daybit);
02395                 found = true;
02396                 break;
02397             }
02398         }
02399         if (!found)
02400         {
02401             MonthPos mpos;
02402             mpos.days.fill(false);
02403             mpos.days.setBit(daybit);
02404             mpos.weeknum = weeknum;
02405             mposns.append(mpos);
02406         }
02407     }
02408     return mposns;
02409 }
02410 #endif
02411 
02412 /******************************************************************************
02413  * Find the alarm template with the specified name.
02414  * Reply = invalid event if not found.
02415  */
02416 KAEvent KAEvent::findTemplateName(AlarmCalendar& calendar, const QString& name)
02417 {
02418     KAEvent event;
02419     Event::List events = calendar.events();
02420     for (Event::List::ConstIterator evit = events.begin();  evit != events.end();  ++evit)
02421     {
02422         Event* ev = *evit;
02423         if (ev->summary() == name)
02424         {
02425             event.set(*ev);
02426             if (!event.isTemplate())
02427                 return KAEvent();    // this shouldn't ever happen
02428             break;
02429         }
02430     }
02431     return event;
02432 }
02433 
02434 /******************************************************************************
02435  * Adjust the time at which date-only events will occur for each of the events
02436  * in a list. Events for which both date and time are specified are left
02437  * unchanged.
02438  * Reply = true if any events have been updated.
02439  */
02440 bool KAEvent::adjustStartOfDay(const Event::List& events)
02441 {
02442     bool changed = false;
02443     QTime startOfDay = Preferences::startOfDay();
02444     for (Event::List::ConstIterator evit = events.begin();  evit != events.end();  ++evit)
02445     {
02446         Event* event = *evit;
02447         const QStringList& cats = event->categories();
02448         if (cats.find(DATE_ONLY_CATEGORY) != cats.end())
02449         {
02450             // It's an untimed event, so fix it
02451             QTime oldTime = event->dtStart().time();
02452             int adjustment = oldTime.secsTo(startOfDay);
02453             if (adjustment)
02454             {
02455                 event->setDtStart(QDateTime(event->dtStart().date(), startOfDay));
02456                 Alarm::List alarms = event->alarms();
02457                 int deferralOffset = 0;
02458                 for (Alarm::List::ConstIterator alit = alarms.begin();  alit != alarms.end();  ++alit)
02459                 {
02460                     // Parse the next alarm's text
02461                     Alarm& alarm = **alit;
02462                     AlarmData data;
02463                     readAlarm(alarm, data);
02464                     if (data.type & KAAlarm::TIMED_DEFERRAL_FLAG)
02465                     {
02466                         // Timed deferral alarm, so adjust the offset
02467                         deferralOffset = alarm.startOffset().asSeconds();
02468                         alarm.setStartOffset(deferralOffset - adjustment);
02469                     }
02470                     else if (data.type == KAAlarm::AUDIO__ALARM
02471                     &&       alarm.startOffset().asSeconds() == deferralOffset)
02472                     {
02473                         // Audio alarm is set for the same time as the deferral alarm
02474                         alarm.setStartOffset(deferralOffset - adjustment);
02475                     }
02476                 }
02477                 changed = true;
02478             }
02479         }
02480         else
02481         {
02482             // It's a timed event. Fix any untimed alarms.
02483             int deferralOffset = 0;
02484             int newDeferralOffset = 0;
02485             AlarmMap alarmMap;
02486             readAlarms(*event, &alarmMap);
02487             for (AlarmMap::Iterator it = alarmMap.begin();  it != alarmMap.end();  ++it)
02488             {
02489                 const AlarmData& data = it.data();
02490                 if ((data.type & KAAlarm::DEFERRED_ALARM)
02491                 &&  !(data.type & KAAlarm::TIMED_DEFERRAL_FLAG))
02492                 {
02493                     // Date-only deferral alarm, so adjust its time
02494                     QDateTime altime = data.alarm->time();
02495                     altime.setTime(startOfDay);
02496                     deferralOffset = data.alarm->startOffset().asSeconds();
02497                     newDeferralOffset = event->dtStart().secsTo(altime);
02498                     const_cast<Alarm*>(data.alarm)->setStartOffset(newDeferralOffset);
02499                     changed = true;
02500                 }
02501                 else if (data.type == KAAlarm::AUDIO__ALARM
02502                 &&       data.alarm->startOffset().asSeconds() == deferralOffset)
02503                 {
02504                     // Audio alarm is set for the same time as the deferral alarm
02505                     const_cast<Alarm*>(data.alarm)->setStartOffset(newDeferralOffset);
02506                     changed = true;
02507                 }
02508             }
02509         }
02510     }
02511     return changed;
02512 }
02513 
02514 /******************************************************************************
02515  * If the calendar was written by a previous version of KAlarm, do any
02516  * necessary format conversions on the events to ensure that when the calendar
02517  * is saved, no information is lost or corrupted.
02518  */
02519 void KAEvent::convertKCalEvents(KCal::Calendar& calendar, int version, bool adjustSummerTime)
02520 {
02521     // KAlarm pre-0.9 codes held in the alarm's DESCRIPTION property
02522     static const QChar   SEPARATOR        = ';';
02523     static const QChar   LATE_CANCEL_CODE = 'C';
02524     static const QChar   AT_LOGIN_CODE    = 'L';   // subsidiary alarm at every login
02525     static const QChar   DEFERRAL_CODE    = 'D';   // extra deferred alarm
02526     static const QString TEXT_PREFIX      = QString::fromLatin1("TEXT:");
02527     static const QString FILE_PREFIX      = QString::fromLatin1("FILE:");
02528     static const QString COMMAND_PREFIX   = QString::fromLatin1("CMD:");
02529 
02530     // KAlarm pre-0.9.2 codes held in the event's CATEGORY property
02531     static const QString BEEP_CATEGORY    = QString::fromLatin1("BEEP");
02532 
02533     // KAlarm pre-1.1.1 LATECANCEL category with no parameter
02534     static const QString LATE_CANCEL_CAT = QString::fromLatin1("LATECANCEL");
02535 
02536     // KAlarm pre-1.3.0 TMPLDEFTIME category with no parameter
02537     static const QString TEMPL_DEF_TIME_CAT = QString::fromLatin1("TMPLDEFTIME");
02538 
02539     // KAlarm pre-1.3.1 XTERM category
02540     static const QString EXEC_IN_XTERM_CAT  = QString::fromLatin1("XTERM");
02541 
02542     if (version >= KAlarm::Version(1,3,1))
02543         return;
02544 
02545     kdDebug(5950) << "KAEvent::convertKCalEvents(): adjusting version " << version << endl;
02546     bool pre_0_7   = (version < KAlarm::Version(0,7,0));
02547     bool pre_0_9   = (version < KAlarm::Version(0,9,0));
02548     bool pre_0_9_2 = (version < KAlarm::Version(0,9,2));
02549     bool pre_1_1_1 = (version < KAlarm::Version(1,1,1));
02550     bool pre_1_2_1 = (version < KAlarm::Version(1,2,1));
02551     bool pre_1_3_0 = (version < KAlarm::Version(1,3,0));
02552     bool pre_1_3_1 = (version < KAlarm::Version(1,3,1));
02553     QDateTime dt0(QDate(1970,1,1), QTime(0,0,0));
02554     QTime startOfDay = Preferences::startOfDay();
02555 
02556     Event::List events = calendar.rawEvents();
02557     for (Event::List::ConstIterator evit = events.begin();  evit != events.end();  ++evit)
02558     {
02559         Event* event = *evit;
02560         Alarm::List alarms = event->alarms();
02561         if (alarms.isEmpty())
02562             continue;    // KAlarm isn't interested in events without alarms
02563         QStringList cats = event->categories();
02564         bool addLateCancel = false;
02565 
02566         if (pre_0_7  &&  event->doesFloat())
02567         {
02568             // It's a KAlarm pre-0.7 calendar file.
02569             // Ensure that when the calendar is saved, the alarm time isn't lost.
02570             event->setFloats(false);
02571         }
02572 
02573         if (pre_0_9)
02574         {
02575             /*
02576              * It's a KAlarm pre-0.9 calendar file.
02577              * All alarms were of type DISPLAY. Instead of the X-KDE-KALARM-TYPE
02578              * alarm property, characteristics were stored as a prefix to the
02579              * alarm DESCRIPTION property, as follows:
02580              *   SEQNO;[FLAGS];TYPE:TEXT
02581              * where
02582              *   SEQNO = sequence number of alarm within the event
02583              *   FLAGS = C for late-cancel, L for repeat-at-login, D for deferral
02584              *   TYPE = TEXT or FILE or CMD
02585              *   TEXT = message text, file name/URL or command
02586              */
02587             for (Alarm::List::ConstIterator alit = alarms.begin();  alit != alarms.end();  ++alit)
02588             {
02589                 Alarm* alarm = *alit;
02590                 bool atLogin    = false;
02591                 bool deferral   = false;
02592                 bool lateCancel = false;
02593                 KAAlarmEventBase::Type action = T_MESSAGE;
02594                 QString txt = alarm->text();
02595                 int length = txt.length();
02596                 int i = 0;
02597                 if (txt[0].isDigit())
02598                 {
02599                     while (++i < length  &&  txt[i].isDigit()) ;
02600                     if (i < length  &&  txt[i++] == SEPARATOR)
02601                     {
02602                         while (i < length)
02603                         {
02604                             QChar ch = txt[i++];
02605                             if (ch == SEPARATOR)
02606                                 break;
02607                             if (ch == LATE_CANCEL_CODE)
02608                                 lateCancel = true;
02609                             else if (ch == AT_LOGIN_CODE)
02610                                 atLogin = true;
02611                             else if (ch == DEFERRAL_CODE)
02612                                 deferral = true;
02613                         }
02614                     }
02615                     else
02616                         i = 0;     // invalid prefix
02617                 }
02618                 if (txt.find(TEXT_PREFIX, i) == i)
02619                     i += TEXT_PREFIX.length();
02620                 else if (txt.find(FILE_PREFIX, i) == i)
02621                 {
02622                     action = T_FILE;
02623                     i += FILE_PREFIX.length();
02624                 }
02625                 else if (txt.find(COMMAND_PREFIX, i) == i)
02626                 {
02627                     action = T_COMMAND;
02628                     i += COMMAND_PREFIX.length();
02629                 }
02630                 else
02631                     i = 0;
02632                 txt = txt.mid(i);
02633 
02634                 QStringList types;
02635                 switch (action)
02636                 {
02637                     case T_FILE:
02638                         types += FILE_TYPE;
02639                         // fall through to T_MESSAGE
02640                     case T_MESSAGE:
02641                         alarm->setDisplayAlarm(txt);
02642                         break;
02643                     case T_COMMAND:
02644                         setProcedureAlarm(alarm, txt);
02645                         break;
02646                     case T_EMAIL:     // email alarms were introduced in KAlarm 0.9
02647                     case T_AUDIO:     // never occurs in this context
02648                         break;
02649                 }
02650                 if (atLogin)
02651                 {
02652                     types += AT_LOGIN_TYPE;
02653                     lateCancel = false;
02654                 }
02655                 else if (deferral)
02656                     types += TIME_DEFERRAL_TYPE;
02657                 if (lateCancel)
02658                     addLateCancel = true;
02659                 if (types.count() > 0)
02660                     alarm->setCustomProperty(APPNAME, TYPE_PROPERTY, types.join(","));
02661 
02662                 if (pre_0_7  &&  alarm->repeatCount() > 0  &&  alarm->snoozeTime() > 0)
02663                 {
02664                     // It's a KAlarm pre-0.7 calendar file.
02665                     // Minutely recurrences were stored differently.
02666                     Recurrence* recur = event->recurrence();
02667                     if (recur  &&  recur->doesRecur())
02668                     {
02669                         recur->setMinutely(alarm->snoozeTime());
02670                         recur->setDuration(alarm->repeatCount() + 1);
02671                         alarm->setRepeatCount(0);
02672                         alarm->setSnoozeTime(0);
02673                     }
02674                 }
02675 
02676                 if (adjustSummerTime)
02677                 {
02678                     // The calendar file was written by the KDE 3.0.0 version of KAlarm 0.5.7.
02679                     // Summer time was ignored when converting to UTC.
02680                     QDateTime dt = alarm->time();
02681                     time_t t = dt0.secsTo(dt);
02682                     struct tm* dtm = localtime(&t);
02683                     if (dtm->tm_isdst)
02684                     {
02685                         dt = dt.addSecs(-3600);
02686                         alarm->setTime(dt);
02687                     }
02688                 }
02689             }
02690         }
02691 
02692         if (pre_0_9_2)
02693         {
02694             /*
02695              * It's a KAlarm pre-0.9.2 calendar file.
02696              * For the expired calendar, set the CREATED time to the DTEND value.
02697              * Convert date-only DTSTART to date/time, and add category "DATE".
02698              * Set the DTEND time to the DTSTART time.
02699              * Convert all alarm times to DTSTART offsets.
02700              * For display alarms, convert the first unlabelled category to an
02701              * X-KDE-KALARM-FONTCOLOUR property.
02702              * Convert BEEP category into an audio alarm with no audio file.
02703              */
02704             if (uidStatus(event->uid()) == EXPIRED)
02705                 event->setCreated(event->dtEnd());
02706             QDateTime start = event->dtStart();
02707             if (event->doesFloat())
02708             {
02709                 event->setFloats(false);
02710                 start.setTime(startOfDay);
02711                 cats.append(DATE_ONLY_CATEGORY);
02712             }
02713             event->setHasEndDate(false);
02714 
02715             Alarm::List::ConstIterator alit;
02716             for (alit = alarms.begin();  alit != alarms.end();  ++alit)
02717             {
02718                 Alarm* alarm = *alit;
02719                 QDateTime dt = alarm->time();
02720                 alarm->setStartOffset(start.secsTo(dt));
02721             }
02722 
02723             if (cats.count() > 0)
02724             {
02725                 for (alit = alarms.begin();  alit != alarms.end();  ++alit)
02726                 {
02727                     Alarm* alarm = *alit;
02728                     if (alarm->type() == Alarm::Display)
02729                         alarm->setCustomProperty(APPNAME, FONT_COLOUR_PROPERTY,
02730                                                  QString::fromLatin1("%1;;").arg(cats[0]));
02731                 }
02732                 cats.remove(cats.begin());
02733             }
02734 
02735             for (QStringList::Iterator it = cats.begin();  it != cats.end();  ++it)
02736             {
02737                 if (*it == BEEP_CATEGORY)
02738                 {
02739                     cats.remove(it);
02740 
02741                     Alarm* alarm = event->newAlarm();
02742                     alarm->setEnabled(true);
02743                     alarm->setAudioAlarm();
02744                     QDateTime dt = event->dtStart();    // default
02745 
02746                     // Parse and order the alarms to know which one's date/time to use
02747                     AlarmMap alarmMap;
02748                     readAlarms(*event, &alarmMap);
02749                     AlarmMap::ConstIterator it = alarmMap.begin();
02750                     if (it != alarmMap.end())
02751                     {
02752                         dt = it.data().alarm->time();
02753                         break;
02754                     }
02755                     alarm->setStartOffset(start.secsTo(dt));
02756                     break;
02757                 }
02758             }
02759 
02760         }
02761 
02762         if (pre_1_1_1)
02763         {
02764             /*
02765              * It's a KAlarm pre-1.1.1 calendar file.
02766              * Convert simple LATECANCEL category to LATECANCEL:n where n = minutes late.
02767              */
02768             QStringList::Iterator it;
02769             while ((it = cats.find(LATE_CANCEL_CAT)) != cats.end())
02770             {
02771                 cats.remove(it);
02772                 addLateCancel = true;
02773             }
02774         }
02775 
02776         if (pre_1_2_1)
02777         {
02778             /*
02779              * It's a KAlarm pre-1.2.1 calendar file.
02780              * Convert email display alarms from translated to untranslated header prefixes.
02781              */
02782             for (Alarm::List::ConstIterator alit = alarms.begin();  alit != alarms.end();  ++alit)
02783             {
02784                 Alarm* alarm = *alit;
02785                 if (alarm->type() == Alarm::Display)
02786                 {
02787                     QString oldtext = alarm->text();
02788                     QString newtext = AlarmText::toCalendarText(oldtext);
02789                     if (oldtext != newtext)
02790                         alarm->setDisplayAlarm(newtext);
02791                 }
02792             }
02793         }
02794 
02795         if (pre_1_3_0)
02796         {
02797             /*
02798              * It's a KAlarm pre-1.3.0 calendar file.
02799              * Convert simple TMPLDEFTIME category to TMPLAFTTIME:n where n = minutes after.
02800              */
02801             QStringList::Iterator it;
02802             while ((it = cats.find(TEMPL_DEF_TIME_CAT)) != cats.end())
02803             {
02804                 cats.remove(it);
02805                 cats.append(QString("%1%2").arg(TEMPL_AFTER_TIME_CATEGORY).arg(0));
02806             }
02807         }
02808 
02809         if (pre_1_3_1)
02810         {
02811             /*
02812              * It's a KAlarm pre-1.3.1 calendar file.
02813              * Convert simple XTERM category to LOG:xterm:
02814              */
02815             QStringList::Iterator it;
02816             while ((it = cats.find(EXEC_IN_XTERM_CAT)) != cats.end())
02817             {
02818                 cats.remove(it);
02819                 cats.append(LOG_CATEGORY + xtermURL);
02820             }
02821         }
02822 
02823         if (addLateCancel)
02824             cats.append(QString("%1%2").arg(LATE_CANCEL_CATEGORY).arg(1));
02825 
02826         event->setCategories(cats);
02827     }
02828 }
02829 
02830 #ifndef NDEBUG
02831 void KAEvent::dumpDebug() const
02832 {
02833     kdDebug(5950) << "KAEvent dump:\n";
02834     KAAlarmEventBase::dumpDebug();
02835     if (!mTemplateName.isEmpty())
02836     {
02837         kdDebug(5950) << "-- mTemplateName:" << mTemplateName << ":\n";
02838         kdDebug(5950) << "-- mTemplateAfterTime:" << mTemplateAfterTime << ":\n";
02839     }
02840     if (mActionType == T_MESSAGE  ||  mActionType == T_FILE)
02841     {
02842         kdDebug(5950) << "-- mAudioFile:" << mAudioFile << ":\n";
02843         kdDebug(5950) << "-- mPreAction:" << mPreAction << ":\n";
02844         kdDebug(5950) << "-- mPostAction:" << mPostAction << ":\n";
02845     }
02846     else if (mActionType == T_COMMAND)
02847     {
02848         kdDebug(5950) << "-- mCommandXterm:" << (mCommandXterm ? "true" : "false") << ":\n";
02849         kdDebug(5950) << "-- mLogFile:" << mLogFile << ":\n";
02850     }
02851     kdDebug(5950) << "-- mKMailSerialNumber:" << mKMailSerialNumber << ":\n";
02852     kdDebug(5950) << "-- mCopyToKOrganizer:" << (mCopyToKOrganizer ? "true" : "false") << ":\n";
02853     kdDebug(5950) << "-- mStartDateTime:" << mStartDateTime.toString() << ":\n";
02854     kdDebug(5950) << "-- mSaveDateTime:" << mSaveDateTime.toString() << ":\n";
02855     if (mRepeatAtLogin)
02856         kdDebug(5950) << "-- mAtLoginDateTime:" << mAtLoginDateTime.toString() << ":\n";
02857     kdDebug(5950) << "-- mArchiveRepeatAtLogin:" << (mArchiveRepeatAtLogin ? "true" : "false") << ":\n";
02858     kdDebug(5950) << "-- mEnabled:" << (mEnabled ? "true" : "false") << ":\n";
02859     if (mReminderMinutes)
02860         kdDebug(5950) << "-- mReminderMinutes:" << mReminderMinutes << ":\n";
02861     if (mArchiveReminderMinutes)
02862         kdDebug(5950) << "-- mArchiveReminderMinutes:" << mArchiveReminderMinutes << ":\n";
02863     if (mReminderMinutes  ||  mArchiveReminderMinutes)
02864         kdDebug(5950) << "-- mReminderOnceOnly:" << mReminderOnceOnly << ":\n";
02865     else if (mDeferral > 0)
02866     {
02867         kdDebug(5950) << "-- mDeferral:" << (mDeferral == NORMAL_DEFERRAL ? "normal" : "reminder") << ":\n";
02868         kdDebug(5950) << "-- mDeferralTime:" << mDeferralTime.toString() << ":\n";
02869     }
02870     else if (mDeferral == CANCEL_DEFERRAL)
02871         kdDebug(5950) << "-- mDeferral:cancel:\n";
02872     if (mDisplaying)
02873     {
02874         kdDebug(5950) << "-- mDisplayingTime:" << mDisplayingTime.toString() << ":\n";
02875         kdDebug(5950) << "-- mDisplayingFlags:" << mDisplayingFlags << ":\n";
02876     }
02877     kdDebug(5950) << "-- mRevision:" << mRevision << ":\n";
02878     kdDebug(5950) << "-- mRecurrence:" << (mRecurrence ? "true" : "false") << ":\n";
02879     if (mRecurrence)
02880         kdDebug(5950) << "-- mRemainingRecurrences:" << mRemainingRecurrences << ":\n";
02881     kdDebug(5950) << "-- mAlarmCount:" << mAlarmCount << ":\n";
02882     kdDebug(5950) << "-- mMainExpired:" << (mMainExpired ? "true" : "false") << ":\n";
02883     kdDebug(5950) << "KAEvent dump end\n";
02884 }
02885 #endif
02886 
02887 
02888 /*=============================================================================
02889 = Class KAAlarm
02890 = Corresponds to a single KCal::Alarm instance.
02891 =============================================================================*/
02892 
02893 KAAlarm::KAAlarm(const KAAlarm& alarm)
02894     : KAAlarmEventBase(alarm),
02895       mType(alarm.mType),
02896       mRecurs(alarm.mRecurs),
02897       mDeferred(alarm.mDeferred)
02898 { }
02899 
02900 
02901 int KAAlarm::flags() const
02902 {
02903     return KAAlarmEventBase::flags()
02904          | (mDeferred ? KAEvent::DEFERRAL : 0);
02905 
02906 }
02907 
02908 #ifndef NDEBUG
02909 void KAAlarm::dumpDebug() const
02910 {
02911     kdDebug(5950) << "KAAlarm dump:\n";
02912     KAAlarmEventBase::dumpDebug();
02913     const char* altype = 0;
02914     switch (mType)
02915     {
02916         case MAIN__ALARM:                    altype = "MAIN";  break;
02917         case REMINDER__ALARM:                altype = "REMINDER";  break;
02918         case DEFERRED_DATE__ALARM:           altype = "DEFERRED(DATE)";  break;
02919         case DEFERRED_TIME__ALARM:           altype = "DEFERRED(TIME)";  break;
02920         case DEFERRED_REMINDER_DATE__ALARM:  altype = "DEFERRED_REMINDER(DATE)";  break;
02921         case DEFERRED_REMINDER_TIME__ALARM:  altype = "DEFERRED_REMINDER(TIME)";  break;
02922         case AT_LOGIN__ALARM:                altype = "LOGIN";  break;
02923         case DISPLAYING__ALARM:              altype = "DISPLAYING";  break;
02924         case AUDIO__ALARM:                   altype = "AUDIO";  break;
02925         case PRE_ACTION__ALARM:              altype = "PRE_ACTION";  break;
02926         case POST_ACTION__ALARM:             altype = "POST_ACTION";  break;
02927         default:                             altype = "INVALID";  break;
02928     }
02929     kdDebug(5950) << "-- mType:" << altype << ":\n";
02930     kdDebug(5950) << "-- mRecurs:" << (mRecurs ? "true" : "false") << ":\n";
02931     kdDebug(5950) << "-- mDeferred:" << (mDeferred ? "true" : "false") << ":\n";
02932     kdDebug(5950) << "KAAlarm dump end\n";
02933 }
02934 
02935 const char* KAAlarm::debugType(Type type)
02936 {
02937     switch (type)
02938     {
02939         case MAIN_ALARM:               return "MAIN";
02940         case REMINDER_ALARM:           return "REMINDER";
02941         case DEFERRED_ALARM:           return "DEFERRED";
02942         case DEFERRED_REMINDER_ALARM:  return "DEFERRED_REMINDER";
02943         case AT_LOGIN_ALARM:           return "LOGIN";
02944         case DISPLAYING_ALARM:         return "DISPLAYING";
02945         case AUDIO_ALARM:              return "AUDIO";
02946         case PRE_ACTION_ALARM:         return "PRE_ACTION";
02947         case POST_ACTION_ALARM:        return "POST_ACTION";
02948         default:                       return "INVALID";
02949     }
02950 }
02951 #endif
02952 
02953 
02954 /*=============================================================================
02955 = Class KAAlarmEventBase
02956 =============================================================================*/
02957 
02958 void KAAlarmEventBase::copy(const KAAlarmEventBase& rhs)
02959 {
02960     mEventID          = rhs.mEventID;
02961     mText             = rhs.mText;
02962     mNextMainDateTime = rhs.mNextMainDateTime;
02963     mBgColour         = rhs.mBgColour;
02964     mFgColour         = rhs.mFgColour;
02965     mFont             = rhs.mFont;
02966     mEmailFromKMail   = rhs.mEmailFromKMail;
02967     mEmailAddresses   = rhs.mEmailAddresses;
02968     mEmailSubject     = rhs.mEmailSubject;
02969     mEmailAttachments = rhs.mEmailAttachments;
02970     mSoundVolume      = rhs.mSoundVolume;
02971     mFadeVolume       = rhs.mFadeVolume;
02972     mFadeSeconds      = rhs.mFadeSeconds;
02973     mActionType       = rhs.mActionType;
02974     mCommandScript    = rhs.mCommandScript;
02975     mRepeatCount      = rhs.mRepeatCount;
02976     mRepeatInterval   = rhs.mRepeatInterval;
02977     mBeep             = rhs.mBeep;
02978     mSpeak            = rhs.mSpeak;
02979     mRepeatSound      = rhs.mRepeatSound;
02980     mRepeatAtLogin    = rhs.mRepeatAtLogin;
02981     mDisplaying       = rhs.mDisplaying;
02982     mLateCancel       = rhs.mLateCancel;
02983     mAutoClose        = rhs.mAutoClose;
02984     mEmailBcc         = rhs.mEmailBcc;
02985     mConfirmAck       = rhs.mConfirmAck;
02986     mDefaultFont      = rhs.mDefaultFont;
02987 }
02988 
02989 void KAAlarmEventBase::set(int flags)
02990 {
02991     mSpeak         = flags & KAEvent::SPEAK;
02992     mBeep          = (flags & KAEvent::BEEP) && !mSpeak;
02993     mRepeatSound   = flags & KAEvent::REPEAT_SOUND;
02994     mRepeatAtLogin = flags & KAEvent::REPEAT_AT_LOGIN;
02995     mAutoClose     = (flags & KAEvent::AUTO_CLOSE) && mLateCancel;
02996     mEmailBcc      = flags & KAEvent::EMAIL_BCC;
02997     mConfirmAck    = flags & KAEvent::CONFIRM_ACK;
02998     mDisplaying    = flags & KAEvent::DISPLAYING_;
02999     mDefaultFont   = flags & KAEvent::DEFAULT_FONT;
03000     mCommandScript = flags & KAEvent::SCRIPT;
03001 }
03002 
03003 int KAAlarmEventBase::flags() const
03004 {
03005     return (mBeep && !mSpeak ? KAEvent::BEEP : 0)
03006          | (mSpeak           ? KAEvent::SPEAK : 0)
03007          | (mRepeatSound     ? KAEvent::REPEAT_SOUND : 0)
03008          | (mRepeatAtLogin   ? KAEvent::REPEAT_AT_LOGIN : 0)
03009          | (mAutoClose       ? KAEvent::AUTO_CLOSE : 0)
03010          | (mEmailBcc        ? KAEvent::EMAIL_BCC : 0)
03011          | (mConfirmAck      ? KAEvent::CONFIRM_ACK : 0)
03012          | (mDisplaying      ? KAEvent::DISPLAYING_ : 0)
03013          | (mDefaultFont     ? KAEvent::DEFAULT_FONT : 0)
03014          | (mCommandScript   ? KAEvent::SCRIPT : 0);
03015 }
03016 
03017 const QFont& KAAlarmEventBase::font() const
03018 {
03019     return mDefaultFont ? Preferences::messageFont() : mFont;
03020 }
03021 
03022 #ifndef NDEBUG
03023 void KAAlarmEventBase::dumpDebug() const
03024 {
03025     kdDebug(5950) << "-- mEventID:" << mEventID << ":\n";
03026     kdDebug(5950) << "-- mActionType:" << (mActionType == T_MESSAGE ? "MESSAGE" : mActionType == T_FILE ? "FILE" : mActionType == T_COMMAND ? "COMMAND" : mActionType == T_EMAIL ? "EMAIL" : mActionType == T_AUDIO ? "AUDIO" : "??") << ":\n";
03027     kdDebug(5950) << "-- mText:" << mText << ":\n";
03028     if (mActionType == T_COMMAND)
03029         kdDebug(5950) << "-- mCommandScript:" << (mCommandScript ? "true" : "false") << ":\n";
03030     kdDebug(5950) << "-- mNextMainDateTime:" << mNextMainDateTime.toString() << ":\n";
03031     if (mActionType == T_EMAIL)
03032     {
03033         kdDebug(5950) << "-- mEmail: FromKMail:" << mEmailFromKMail << ":\n";
03034         kdDebug(5950) << "--         Addresses:" << mEmailAddresses.join(", ") << ":\n";
03035         kdDebug(5950) << "--         Subject:" << mEmailSubject << ":\n";
03036         kdDebug(5950) << "--         Attachments:" << mEmailAttachments.join(", ") << ":\n";
03037         kdDebug(5950) << "--         Bcc:" << (mEmailBcc ? "true" : "false") << ":\n";
03038     }
03039     kdDebug(5950) << "-- mBgColour:" << mBgColour.name() << ":\n";
03040     kdDebug(5950) << "-- mFgColour:" << mFgColour.name() << ":\n";
03041     kdDebug(5950) << "-- mDefaultFont:" << (mDefaultFont ? "true" : "false") << ":\n";
03042     if (!mDefaultFont)
03043         kdDebug(5950) << "-- mFont:" << mFont.toString() << ":\n";
03044     kdDebug(5950) << "-- mBeep:" << (mBeep ? "true" : "false") << ":\n";
03045     kdDebug(5950) << "-- mSpeak:" << (mSpeak ? "true" : "false") << ":\n";
03046     if (mActionType == T_AUDIO)
03047     {
03048         if (mSoundVolume >= 0)
03049         {
03050             kdDebug(5950) << "-- mSoundVolume:" << mSoundVolume << ":\n";
03051             if (mFadeVolume >= 0)
03052             {
03053                 kdDebug(5950) << "-- mFadeVolume:" << mFadeVolume << ":\n";
03054                 kdDebug(5950) << "-- mFadeSeconds:" << mFadeSeconds << ":\n";
03055             }
03056             else
03057                 kdDebug(5950) << "-- mFadeVolume:-:\n";
03058         }
03059         else
03060             kdDebug(5950) << "-- mSoundVolume:-:\n";
03061         kdDebug(5950) << "-- mRepeatSound:" << (mRepeatSound ? "true" : "false") << ":\n";
03062     }
03063     kdDebug(5950) << "-- mConfirmAck:" << (mConfirmAck ? "true" : "false") << ":\n";
03064     kdDebug(5950) << "-- mRepeatAtLogin:" << (mRepeatAtLogin ? "true" : "false") << ":\n";
03065     kdDebug(5950) << "-- mRepeatCount:" << mRepeatCount << ":\n";
03066     kdDebug(5950) << "-- mRepeatInterval:" << mRepeatInterval << ":\n";
03067     kdDebug(5950) << "-- mDisplaying:" << (mDisplaying ? "true" : "false") << ":\n";
03068     kdDebug(5950) << "-- mLateCancel:" << mLateCancel << ":\n";
03069     kdDebug(5950) << "-- mAutoClose:" << (mAutoClose ? "true" : "false") << ":\n";
03070 }
03071 #endif
03072 
03073 
03074 /*=============================================================================
03075 = Class EmailAddressList
03076 =============================================================================*/
03077 
03078 /******************************************************************************
03079  * Sets the list of email addresses, removing any empty addresses.
03080  * Reply = false if empty addresses were found.
03081  */
03082 EmailAddressList& EmailAddressList::operator=(const QValueList<Person>& addresses)
03083 {
03084     clear();
03085     for (QValueList<Person>::ConstIterator it = addresses.begin();  it != addresses.end();  ++it)
03086     {
03087         if (!(*it).email().isEmpty())
03088             append(*it);
03089     }
03090     return *this;
03091 }
03092 
03093 /******************************************************************************
03094  * Return the email address list as a string, each address being delimited by
03095  * the specified separator string.
03096  */
03097 QString EmailAddressList::join(const QString& separator) const
03098 {
03099     QString result;
03100     bool first = true;
03101     for (QValueList<Person>::ConstIterator it = begin();  it != end();  ++it)
03102     {
03103         if (first)
03104             first = false;
03105         else
03106             result += separator;
03107 
03108         bool quote = false;
03109         QString name = (*it).name();
03110         if (!name.isEmpty())
03111         {
03112             // Need to enclose the name in quotes if it has any special characters
03113             int len = name.length();
03114             for (int i = 0;  i < len;  ++i)
03115             {
03116                 QChar ch = name[i];
03117                 if (!ch.isLetterOrNumber())
03118                 {
03119                     quote = true;
03120                     result += '\"';
03121                     break;
03122                 }
03123             }
03124             result += (*it).name();
03125             result += (quote ? "\" <" : " <");
03126             quote = true;    // need angle brackets round email address
03127         }
03128 
03129         result += (*it).email();
03130         if (quote)
03131             result += ">";
03132     }
03133     return result;
03134 }
03135 
03136 
03137 /*=============================================================================
03138 = Static functions
03139 =============================================================================*/
03140 
03141 /******************************************************************************
03142  * Set the specified alarm to be a procedure alarm with the given command line.
03143  * The command line is first split into its program file and arguments before
03144  * initialising the alarm.
03145  */
03146 static void setProcedureAlarm(Alarm* alarm, const QString& commandLine)
03147 {
03148     QString command   = QString::null;
03149     QString arguments = QString::null;
03150     QChar quoteChar;
03151     bool quoted = false;
03152     uint posMax = commandLine.length();
03153     uint pos;
03154     for (pos = 0;  pos < posMax;  ++pos)
03155     {
03156         QChar ch = commandLine[pos];
03157         if (quoted)
03158         {
03159             if (ch == quoteChar)
03160             {
03161                 ++pos;    // omit the quote character
03162                 break;
03163             }
03164             command += ch;
03165         }
03166         else
03167         {
03168             bool done = false;
03169             switch (ch)
03170             {
03171                 case ' ':
03172                 case ';':
03173                 case '|':
03174                 case '<':
03175                 case '>':
03176                     done = !command.isEmpty();
03177                     break;
03178                 case '\'':
03179                 case '"':
03180                     if (command.isEmpty())
03181                     {
03182                         // Start of a quoted string. Omit the quote character.
03183                         quoted = true;
03184                         quoteChar = ch;
03185                         break;
03186                     }
03187                     // fall through to default
03188                 default:
03189                     command += ch;
03190                     break;
03191             }
03192             if (done)
03193                 break;
03194         }
03195     }
03196 
03197     // Skip any spaces after the command
03198     for ( ;  pos < posMax  &&  commandLine[pos] == ' ';  ++pos) ;
03199     arguments = commandLine.mid(pos);
03200 
03201     alarm->setProcedureAlarm(command, arguments);
03202 }
KDE Home | KDE Accessibility Home | Description of Access Keys