kalarm

alarmevent.cpp

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