kalarm

alarmevent.cpp

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