kalarm

alarmevent.cpp

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