kalarm

alarmevent.cpp

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