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