kalarm

alarmevent.cpp

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