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, false);
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         // N.B. This is coded to avoid 32-bit integer overflow which occurs
00838         //      in QDateTime::secsTo() for large enough time differences.
00839         QDateTime now = QDateTime::currentDateTime();
00840         if (now > mNextMainDateTime)
00841         {
00842             dt = mainEndRepeatTime();    // get the last repetition time
00843             if (dt > now)
00844             {
00845                 int repeatSecs = mRepeatInterval * 60;
00846                 int repetition = (mNextMainDateTime.secsTo(now) + repeatSecs - 1) / repeatSecs;
00847                 dt = mNextMainDateTime.addSecs(repetition * repeatSecs);
00848             }
00849         }
00850     }
00851     if (mDeferral > 0
00852     &&  (includeReminders  ||  mDeferral != REMINDER_DEFERRAL))
00853     {
00854         if (mMainExpired)
00855             return mDeferralTime;
00856         return QMIN(mDeferralTime, dt);
00857     }
00858     return dt;
00859 }
00860 
00861 /******************************************************************************
00862  * Convert a unique ID to indicate that the event is in a specified calendar file.
00863  */
00864 QString KAEvent::uid(const QString& id, Status status)
00865 {
00866     QString result = id;
00867     Status oldStatus;
00868     int i, len;
00869     if ((i = result.find(EXPIRED_UID)) > 0)
00870     {
00871         oldStatus = EXPIRED;
00872         len = EXPIRED_UID.length();
00873     }
00874     else if ((i = result.find(DISPLAYING_UID)) > 0)
00875     {
00876         oldStatus = DISPLAYING;
00877         len = DISPLAYING_UID.length();
00878     }
00879     else if ((i = result.find(TEMPLATE_UID)) > 0)
00880     {
00881         oldStatus = TEMPLATE;
00882         len = TEMPLATE_UID.length();
00883     }
00884     else if ((i = result.find(KORGANIZER_UID)) > 0)
00885     {
00886         oldStatus = KORGANIZER;
00887         len = KORGANIZER_UID.length();
00888     }
00889     else
00890     {
00891         oldStatus = ACTIVE;
00892         i = result.findRev('-');
00893         len = 1;
00894     }
00895     if (status != oldStatus  &&  i > 0)
00896     {
00897         QString part;
00898         switch (status)
00899         {
00900             case ACTIVE:      part = "-";  break;
00901             case EXPIRED:     part = EXPIRED_UID;  break;
00902             case DISPLAYING:  part = DISPLAYING_UID;  break;
00903             case TEMPLATE:    part = TEMPLATE_UID;  break;
00904             case KORGANIZER:  part = KORGANIZER_UID;  break;
00905         }
00906         result.replace(i, len, part);
00907     }
00908     return result;
00909 }
00910 
00911 /******************************************************************************
00912  * Get the calendar type for a unique ID.
00913  */
00914 KAEvent::Status KAEvent::uidStatus(const QString& uid)
00915 {
00916     if (uid.find(EXPIRED_UID) > 0)
00917         return EXPIRED;
00918     if (uid.find(DISPLAYING_UID) > 0)
00919         return DISPLAYING;
00920     if (uid.find(TEMPLATE_UID) > 0)
00921         return TEMPLATE;
00922     if (uid.find(KORGANIZER_UID) > 0)
00923         return KORGANIZER;
00924     return ACTIVE;
00925 }
00926 
00927 void KAEvent::set(int flags)
00928 {
00929     KAAlarmEventBase::set(flags & ~READ_ONLY_FLAGS);
00930     mStartDateTime.setDateOnly(flags & ANY_TIME);
00931     set_deferral((flags & DEFERRAL) ? NORMAL_DEFERRAL : NO_DEFERRAL);
00932     mCommandXterm     = flags & EXEC_IN_XTERM;
00933     mCopyToKOrganizer = flags & COPY_KORGANIZER;
00934     mEnabled          = !(flags & DISABLED);
00935     mUpdated          = true;
00936 }
00937 
00938 int KAEvent::flags() const
00939 {
00940     return KAAlarmEventBase::flags()
00941          | (mStartDateTime.isDateOnly() ? ANY_TIME : 0)
00942          | (mDeferral > 0               ? DEFERRAL : 0)
00943          | (mCommandXterm               ? EXEC_IN_XTERM : 0)
00944          | (mCopyToKOrganizer           ? COPY_KORGANIZER : 0)
00945          | (mEnabled                    ? 0 : DISABLED);
00946 }
00947 
00948 /******************************************************************************
00949  * Create a new Event from the KAEvent data.
00950  */
00951 Event* KAEvent::event() const
00952 {
00953     KCal::Event* ev = new KCal::Event;
00954     ev->setUid(mEventID);
00955     updateKCalEvent(*ev, false);
00956     return ev;
00957 }
00958 
00959 /******************************************************************************
00960  * Update an existing KCal::Event with the KAEvent data.
00961  * If 'original' is true, the event start date/time is adjusted to its original
00962  * value instead of its next occurrence, and the expired main alarm is
00963  * reinstated.
00964  */
00965 bool KAEvent::updateKCalEvent(Event& ev, bool checkUid, bool original, bool cancelCancelledDefer) const
00966 {
00967     if (checkUid  &&  !mEventID.isEmpty()  &&  mEventID != ev.uid()
00968     ||  !mAlarmCount  &&  (!original || !mMainExpired))
00969         return false;
00970 
00971     checkRecur();     // ensure recurrence/repetition data is consistent
00972     bool readOnly = ev.isReadOnly();
00973     ev.setReadOnly(false);
00974     ev.setTransparency(Event::Transparent);
00975 
00976     // Set up event-specific data
00977     QStringList cats;
00978     if (mStartDateTime.isDateOnly())
00979         cats.append(DATE_ONLY_CATEGORY);
00980     if (mConfirmAck)
00981         cats.append(CONFIRM_ACK_CATEGORY);
00982     if (mEmailBcc)
00983         cats.append(EMAIL_BCC_CATEGORY);
00984     if (mKMailSerialNumber)
00985         cats.append(QString("%1%2").arg(KMAIL_SERNUM_CATEGORY).arg(mKMailSerialNumber));
00986     if (mCopyToKOrganizer)
00987         cats.append(KORGANIZER_CATEGORY);
00988     if (mCommandXterm)
00989         cats.append(LOG_CATEGORY + xtermURL);
00990     else if (!mLogFile.isEmpty())
00991         cats.append(LOG_CATEGORY + mLogFile);
00992     if (mLateCancel)
00993         cats.append(QString("%1%2").arg(mAutoClose ? AUTO_CLOSE_CATEGORY : LATE_CANCEL_CATEGORY).arg(mLateCancel));
00994     if (mDeferDefaultMinutes)
00995         cats.append(QString("%1%2").arg(DEFER_CATEGORY).arg(mDeferDefaultMinutes));
00996     if (!mTemplateName.isEmpty()  &&  mTemplateAfterTime >= 0)
00997         cats.append(QString("%1%2").arg(TEMPL_AFTER_TIME_CATEGORY).arg(mTemplateAfterTime));
00998     if (mArchive  &&  !original)
00999     {
01000         QStringList params;
01001         if (mArchiveReminderMinutes)
01002         {
01003             if (mReminderOnceOnly)
01004                 params += ARCHIVE_REMINDER_ONCE_TYPE;
01005             char unit = 'M';
01006             int count = mArchiveReminderMinutes;
01007             if (count % 1440 == 0)
01008             {
01009                 unit = 'D';
01010                 count /= 1440;
01011             }
01012             else if (count % 60 == 0)
01013             {
01014                 unit = 'H';
01015                 count /= 60;
01016             }
01017             params += QString("%1%2").arg(count).arg(unit);
01018         }
01019         if (mArchiveRepeatAtLogin)
01020             params += AT_LOGIN_TYPE;
01021         if (params.count() > 0)
01022         {
01023             QString cat = ARCHIVE_CATEGORIES;
01024             cat += params.join(QString::fromLatin1(";"));
01025             cats.append(cat);
01026         }
01027         else
01028             cats.append(ARCHIVE_CATEGORY);
01029     }
01030     ev.setCategories(cats);
01031     ev.setCustomStatus(mEnabled ? QString::null : DISABLED_STATUS);
01032     ev.setRevision(mRevision);
01033     ev.clearAlarms();
01034 
01035     // Always set DTSTART as date/time, since alarm times can only be specified
01036     // in local time (instead of UTC) if they are relative to a DTSTART or DTEND
01037     // which is also specified in local time. Instead of calling setFloats() to
01038     // indicate a date-only event, the category "DATE" is included.
01039     ev.setDtStart(mStartDateTime.dateTime());
01040     ev.setFloats(false);
01041     ev.setHasEndDate(false);
01042 
01043     DateTime dtMain = original ? mStartDateTime : mNextMainDateTime;
01044     int      ancillaryType = 0;   // 0 = invalid, 1 = time, 2 = offset
01045     DateTime ancillaryTime;       // time for ancillary alarms (audio, pre-action, etc)
01046     int      ancillaryOffset = 0; // start offset for ancillary alarms
01047     if (!mMainExpired  ||  original)
01048     {
01049         // Add the main alarm
01050         initKcalAlarm(ev, dtMain, QStringList(), KAAlarm::MAIN_ALARM);
01051         ancillaryTime = dtMain;
01052         ancillaryType = dtMain.isValid() ? 1 : 0;
01053     }
01054 
01055     // Add subsidiary alarms
01056     if (mRepeatAtLogin  ||  mArchiveRepeatAtLogin && original)
01057     {
01058         DateTime dtl;
01059         if (mArchiveRepeatAtLogin)
01060             dtl = mStartDateTime.dateTime().addDays(-1);
01061         else if (mAtLoginDateTime.isValid())
01062             dtl = mAtLoginDateTime;
01063         else if (mStartDateTime.isDateOnly())
01064             dtl = QDate::currentDate().addDays(-1);
01065         else
01066             dtl = QDateTime::currentDateTime();
01067         initKcalAlarm(ev, dtl, AT_LOGIN_TYPE);
01068         if (!ancillaryType  &&  dtl.isValid())
01069         {
01070             ancillaryTime = dtl;
01071             ancillaryType = 1;
01072         }
01073     }
01074     if (mReminderMinutes  ||  mArchiveReminderMinutes && original)
01075     {
01076         int minutes = mReminderMinutes ? mReminderMinutes : mArchiveReminderMinutes;
01077         initKcalAlarm(ev, -minutes * 60, QStringList(mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE));
01078         if (!ancillaryType)
01079         {
01080             ancillaryOffset = -minutes * 60;
01081             ancillaryType = 2;
01082         }
01083     }
01084     if (mDeferral > 0  ||  mDeferral == CANCEL_DEFERRAL && !cancelCancelledDefer)
01085     {
01086         QStringList list;
01087         if (mDeferralTime.isDateOnly())
01088             list += DATE_DEFERRAL_TYPE;
01089         else
01090             list += TIME_DEFERRAL_TYPE;
01091         if (mDeferral == REMINDER_DEFERRAL)
01092             list += mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE;
01093         initKcalAlarm(ev, mDeferralTime, list);
01094         if (!ancillaryType  &&  mDeferralTime.isValid())
01095         {
01096             ancillaryTime = mDeferralTime;
01097             ancillaryType = 1;
01098         }
01099     }
01100     if (!mTemplateName.isEmpty())
01101         ev.setSummary(mTemplateName);
01102     else if (mDisplaying)
01103     {
01104         QStringList list(DISPLAYING_TYPE);
01105         if (mDisplayingFlags & REPEAT_AT_LOGIN)
01106             list += AT_LOGIN_TYPE;
01107         else if (mDisplayingFlags & DEFERRAL)
01108         {
01109             if (mDisplayingFlags & TIMED_FLAG)
01110                 list += TIME_DEFERRAL_TYPE;
01111             else
01112                 list += DATE_DEFERRAL_TYPE;
01113         }
01114         if (mDisplayingFlags & REMINDER)
01115             list += mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE;
01116         initKcalAlarm(ev, mDisplayingTime, list);
01117         if (!ancillaryType  &&  mDisplayingTime.isValid())
01118         {
01119             ancillaryTime = mDisplayingTime;
01120             ancillaryType = 1;
01121         }
01122     }
01123     if (mBeep  ||  mSpeak  ||  !mAudioFile.isEmpty())
01124     {
01125         // A sound is specified
01126         if (ancillaryType == 2)
01127             initKcalAlarm(ev, ancillaryOffset, QStringList(), KAAlarm::AUDIO_ALARM);
01128         else
01129             initKcalAlarm(ev, ancillaryTime, QStringList(), KAAlarm::AUDIO_ALARM);
01130     }
01131     if (!mPreAction.isEmpty())
01132     {
01133         // A pre-display action is specified
01134         if (ancillaryType == 2)
01135             initKcalAlarm(ev, ancillaryOffset, QStringList(PRE_ACTION_TYPE), KAAlarm::PRE_ACTION_ALARM);
01136         else
01137             initKcalAlarm(ev, ancillaryTime, QStringList(PRE_ACTION_TYPE), KAAlarm::PRE_ACTION_ALARM);
01138     }
01139     if (!mPostAction.isEmpty())
01140     {
01141         // A post-display action is specified
01142         if (ancillaryType == 2)
01143             initKcalAlarm(ev, ancillaryOffset, QStringList(POST_ACTION_TYPE), KAAlarm::POST_ACTION_ALARM);
01144         else
01145             initKcalAlarm(ev, ancillaryTime, QStringList(POST_ACTION_TYPE), KAAlarm::POST_ACTION_ALARM);
01146     }
01147 
01148     if (mRecurrence)
01149         mRecurrence->writeRecurrence(*ev.recurrence());
01150     else
01151         ev.clearRecurrence();
01152     if (mSaveDateTime.isValid())
01153         ev.setCreated(mSaveDateTime);
01154     ev.setReadOnly(readOnly);
01155     return true;
01156 }
01157 
01158 /******************************************************************************
01159  * Create a new alarm for a libkcal event, and initialise it according to the
01160  * alarm action. If 'types' is non-null, it is appended to the X-KDE-KALARM-TYPE
01161  * property value list.
01162  */
01163 Alarm* KAEvent::initKcalAlarm(Event& event, const DateTime& dt, const QStringList& types, KAAlarm::Type type) const
01164 {
01165     int startOffset = dt.isDateOnly() ? mStartDateTime.secsTo(dt)
01166                                       : mStartDateTime.dateTime().secsTo(dt.dateTime());
01167     return initKcalAlarm(event, startOffset, types, type);
01168 }
01169 
01170 Alarm* KAEvent::initKcalAlarm(Event& event, int startOffsetSecs, const QStringList& types, KAAlarm::Type type) const
01171 {
01172     QStringList alltypes;
01173     Alarm* alarm = event.newAlarm();
01174     alarm->setEnabled(true);
01175     // RFC2445 specifies that absolute alarm times must be stored as UTC.
01176     // So, in order to store local times, set the alarm time as an offset to DTSTART.
01177     alarm->setStartOffset(startOffsetSecs);
01178 
01179     switch (type)
01180     {
01181         case KAAlarm::AUDIO_ALARM:
01182             alarm->setAudioAlarm(mAudioFile);  // empty for a beep or for speaking
01183             if (mSpeak)
01184                 alarm->setCustomProperty(APPNAME, SPEAK_PROPERTY, QString::fromLatin1("Y"));
01185             if (mRepeatSound)
01186             {
01187                 alarm->setRepeatCount(-1);
01188                 alarm->setSnoozeTime(0);
01189             }
01190             if (!mAudioFile.isEmpty()  &&  mSoundVolume >= 0)
01191                 alarm->setCustomProperty(APPNAME, VOLUME_PROPERTY,
01192                               QString::fromLatin1("%1;%2;%3").arg(QString::number(mSoundVolume, 'f', 2))
01193                                                              .arg(QString::number(mFadeVolume, 'f', 2))
01194                                                              .arg(mFadeSeconds));
01195             break;
01196         case KAAlarm::PRE_ACTION_ALARM:
01197             setProcedureAlarm(alarm, mPreAction);
01198             break;
01199         case KAAlarm::POST_ACTION_ALARM:
01200             setProcedureAlarm(alarm, mPostAction);
01201             break;
01202         case KAAlarm::MAIN_ALARM:
01203             alarm->setSnoozeTime(mRepeatInterval);
01204             alarm->setRepeatCount(mRepeatCount);
01205             // fall through to INVALID_ALARM
01206         case KAAlarm::INVALID_ALARM:
01207             switch (mActionType)
01208             {
01209                 case T_FILE:
01210                     alltypes += FILE_TYPE;
01211                     // fall through to T_MESSAGE
01212                 case T_MESSAGE:
01213                     alarm->setDisplayAlarm(AlarmText::toCalendarText(mText));
01214                     alarm->setCustomProperty(APPNAME, FONT_COLOUR_PROPERTY,
01215                               QString::fromLatin1("%1;%2;%3").arg(mBgColour.name())
01216                                              .arg(mFgColour.name())
01217                                              .arg(mDefaultFont ? QString::null : mFont.toString()));
01218                     break;
01219                 case T_COMMAND:
01220                     if (mCommandScript)
01221                         alarm->setProcedureAlarm("", mText);
01222                     else
01223                         setProcedureAlarm(alarm, mText);
01224                     break;
01225                 case T_EMAIL:
01226                     alarm->setEmailAlarm(mEmailSubject, mText, mEmailAddresses, mEmailAttachments);
01227                     if (!mEmailFromKMail.isEmpty())
01228                         alarm->setCustomProperty(APPNAME, KMAIL_ID_PROPERTY, mEmailFromKMail);
01229                     break;
01230                 case T_AUDIO:
01231                     break;
01232             }
01233             break;
01234         case KAAlarm::REMINDER_ALARM:
01235         case KAAlarm::DEFERRED_ALARM:
01236         case KAAlarm::DEFERRED_REMINDER_ALARM:
01237         case KAAlarm::AT_LOGIN_ALARM:
01238         case KAAlarm::DISPLAYING_ALARM:
01239             break;
01240     }
01241     alltypes += types;
01242     if (alltypes.count() > 0)
01243         alarm->setCustomProperty(APPNAME, TYPE_PROPERTY, alltypes.join(","));
01244     return alarm;
01245 }
01246 
01247 /******************************************************************************
01248  * Return the alarm of the specified type.
01249  */
01250 KAAlarm KAEvent::alarm(KAAlarm::Type type) const
01251 {
01252     checkRecur();     // ensure recurrence/repetition data is consistent
01253     KAAlarm al;       // this sets type to INVALID_ALARM
01254     if (mAlarmCount)
01255     {
01256         al.mEventID       = mEventID;
01257         al.mActionType    = mActionType;
01258         al.mText          = mText;
01259         al.mBgColour      = mBgColour;
01260         al.mFgColour      = mFgColour;
01261         al.mFont          = mFont;
01262         al.mDefaultFont   = mDefaultFont;
01263         al.mBeep          = mBeep;
01264         al.mSpeak         = mSpeak;
01265         al.mSoundVolume   = mSoundVolume;
01266         al.mFadeVolume    = mFadeVolume;
01267         al.mFadeSeconds   = mFadeSeconds;
01268         al.mRepeatSound   = mRepeatSound;
01269         al.mConfirmAck    = mConfirmAck;
01270         al.mRepeatAtLogin = false;
01271         al.mDeferred      = false;
01272         al.mLateCancel    = mLateCancel;
01273         al.mAutoClose     = mAutoClose;
01274         al.mEmailBcc      = mEmailBcc;
01275         al.mCommandScript = mCommandScript;
01276         if (mActionType == T_EMAIL)
01277         {
01278             al.mEmailFromKMail   = mEmailFromKMail;
01279             al.mEmailAddresses   = mEmailAddresses;
01280             al.mEmailSubject     = mEmailSubject;
01281             al.mEmailAttachments = mEmailAttachments;
01282         }
01283         switch (type)
01284         {
01285             case KAAlarm::MAIN_ALARM:
01286                 if (!mMainExpired)
01287                 {
01288                     al.mType             = KAAlarm::MAIN__ALARM;
01289                     al.mNextMainDateTime = mNextMainDateTime;
01290                     al.mRepeatCount      = mRepeatCount;
01291                     al.mRepeatInterval   = mRepeatInterval;
01292                 }
01293                 break;
01294             case KAAlarm::REMINDER_ALARM:
01295                 if (mReminderMinutes)
01296                 {
01297                     al.mType = KAAlarm::REMINDER__ALARM;
01298                     if (mReminderOnceOnly)
01299                         al.mNextMainDateTime = mStartDateTime.addMins(-mReminderMinutes);
01300                     else
01301                         al.mNextMainDateTime = mNextMainDateTime.addMins(-mReminderMinutes);
01302                 }
01303                 break;
01304             case KAAlarm::DEFERRED_REMINDER_ALARM:
01305                 if (mDeferral != REMINDER_DEFERRAL)
01306                     break;
01307                 // fall through to DEFERRED_ALARM
01308             case KAAlarm::DEFERRED_ALARM:
01309                 if (mDeferral > 0)
01310                 {
01311                     al.mType = static_cast<KAAlarm::SubType>((mDeferral == REMINDER_DEFERRAL ? KAAlarm::DEFERRED_REMINDER_ALARM : KAAlarm::DEFERRED_ALARM)
01312                                                              | (mDeferralTime.isDateOnly() ? 0 : KAAlarm::TIMED_DEFERRAL_FLAG));
01313                     al.mNextMainDateTime = mDeferralTime;
01314                     al.mDeferred         = true;
01315                 }
01316                 break;
01317             case KAAlarm::AT_LOGIN_ALARM:
01318                 if (mRepeatAtLogin)
01319                 {
01320                     al.mType             = KAAlarm::AT_LOGIN__ALARM;
01321                     al.mNextMainDateTime = mAtLoginDateTime;
01322                     al.mRepeatAtLogin    = true;
01323                     al.mLateCancel       = 0;
01324                     al.mAutoClose        = false;
01325                 }
01326                 break;
01327             case KAAlarm::DISPLAYING_ALARM:
01328                 if (mDisplaying)
01329                 {
01330                     al.mType             = KAAlarm::DISPLAYING__ALARM;
01331                     al.mNextMainDateTime = mDisplayingTime;
01332                     al.mDisplaying       = true;
01333                 }
01334                 break;
01335             case KAAlarm::AUDIO_ALARM:
01336             case KAAlarm::PRE_ACTION_ALARM:
01337             case KAAlarm::POST_ACTION_ALARM:
01338             case KAAlarm::INVALID_ALARM:
01339             default:
01340                 break;
01341         }
01342     }
01343     return al;
01344 }
01345 
01346 /******************************************************************************
01347  * Return the main alarm for the event.
01348  * If the main alarm does not exist, one of the subsidiary ones is returned if
01349  * possible.
01350  * N.B. a repeat-at-login alarm can only be returned if it has been read from/
01351  * written to the calendar file.
01352  */
01353 KAAlarm KAEvent::firstAlarm() const
01354 {
01355     if (mAlarmCount)
01356     {
01357         if (!mMainExpired)
01358             return alarm(KAAlarm::MAIN_ALARM);
01359         return nextAlarm(KAAlarm::MAIN_ALARM);
01360     }
01361     return KAAlarm();
01362 }
01363 
01364 /******************************************************************************
01365  * Return the next alarm for the event, after the specified alarm.
01366  * N.B. a repeat-at-login alarm can only be returned if it has been read from/
01367  * written to the calendar file.
01368  */
01369 KAAlarm KAEvent::nextAlarm(KAAlarm::Type prevType) const
01370 {
01371     switch (prevType)
01372     {
01373         case KAAlarm::MAIN_ALARM:
01374             if (mReminderMinutes)
01375                 return alarm(KAAlarm::REMINDER_ALARM);
01376             // fall through to REMINDER_ALARM
01377         case KAAlarm::REMINDER_ALARM:
01378             // There can only be one deferral alarm
01379             if (mDeferral == REMINDER_DEFERRAL)
01380                 return alarm(KAAlarm::DEFERRED_REMINDER_ALARM);
01381             if (mDeferral == NORMAL_DEFERRAL)
01382                 return alarm(KAAlarm::DEFERRED_ALARM);
01383             // fall through to DEFERRED_ALARM
01384         case KAAlarm::DEFERRED_REMINDER_ALARM:
01385         case KAAlarm::DEFERRED_ALARM:
01386             if (mRepeatAtLogin)
01387                 return alarm(KAAlarm::AT_LOGIN_ALARM);
01388             // fall through to AT_LOGIN_ALARM
01389         case KAAlarm::AT_LOGIN_ALARM:
01390             if (mDisplaying)
01391                 return alarm(KAAlarm::DISPLAYING_ALARM);
01392             // fall through to DISPLAYING_ALARM
01393         case KAAlarm::DISPLAYING_ALARM:
01394             // fall through to default
01395         case KAAlarm::AUDIO_ALARM:
01396         case KAAlarm::PRE_ACTION_ALARM:
01397         case KAAlarm::POST_ACTION_ALARM:
01398         case KAAlarm::INVALID_ALARM:
01399         default:
01400             break;
01401     }
01402     return KAAlarm();
01403 }
01404 
01405 /******************************************************************************
01406  * Remove the alarm of the specified type from the event.
01407  * This must only be called to remove an alarm which has expired, not to
01408  * reconfigure the event.
01409  */
01410 void KAEvent::removeExpiredAlarm(KAAlarm::Type type)
01411 {
01412     int count = mAlarmCount;
01413     switch (type)
01414     {
01415         case KAAlarm::MAIN_ALARM:
01416             mAlarmCount = 0;    // removing main alarm - also remove subsidiary alarms
01417             break;
01418         case KAAlarm::AT_LOGIN_ALARM:
01419             if (mRepeatAtLogin)
01420             {
01421                 // Remove the at-login alarm, but keep a note of it for archiving purposes
01422                 mArchiveRepeatAtLogin = true;
01423                 mRepeatAtLogin = false;
01424                 --mAlarmCount;
01425             }
01426             break;
01427         case KAAlarm::REMINDER_ALARM:
01428             // Remove any reminder alarm, but keep a note of it for archiving purposes
01429             set_archiveReminder();
01430             break;
01431         case KAAlarm::DEFERRED_REMINDER_ALARM:
01432         case KAAlarm::DEFERRED_ALARM:
01433             set_deferral(NO_DEFERRAL);
01434             break;
01435         case KAAlarm::DISPLAYING_ALARM:
01436             if (mDisplaying)
01437             {
01438                 mDisplaying = false;
01439                 --mAlarmCount;
01440             }
01441             break;
01442         case KAAlarm::AUDIO_ALARM:
01443         case KAAlarm::PRE_ACTION_ALARM:
01444         case KAAlarm::POST_ACTION_ALARM:
01445         case KAAlarm::INVALID_ALARM:
01446         default:
01447             break;
01448     }
01449     if (mAlarmCount != count)
01450         mUpdated = true;
01451 }
01452 
01453 /******************************************************************************
01454  * Defer the event to the specified time.
01455  * If the main alarm time has passed, the main alarm is marked as expired.
01456  * If 'adjustRecurrence' is true, ensure that the next scheduled recurrence is
01457  * after the current time.
01458  * Reply = true if a repetition has been deferred.
01459  */
01460 bool KAEvent::defer(const DateTime& dateTime, bool reminder, bool adjustRecurrence)
01461 {
01462     bool result = false;
01463     cancelCancelledDeferral();
01464     if (checkRecur() == KARecurrence::NO_RECUR)
01465     {
01466         if (mReminderMinutes  ||  mDeferral == REMINDER_DEFERRAL  ||  mArchiveReminderMinutes)
01467         {
01468             if (dateTime < mNextMainDateTime.dateTime())
01469             {
01470                 set_deferral(REMINDER_DEFERRAL);   // defer reminder alarm
01471                 mDeferralTime = dateTime;
01472             }
01473             else
01474             {
01475                 // Deferring past the main alarm time, so adjust any existing deferral
01476                 if (mReminderMinutes  ||  mDeferral == REMINDER_DEFERRAL)
01477                     set_deferral(NO_DEFERRAL);
01478             }
01479             // Remove any reminder alarm, but keep a note of it for archiving purposes
01480             set_archiveReminder();
01481         }
01482         if (mDeferral != REMINDER_DEFERRAL)
01483         {
01484             // We're deferring the main alarm, not a reminder
01485             if (mRepeatCount  &&  dateTime < mainEndRepeatTime())
01486             {
01487                 // The alarm is repeated, and we're deferring to a time before the last repetition
01488                 set_deferral(NORMAL_DEFERRAL);
01489                 mDeferralTime = dateTime;
01490                 result = true;
01491             }
01492             else
01493             {
01494                 // Main alarm has now expired
01495                 mNextMainDateTime = mDeferralTime = dateTime;
01496                 set_deferral(NORMAL_DEFERRAL);
01497                 if (!mMainExpired)
01498                 {
01499                     // Mark the alarm as expired now
01500                     mMainExpired = true;
01501                     --mAlarmCount;
01502                     if (mRepeatAtLogin)
01503                     {
01504                         // Remove the repeat-at-login alarm, but keep a note of it for archiving purposes
01505                         mArchiveRepeatAtLogin = true;
01506                         mRepeatAtLogin = false;
01507                         --mAlarmCount;
01508                     }
01509                 }
01510             }
01511         }
01512     }
01513     else if (reminder)
01514     {
01515         // Deferring a reminder for a recurring alarm
01516         if (dateTime >= mNextMainDateTime.dateTime())
01517             set_deferral(NO_DEFERRAL);    // (error)
01518         else
01519         {
01520             set_deferral(REMINDER_DEFERRAL);
01521             mDeferralTime = dateTime;
01522         }
01523     }
01524     else
01525     {
01526         mDeferralTime = dateTime;
01527         if (mDeferral <= 0)
01528             set_deferral(NORMAL_DEFERRAL);
01529         if (adjustRecurrence)
01530         {
01531             QDateTime now = QDateTime::currentDateTime();
01532             if (mainEndRepeatTime() < now)
01533             {
01534                 // The last repetition (if any) of the current recurrence has already passed.
01535                 // Adjust to the next scheduled recurrence after now.
01536                 if (!mMainExpired  &&  setNextOccurrence(now, true) == NO_OCCURRENCE)
01537                 {
01538                     mMainExpired = true;
01539                     --mAlarmCount;
01540                 }
01541             }
01542         }
01543     }
01544     mUpdated = true;
01545     return result;
01546 }
01547 
01548 /******************************************************************************
01549  * Cancel any deferral alarm.
01550  */
01551 void KAEvent::cancelDefer()
01552 {
01553     if (mDeferral > 0)
01554     {
01555         // Set the deferral time to be the same as the next recurrence/repetition.
01556         // This prevents an immediate retriggering of the alarm.
01557         if (mMainExpired
01558         ||  nextOccurrence(QDateTime::currentDateTime(), mDeferralTime, RETURN_REPETITION) == NO_OCCURRENCE)
01559         {
01560             // The main alarm has expired, so simply delete the deferral
01561             mDeferralTime = DateTime();
01562             set_deferral(NO_DEFERRAL);
01563         }
01564         else
01565             set_deferral(CANCEL_DEFERRAL);
01566         mUpdated = true;
01567     }
01568 }
01569 
01570 /******************************************************************************
01571  * Cancel any cancelled deferral alarm.
01572  */
01573 void KAEvent::cancelCancelledDeferral()
01574 {
01575     if (mDeferral == CANCEL_DEFERRAL)
01576     {
01577         mDeferralTime = DateTime();
01578         set_deferral(NO_DEFERRAL);
01579     }
01580 }
01581 
01582 /******************************************************************************
01583 *  Find the latest time which the alarm can currently be deferred to.
01584 */
01585 DateTime KAEvent::deferralLimit(KAEvent::DeferLimitType* limitType) const
01586 {
01587     DeferLimitType ltype;
01588     DateTime endTime;
01589     bool recurs = (checkRecur() != KARecurrence::NO_RECUR);
01590     if (recurs  ||  mRepeatCount)
01591     {
01592         // It's a repeated alarm. Don't allow it to be deferred past its
01593         // next occurrence or repetition.
01594         DateTime reminderTime;
01595         QDateTime now = QDateTime::currentDateTime();
01596         OccurType type = nextOccurrence(now, endTime, RETURN_REPETITION);
01597         if (type & OCCURRENCE_REPEAT)
01598             ltype = LIMIT_REPETITION;
01599         else if (type == NO_OCCURRENCE)
01600             ltype = LIMIT_NONE;
01601         else if (mReminderMinutes  &&  (now < (reminderTime = endTime.addMins(-mReminderMinutes))))
01602         {
01603             endTime = reminderTime;
01604             ltype = LIMIT_REMINDER;
01605         }
01606         else if (type == FIRST_OR_ONLY_OCCURRENCE  &&  !recurs)
01607             ltype = LIMIT_REPETITION;
01608         else
01609             ltype = LIMIT_RECURRENCE;
01610     }
01611     else if ((mReminderMinutes  ||  mDeferral == REMINDER_DEFERRAL  ||  mArchiveReminderMinutes)
01612          &&  QDateTime::currentDateTime() < mNextMainDateTime.dateTime())
01613     {
01614         // It's an reminder alarm. Don't allow it to be deferred past its main alarm time.
01615         endTime = mNextMainDateTime;
01616         ltype = LIMIT_REMINDER;
01617     }
01618     else
01619         ltype = LIMIT_NONE;
01620     if (ltype != LIMIT_NONE)
01621         endTime = endTime.addMins(-1);
01622     if (limitType)
01623         *limitType = ltype;
01624     return endTime;
01625 }
01626 
01627 /******************************************************************************
01628  * Set the event to be a copy of the specified event, making the specified
01629  * alarm the 'displaying' alarm.
01630  * The purpose of setting up a 'displaying' alarm is to be able to reinstate
01631  * the alarm message in case of a crash, or to reinstate it should the user
01632  * choose to defer the alarm. Note that even repeat-at-login alarms need to be
01633  * saved in case their end time expires before the next login.
01634  * Reply = true if successful, false if alarm was not copied.
01635  */
01636 bool KAEvent::setDisplaying(const KAEvent& event, KAAlarm::Type alarmType, const QDateTime& repeatAtLoginTime)
01637 {
01638     if (!mDisplaying
01639     &&  (alarmType == KAAlarm::MAIN_ALARM
01640       || alarmType == KAAlarm::REMINDER_ALARM
01641       || alarmType == KAAlarm::DEFERRED_REMINDER_ALARM
01642       || alarmType == KAAlarm::DEFERRED_ALARM
01643       || alarmType == KAAlarm::AT_LOGIN_ALARM))
01644     {
01645 //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;
01646         KAAlarm al = event.alarm(alarmType);
01647         if (al.valid())
01648         {
01649             *this = event;
01650             setUid(DISPLAYING);
01651             mDisplaying     = true;
01652             mDisplayingTime = (alarmType == KAAlarm::AT_LOGIN_ALARM) ? repeatAtLoginTime : al.dateTime();
01653             switch (al.type())
01654             {
01655                 case KAAlarm::AT_LOGIN__ALARM:                mDisplayingFlags = REPEAT_AT_LOGIN;  break;
01656                 case KAAlarm::REMINDER__ALARM:                mDisplayingFlags = REMINDER;  break;
01657                 case KAAlarm::DEFERRED_REMINDER_TIME__ALARM:  mDisplayingFlags = REMINDER | TIME_DEFERRAL;  break;
01658                 case KAAlarm::DEFERRED_REMINDER_DATE__ALARM:  mDisplayingFlags = REMINDER | DATE_DEFERRAL;  break;
01659                 case KAAlarm::DEFERRED_TIME__ALARM:           mDisplayingFlags = TIME_DEFERRAL;  break;
01660                 case KAAlarm::DEFERRED_DATE__ALARM:           mDisplayingFlags = DATE_DEFERRAL;  break;
01661                 default:                                      mDisplayingFlags = 0;  break;
01662             }
01663             ++mAlarmCount;
01664             mUpdated = true;
01665             return true;
01666         }
01667     }
01668     return false;
01669 }
01670 
01671 /******************************************************************************
01672  * Return the original alarm which the displaying alarm refers to.
01673  */
01674 KAAlarm KAEvent::convertDisplayingAlarm() const
01675 {
01676     KAAlarm al;
01677     if (mDisplaying)
01678     {
01679         al = alarm(KAAlarm::DISPLAYING_ALARM);
01680         if (mDisplayingFlags & REPEAT_AT_LOGIN)
01681         {
01682             al.mRepeatAtLogin = true;
01683             al.mType = KAAlarm::AT_LOGIN__ALARM;
01684         }
01685         else if (mDisplayingFlags & DEFERRAL)
01686         {
01687             al.mDeferred = true;
01688             al.mType = (mDisplayingFlags == (REMINDER | DATE_DEFERRAL)) ? KAAlarm::DEFERRED_REMINDER_DATE__ALARM
01689                      : (mDisplayingFlags == (REMINDER | TIME_DEFERRAL)) ? KAAlarm::DEFERRED_REMINDER_TIME__ALARM
01690                      : (mDisplayingFlags == DATE_DEFERRAL) ? KAAlarm::DEFERRED_DATE__ALARM
01691                      : KAAlarm::DEFERRED_TIME__ALARM;
01692         }
01693         else if (mDisplayingFlags & REMINDER)
01694             al.mType = KAAlarm::REMINDER__ALARM;
01695         else
01696             al.mType = KAAlarm::MAIN__ALARM;
01697     }
01698     return al;
01699 }
01700 
01701 /******************************************************************************
01702  * Reinstate the original event from the 'displaying' event.
01703  */
01704 void KAEvent::reinstateFromDisplaying(const KAEvent& dispEvent)
01705 {
01706     if (dispEvent.mDisplaying)
01707     {
01708         *this = dispEvent;
01709         setUid(ACTIVE);
01710         mDisplaying = false;
01711         --mAlarmCount;
01712         mUpdated = true;
01713     }
01714 }
01715 
01716 /******************************************************************************
01717  * Determine whether the event will occur after the specified date/time.
01718  * If 'includeRepetitions' is true and the alarm has a simple repetition, it
01719  * returns true if any repetitions occur after the specified date/time.
01720  */
01721 bool KAEvent::occursAfter(const QDateTime& preDateTime, bool includeRepetitions) const
01722 {
01723     QDateTime dt;
01724     if (checkRecur() != KARecurrence::NO_RECUR)
01725     {
01726         if (mRecurrence->duration() < 0)
01727             return true;    // infinite recurrence
01728         dt = mRecurrence->endDateTime();
01729     }
01730     else
01731         dt = mNextMainDateTime.dateTime();
01732     if (mStartDateTime.isDateOnly())
01733     {
01734         QDate pre = preDateTime.date();
01735         if (preDateTime.time() < Preferences::startOfDay())
01736             pre = pre.addDays(-1);    // today's recurrence (if today recurs) is still to come
01737         if (pre < dt.date())
01738             return true;
01739     }
01740     else if (preDateTime < dt)
01741         return true;
01742 
01743     if (includeRepetitions  &&  mRepeatCount)
01744     {
01745         dt.addSecs(mRepeatCount * mRepeatInterval * 60);
01746         if (preDateTime < dt)
01747             return true;
01748     }
01749     return false;
01750 }
01751 
01752 /******************************************************************************
01753  * Get the date/time of the next occurrence of the event, after the specified
01754  * date/time.
01755  * 'result' = date/time of next occurrence, or invalid date/time if none.
01756  */
01757 KAEvent::OccurType KAEvent::nextOccurrence(const QDateTime& preDateTime, DateTime& result,
01758                                            KAEvent::OccurOption includeRepetitions) const
01759 {
01760     int repeatSecs = 0;
01761     QDateTime pre = preDateTime;
01762     if (includeRepetitions != IGNORE_REPETITION)
01763     {
01764         if (!mRepeatCount)
01765             includeRepetitions = IGNORE_REPETITION;
01766         else
01767         {
01768             repeatSecs = mRepeatInterval * 60;
01769             pre = preDateTime.addSecs(-mRepeatCount * repeatSecs);
01770         }
01771     }
01772 
01773     OccurType type;
01774     bool recurs = (checkRecur() != KARecurrence::NO_RECUR);
01775     if (recurs)
01776     {
01777         int remainingCount;
01778         type = nextRecurrence(pre, result, remainingCount);
01779     }
01780     else if (pre < mNextMainDateTime.dateTime())
01781     {
01782         result = mNextMainDateTime;
01783         type = FIRST_OR_ONLY_OCCURRENCE;
01784     }
01785     else
01786     {
01787         result = DateTime();
01788         type = NO_OCCURRENCE;
01789     }
01790 
01791     if (type != NO_OCCURRENCE  &&  result <= preDateTime)
01792     {
01793         // The next occurrence is a simple repetition
01794         int repetition = result.secsTo(preDateTime) / repeatSecs + 1;
01795         DateTime repeatDT = result.addSecs(repetition * repeatSecs);
01796         if (recurs)
01797         {
01798             // We've found a recurrence before the specified date/time, which has
01799             // a simple repetition after the date/time.
01800             // However, if the intervals between recurrences vary, we could possibly
01801             // have missed a later recurrence, which fits the criterion, so check again.
01802             DateTime dt;
01803             OccurType newType = previousOccurrence(repeatDT.dateTime(), dt, false);
01804             if (dt > result)
01805             {
01806                 type = newType;
01807                 result = dt;
01808                 if (includeRepetitions == RETURN_REPETITION  &&  result <= preDateTime)
01809                 {
01810                     // The next occurrence is a simple repetition
01811                     int repetition = result.secsTo(preDateTime) / repeatSecs + 1;
01812                     result = result.addSecs(repetition * repeatSecs);
01813                     type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01814                 }
01815                 return type;
01816             }
01817         }
01818         if (includeRepetitions == RETURN_REPETITION)
01819         {
01820             // The next occurrence is a simple repetition
01821             result = repeatDT;
01822             type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01823         }
01824     }
01825     return type;
01826 }
01827 
01828 /******************************************************************************
01829  * Get the date/time of the last previous occurrence of the event, before the
01830  * specified date/time.
01831  * If 'includeRepetitions' is true and the alarm has a simple repetition, the
01832  * last previous repetition is returned if appropriate.
01833  * 'result' = date/time of previous occurrence, or invalid date/time if none.
01834  */
01835 KAEvent::OccurType KAEvent::previousOccurrence(const QDateTime& afterDateTime, DateTime& result, bool includeRepetitions) const
01836 {
01837     if (mStartDateTime >= afterDateTime)
01838     {
01839         result = QDateTime();
01840         return NO_OCCURRENCE;     // the event starts after the specified date/time
01841     }
01842 
01843     // Find the latest recurrence of the event
01844     OccurType type;
01845     if (checkRecur() == KARecurrence::NO_RECUR)
01846     {
01847         result = mStartDateTime;
01848         type = FIRST_OR_ONLY_OCCURRENCE;
01849     }
01850     else
01851     {
01852         QDateTime recurStart = mRecurrence->startDateTime();
01853         QDateTime after = afterDateTime;
01854         if (mStartDateTime.isDateOnly()  &&  afterDateTime.time() > Preferences::startOfDay())
01855             after = after.addDays(1);    // today's recurrence (if today recurs) has passed
01856         QDateTime dt = mRecurrence->getPreviousDateTime(after);
01857         result.set(dt, mStartDateTime.isDateOnly());
01858         if (!dt.isValid())
01859             return NO_OCCURRENCE;
01860         if (dt == recurStart)
01861             type = FIRST_OR_ONLY_OCCURRENCE;
01862         else if (mRecurrence->getNextDateTime(dt).isValid())
01863             type = result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
01864         else
01865             type = LAST_RECURRENCE;
01866     }
01867 
01868     if (includeRepetitions  &&  mRepeatCount)
01869     {
01870         // Find the latest repetition which is before the specified time.
01871         // N.B. This is coded to avoid 32-bit integer overflow which occurs
01872         //      in QDateTime::secsTo() for large enough time differences.
01873         int repeatSecs = mRepeatInterval * 60;
01874         DateTime lastRepetition = result.addSecs(mRepeatCount * repeatSecs);
01875         if (lastRepetition < afterDateTime)
01876         {
01877             result = lastRepetition;
01878             return static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01879         }
01880         int repetition = (result.dateTime().secsTo(afterDateTime) - 1) / repeatSecs;
01881         if (repetition > 0)
01882         {
01883             result = result.addSecs(repetition * repeatSecs);
01884             return static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01885         }
01886     }
01887     return type;
01888 }
01889 
01890 /******************************************************************************
01891  * Set the date/time of the event to the next scheduled occurrence after the
01892  * specified date/time, provided that this is later than its current date/time.
01893  * Any reminder alarm is adjusted accordingly.
01894  * If 'includeRepetitions' is true and the alarm has a simple repetition, and
01895  * a repetition of a previous recurrence occurs after the specified date/time,
01896  * that previous recurrence is returned instead.
01897  */
01898 KAEvent::OccurType KAEvent::setNextOccurrence(const QDateTime& preDateTime, bool includeRepetitions)
01899 {
01900     if (preDateTime < mNextMainDateTime.dateTime())
01901         return FIRST_OR_ONLY_OCCURRENCE;    // it might not be the first recurrence - tant pis
01902     QDateTime pre = preDateTime;
01903     if (includeRepetitions)
01904     {
01905         if (!mRepeatCount)
01906             includeRepetitions = false;
01907         else
01908             pre = preDateTime.addSecs(-mRepeatCount * mRepeatInterval * 60);
01909     }
01910 
01911     DateTime dt;
01912     OccurType type;
01913     if (pre < mNextMainDateTime.dateTime())
01914     {
01915         dt = mNextMainDateTime;
01916         type = FIRST_OR_ONLY_OCCURRENCE;   // may not actually be the first occurrence
01917     }
01918     else if (checkRecur() != KARecurrence::NO_RECUR)
01919     {
01920         int remainingCount;
01921         type = nextRecurrence(pre, dt, remainingCount);
01922         if (type == NO_OCCURRENCE)
01923             return NO_OCCURRENCE;
01924         if (type != FIRST_OR_ONLY_OCCURRENCE  &&  dt != mNextMainDateTime)
01925         {
01926             // Need to reschedule the next trigger date/time
01927             mNextMainDateTime = dt;
01928             if (mRecurrence->duration() > 0)
01929                 mRemainingRecurrences = remainingCount;
01930             // Reinstate the reminder (if any) for the rescheduled recurrence
01931             if (mDeferral == REMINDER_DEFERRAL  ||  mArchiveReminderMinutes)
01932             {
01933                 if (mReminderOnceOnly)
01934                 {
01935                     if (mReminderMinutes)
01936                         set_archiveReminder();
01937                 }
01938                 else
01939                     set_reminder(mArchiveReminderMinutes);
01940             }
01941             if (mDeferral == REMINDER_DEFERRAL)
01942                 set_deferral(NO_DEFERRAL);
01943             mUpdated = true;
01944         }
01945     }
01946     else
01947         return NO_OCCURRENCE;
01948 
01949     if (includeRepetitions  &&  dt.dateTime() <= preDateTime)
01950     {
01951         // The next occurrence is a simple repetition.
01952         type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
01953         // Repetitions can't have a reminder, so remove any.
01954         if (mReminderMinutes)
01955             set_archiveReminder();
01956         if (mDeferral == REMINDER_DEFERRAL)
01957             set_deferral(NO_DEFERRAL);
01958         mUpdated = true;
01959     }
01960     return type;
01961 }
01962 
01963 /******************************************************************************
01964  * Get the date/time of the next recurrence of the event, after the specified
01965  * date/time.
01966  * 'result' = date/time of next occurrence, or invalid date/time if none.
01967  * 'remainingCount' = number of repetitions due, including the next occurrence.
01968  */
01969 KAEvent::OccurType KAEvent::nextRecurrence(const QDateTime& preDateTime, DateTime& result, int& remainingCount) const
01970 {
01971     QDateTime recurStart = mRecurrence->startDateTime();
01972     QDateTime pre = preDateTime;
01973     if (mStartDateTime.isDateOnly()  &&  preDateTime.time() < Preferences::startOfDay())
01974         pre = pre.addDays(-1);    // today's recurrence (if today recurs) is still to come
01975     remainingCount = 0;
01976     QDateTime dt = mRecurrence->getNextDateTime(pre);
01977     result.set(dt, mStartDateTime.isDateOnly());
01978     if (!dt.isValid())
01979         return NO_OCCURRENCE;
01980     if (dt == recurStart)
01981     {
01982         remainingCount = mRecurrence->duration();
01983         return FIRST_OR_ONLY_OCCURRENCE;
01984     }
01985     remainingCount = mRecurrence->duration() - mRecurrence->durationTo(dt) + 1;
01986     if (remainingCount == 1)
01987         return LAST_RECURRENCE;
01988     return result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
01989 }
01990 
01991 /******************************************************************************
01992  * Return the recurrence interval as text suitable for display.
01993  */
01994 QString KAEvent::recurrenceText(bool brief) const
01995 {
01996     if (mRepeatAtLogin)
01997         return brief ? i18n("Brief form of 'At Login'", "Login") : i18n("At login");
01998     if (mRecurrence)
01999     {
02000         int frequency = mRecurrence->frequency();
02001         switch (mRecurrence->defaultRRuleConst()->recurrenceType())
02002         {
02003             case RecurrenceRule::rMinutely:
02004                 if (frequency < 60)
02005                     return i18n("1 Minute", "%n Minutes", frequency);
02006                 else if (frequency % 60 == 0)
02007                     return i18n("1 Hour", "%n Hours", frequency/60);
02008                 else
02009                 {
02010                     QString mins;
02011                     return i18n("Hours and Minutes", "%1H %2M").arg(QString::number(frequency/60)).arg(mins.sprintf("%02d", frequency%60));
02012                 }
02013             case RecurrenceRule::rDaily:
02014                 return i18n("1 Day", "%n Days", frequency);
02015             case RecurrenceRule::rWeekly:
02016                 return i18n("1 Week", "%n Weeks", frequency);
02017             case RecurrenceRule::rMonthly:
02018                 return i18n("1 Month", "%n Months", frequency);
02019             case RecurrenceRule::rYearly:
02020                 return i18n("1 Year", "%n Years", frequency);
02021             case RecurrenceRule::rNone:
02022             default:
02023                 break;
02024         }
02025     }
02026     return brief ? QString::null : i18n("None");
02027 }
02028 
02029 /******************************************************************************
02030  * Return the repetition interval as text suitable for display.
02031  */
02032 QString KAEvent::repetitionText(bool brief) const
02033 {
02034     if (mRepeatCount)
02035     {
02036         if (mRepeatInterval % 1440)
02037         {
02038             if (mRepeatInterval < 60)
02039                 return i18n("1 Minute", "%n Minutes", mRepeatInterval);
02040             if (mRepeatInterval % 60 == 0)
02041                 return i18n("1 Hour", "%n Hours", mRepeatInterval/60);
02042             QString mins;
02043             return i18n("Hours and Minutes", "%1H %2M").arg(QString::number(mRepeatInterval/60)).arg(mins.sprintf("%02d", mRepeatInterval%60));
02044         }
02045         if (mRepeatInterval % (7*1440))
02046             return i18n("1 Day", "%n Days", mRepeatInterval/1440);
02047         return i18n("1 Week", "%n Weeks", mRepeatInterval/(7*1440));
02048     }
02049     return brief ? QString::null : i18n("None");
02050 }
02051 
02052 /******************************************************************************
02053  * Adjust the event date/time to the first recurrence of the event, on or after
02054  * start date/time. The event start date may not be a recurrence date, in which
02055  * case a later date will be set.
02056  */
02057 void KAEvent::setFirstRecurrence()
02058 {
02059     switch (checkRecur())
02060     {
02061         case KARecurrence::NO_RECUR:
02062         case KARecurrence::MINUTELY:
02063             return;
02064         case KARecurrence::ANNUAL_DATE:
02065         case KARecurrence::ANNUAL_POS:
02066             if (mRecurrence->yearMonths().isEmpty())
02067                 return;    // (presumably it's a template)
02068             break;
02069         case KARecurrence::DAILY:
02070         case KARecurrence::WEEKLY:
02071         case KARecurrence::MONTHLY_POS:
02072         case KARecurrence::MONTHLY_DAY:
02073             break;
02074     }
02075     QDateTime recurStart = mRecurrence->startDateTime();
02076     if (mRecurrence->recursOn(recurStart.date()))
02077         return;           // it already recurs on the start date
02078 
02079     // Set the frequency to 1 to find the first possible occurrence
02080     int frequency = mRecurrence->frequency();
02081     mRecurrence->setFrequency(1);
02082     int remainingCount;
02083     DateTime next;
02084     nextRecurrence(mNextMainDateTime.dateTime(), next, remainingCount);
02085     if (!next.isValid())
02086         mRecurrence->setStartDateTime(recurStart);   // reinstate the old value
02087     else
02088     {
02089         mRecurrence->setStartDateTime(next.dateTime());
02090         mStartDateTime = mNextMainDateTime = next;
02091         mUpdated = true;
02092     }
02093     mRecurrence->setFrequency(frequency);    // restore the frequency
02094 }
02095 
02096 /******************************************************************************
02097 *  Initialise the event's recurrence from a KCal::Recurrence.
02098 *  The event's start date/time is not changed.
02099 */
02100 void KAEvent::setRecurrence(const KARecurrence& recurrence)
02101 {
02102     mUpdated = true;
02103     delete mRecurrence;
02104     if (recurrence.doesRecur())
02105     {
02106         mRecurrence = new KARecurrence(recurrence);
02107         mRecurrence->setStartDateTime(mStartDateTime.dateTime());
02108         mRecurrence->setFloats(mStartDateTime.isDateOnly());
02109         mRemainingRecurrences = mRecurrence->duration();
02110         if (mRemainingRecurrences > 0  &&  !isTemplate())
02111             mRemainingRecurrences -= mRecurrence->durationTo(mNextMainDateTime.dateTime()) - 1;
02112     }
02113     else
02114     {
02115         mRecurrence = 0;
02116         mRemainingRecurrences = 0;
02117     }
02118 
02119     // Adjust simple repetition values to fit the recurrence
02120     setRepetition(mRepeatInterval, mRepeatCount);
02121 }
02122 
02123 /******************************************************************************
02124 *  Initialise the event's simple repetition.
02125 *  The repetition length is adjusted if necessary to fit any recurrence interval.
02126 *  Reply = false if a non-daily interval was specified for a date-only recurrence.
02127 */
02128 bool KAEvent::setRepetition(int interval, int count)
02129 {
02130     mUpdated        = true;
02131     mRepeatInterval = 0;
02132     mRepeatCount    = 0;
02133     if (interval > 0  &&  count > 0  &&  !mRepeatAtLogin)
02134     {
02135         if (interval % 1440  &&  mStartDateTime.isDateOnly())
02136             return false;    // interval must be in units of days for date-only alarms
02137         if (checkRecur() != KARecurrence::NO_RECUR)
02138         {
02139             int longestInterval = mRecurrence->longestInterval() - 1;
02140             if (interval * count > longestInterval)
02141                 count = longestInterval / interval;
02142         }
02143         mRepeatInterval = interval;
02144         mRepeatCount    = count;
02145     }
02146     return true;
02147 }
02148 
02149 /******************************************************************************
02150  * Set the recurrence to recur at a minutes interval.
02151  * Parameters:
02152  *    freq  = how many minutes between recurrences.
02153  *    count = number of occurrences, including first and last.
02154  *          = -1 to recur indefinitely.
02155  *          = 0 to use 'end' instead.
02156  *    end   = end date/time (invalid to use 'count' instead).
02157  * Reply = false if no recurrence was set up.
02158  */
02159 bool KAEvent::setRecurMinutely(int freq, int count, const QDateTime& end)
02160 {
02161     return setRecur(RecurrenceRule::rMinutely, freq, count, end);
02162 }
02163 
02164 /******************************************************************************
02165  * Set the recurrence to recur daily.
02166  * Parameters:
02167  *    freq  = how many days between recurrences.
02168  *    days  = which days of the week alarms are allowed to occur on.
02169  *    count = number of occurrences, including first and last.
02170  *          = -1 to recur indefinitely.
02171  *          = 0 to use 'end' instead.
02172  *    end   = end date (invalid to use 'count' instead).
02173  * Reply = false if no recurrence was set up.
02174  */
02175 bool KAEvent::setRecurDaily(int freq, const QBitArray& days, int count, const QDate& end)
02176 {
02177     if (!setRecur(RecurrenceRule::rDaily, freq, count, end))
02178         return false;
02179     int n = 0;
02180     for (int i = 0;  i < 7;  ++i)
02181     {
02182         if (days.testBit(i))
02183             ++n;
02184     }
02185     if (n < 7)
02186         mRecurrence->addWeeklyDays(days);
02187     return true;
02188 }
02189 
02190 /******************************************************************************
02191  * Set the recurrence to recur weekly, on the specified weekdays.
02192  * Parameters:
02193  *    freq  = how many weeks between recurrences.
02194  *    days  = which days of the week alarms should occur on.
02195  *    count = number of occurrences, including first and last.
02196  *          = -1 to recur indefinitely.
02197  *          = 0 to use 'end' instead.
02198  *    end   = end date (invalid to use 'count' instead).
02199  * Reply = false if no recurrence was set up.
02200  */
02201 bool KAEvent::setRecurWeekly(int freq, const QBitArray& days, int count, const QDate& end)
02202 {
02203     if (!setRecur(RecurrenceRule::rWeekly, freq, count, end))
02204         return false;
02205     mRecurrence->addWeeklyDays(days);
02206     return true;
02207 }
02208 
02209 /******************************************************************************
02210  * Set the recurrence to recur monthly, on the specified days within the month.
02211  * Parameters:
02212  *    freq  = how many months between recurrences.
02213  *    days  = which days of the month alarms should occur on.
02214  *    count = number of occurrences, including first and last.
02215  *          = -1 to recur indefinitely.
02216  *          = 0 to use 'end' instead.
02217  *    end   = end date (invalid to use 'count' instead).
02218  * Reply = false if no recurrence was set up.
02219  */
02220 bool KAEvent::setRecurMonthlyByDate(int freq, const QValueList<int>& days, int count, const QDate& end)
02221 {
02222     if (!setRecur(RecurrenceRule::rMonthly, freq, count, end))
02223         return false;
02224     for (QValueListConstIterator<int> it = days.begin();  it != days.end();  ++it)
02225         mRecurrence->addMonthlyDate(*it);
02226     return true;
02227 }
02228 
02229 /******************************************************************************
02230  * Set the recurrence to recur monthly, on the specified weekdays in the
02231  * specified weeks of the month.
02232  * Parameters:
02233  *    freq  = how many months between recurrences.
02234  *    posns = which days of the week/weeks of the month alarms should occur on.
02235  *    count = number of occurrences, including first and last.
02236  *          = -1 to recur indefinitely.
02237  *          = 0 to use 'end' instead.
02238  *    end   = end date (invalid to use 'count' instead).
02239  * Reply = false if no recurrence was set up.
02240  */
02241 bool KAEvent::setRecurMonthlyByPos(int freq, const QValueList<MonthPos>& posns, int count, const QDate& end)
02242 {
02243     if (!setRecur(RecurrenceRule::rMonthly, freq, count, end))
02244         return false;
02245     for (QValueListConstIterator<MonthPos> it = posns.begin();  it != posns.end();  ++it)
02246         mRecurrence->addMonthlyPos((*it).weeknum, (*it).days);
02247     return true;
02248 }
02249 
02250 /******************************************************************************
02251  * Set the recurrence to recur annually, on the specified start date in each
02252  * of the specified months.
02253  * Parameters:
02254  *    freq   = how many years between recurrences.
02255  *    months = which months of the year alarms should occur on.
02256  *    day    = day of month, or 0 to use start date
02257  *    feb29  = when February 29th should recur in non-leap years.
02258  *    count  = number of occurrences, including first and last.
02259  *           = -1 to recur indefinitely.
02260  *           = 0 to use 'end' instead.
02261  *    end    = end date (invalid to use 'count' instead).
02262  * Reply = false if no recurrence was set up.
02263  */
02264 bool KAEvent::setRecurAnnualByDate(int freq, const QValueList<int>& months, int day, KARecurrence::Feb29Type feb29, int count, const QDate& end)
02265 {
02266     if (!setRecur(RecurrenceRule::rYearly, freq, count, end, feb29))
02267         return false;
02268     for (QValueListConstIterator<int> it = months.begin();  it != months.end();  ++it)
02269         mRecurrence->addYearlyMonth(*it);
02270     if (day)
02271         mRecurrence->addMonthlyDate(day);
02272     return true;
02273 }
02274 
02275 /******************************************************************************
02276  * Set the recurrence to recur annually, on the specified weekdays in the
02277  * specified weeks of the specified months.
02278  * Parameters:
02279  *    freq   = how many years between recurrences.
02280  *    posns  = which days of the week/weeks of the month alarms should occur on.
02281  *    months = which months of the year alarms should occur on.
02282  *    count  = number of occurrences, including first and last.
02283  *           = -1 to recur indefinitely.
02284  *           = 0 to use 'end' instead.
02285  *    end    = end date (invalid to use 'count' instead).
02286  * Reply = false if no recurrence was set up.
02287  */
02288 bool KAEvent::setRecurAnnualByPos(int freq, const QValueList<MonthPos>& posns, const QValueList<int>& months, int count, const QDate& end)
02289 {
02290     if (!setRecur(RecurrenceRule::rYearly, freq, count, end))
02291         return false;
02292     for (QValueListConstIterator<int> it = months.begin();  it != months.end();  ++it)
02293         mRecurrence->addYearlyMonth(*it);
02294     for (QValueListConstIterator<MonthPos> it = posns.begin();  it != posns.end();  ++it)
02295         mRecurrence->addYearlyPos((*it).weeknum, (*it).days);
02296     return true;
02297 }
02298 
02299 /******************************************************************************
02300  * Initialise the event's recurrence data.
02301  * Parameters:
02302  *    freq  = how many intervals between recurrences.
02303  *    count = number of occurrences, including first and last.
02304  *          = -1 to recur indefinitely.
02305  *          = 0 to use 'end' instead.
02306  *    end   = end date/time (invalid to use 'count' instead).
02307  * Reply = false if no recurrence was set up.
02308  */
02309 bool KAEvent::setRecur(RecurrenceRule::PeriodType recurType, int freq, int count, const QDateTime& end, KARecurrence::Feb29Type feb29)
02310 {
02311     if (count >= -1  &&  (count || end.date().isValid()))
02312     {
02313         if (!mRecurrence)
02314             mRecurrence = new KARecurrence;
02315         if (mRecurrence->init(recurType, freq, count, mNextMainDateTime, end, feb29))
02316         {
02317             mUpdated = true;
02318             mRemainingRecurrences = count;
02319             return true;
02320         }
02321     }
02322     clearRecur();
02323     return false;
02324 }
02325 
02326 /******************************************************************************
02327  * Clear the event's recurrence and alarm repetition data.
02328  */
02329 void KAEvent::clearRecur()
02330 {
02331     mUpdated = true;
02332     delete mRecurrence;
02333     mRecurrence = 0;
02334     mRemainingRecurrences = 0;
02335 }
02336 
02337 /******************************************************************************
02338  * Validate the event's recurrence and alarm repetition data, correcting any
02339  * inconsistencies (which should never occur!).
02340  * Reply = true if a recurrence (as opposed to a login repetition) exists.
02341  */
02342 KARecurrence::Type KAEvent::checkRecur() const
02343 {
02344     if (mRecurrence)
02345     {
02346         KARecurrence::Type type = mRecurrence->type();
02347         switch (type)
02348         {
02349             case KARecurrence::MINUTELY:     // hourly      
02350             case KARecurrence::DAILY:        // daily
02351             case KARecurrence::WEEKLY:       // weekly on multiple days of week
02352             case KARecurrence::MONTHLY_DAY:  // monthly on multiple dates in month
02353             case KARecurrence::MONTHLY_POS:  // monthly on multiple nth day of week
02354             case KARecurrence::ANNUAL_DATE:  // annually on multiple months (day of month = start date)
02355             case KARecurrence::ANNUAL_POS:   // annually on multiple nth day of week in multiple months
02356                 return type;
02357             default:
02358                 if (mRecurrence)
02359                 {
02360                     delete mRecurrence;     // this shouldn't exist!!
02361                     const_cast<KAEvent*>(this)->mRecurrence = 0;
02362                 }
02363                 break;
02364         }
02365     }
02366     return KARecurrence::NO_RECUR;
02367 }
02368 
02369 
02370 /******************************************************************************
02371  * Return the recurrence interval in units of the recurrence period type.
02372  */
02373 int KAEvent::recurInterval() const
02374 {
02375     if (mRecurrence)
02376     {
02377         switch (mRecurrence->type())
02378         {
02379             case KARecurrence::MINUTELY:
02380             case KARecurrence::DAILY:
02381             case KARecurrence::WEEKLY:
02382             case KARecurrence::MONTHLY_DAY:
02383             case KARecurrence::MONTHLY_POS:
02384             case KARecurrence::ANNUAL_DATE:
02385             case KARecurrence::ANNUAL_POS:
02386                 return mRecurrence->frequency();
02387             default:
02388                 break;
02389         }
02390     }
02391     return 0;
02392 }
02393 
02394 #if 0
02395 /******************************************************************************
02396  * Convert a QValueList<WDayPos> to QValueList<MonthPos>.
02397  */
02398 QValueList<KAEvent::MonthPos> KAEvent::convRecurPos(const QValueList<KCal::RecurrenceRule::WDayPos>& wdaypos)
02399 {
02400     QValueList<MonthPos> mposns;
02401     for (QValueList<KCal::RecurrenceRule::WDayPos>::ConstIterator it = wdaypos.begin();  it != wdaypos.end();  ++it)
02402     {
02403         int daybit  = (*it).day() - 1;
02404         int weeknum = (*it).pos();
02405         bool found = false;
02406         for (QValueList<MonthPos>::Iterator mit = mposns.begin();  mit != mposns.end();  ++mit)
02407         {
02408             if ((*mit).weeknum == weeknum)
02409             {
02410                 (*mit).days.setBit(daybit);
02411                 found = true;
02412                 break;
02413             }
02414         }
02415         if (!found)
02416         {
02417             MonthPos mpos;
02418             mpos.days.fill(false);
02419             mpos.days.setBit(daybit);
02420             mpos.weeknum = weeknum;
02421             mposns.append(mpos);
02422         }
02423     }
02424     return mposns;
02425 }
02426 #endif
02427 
02428 /******************************************************************************
02429  * Find the alarm template with the specified name.
02430  * Reply = invalid event if not found.
02431  */
02432 KAEvent KAEvent::findTemplateName(AlarmCalendar& calendar, const QString& name)
02433 {
02434     KAEvent event;
02435     Event::List events = calendar.events();
02436     for (Event::List::ConstIterator evit = events.begin();  evit != events.end();  ++evit)
02437     {
02438         Event* ev = *evit;
02439         if (ev->summary() == name)
02440         {
02441             event.set(*ev);
02442             if (!event.isTemplate())
02443                 return KAEvent();    // this shouldn't ever happen
02444             break;
02445         }
02446     }
02447     return event;
02448 }
02449 
02450 /******************************************************************************
02451  * Adjust the time at which date-only events will occur for each of the events
02452  * in a list. Events for which both date and time are specified are left
02453  * unchanged.
02454  * Reply = true if any events have been updated.
02455  */
02456 bool KAEvent::adjustStartOfDay(const Event::List& events)
02457 {
02458     bool changed = false;
02459     QTime startOfDay = Preferences::startOfDay();
02460     for (Event::List::ConstIterator evit = events.begin();  evit != events.end();  ++evit)
02461     {
02462         Event* event = *evit;
02463         const QStringList& cats = event->categories();
02464         if (cats.find(DATE_ONLY_CATEGORY) != cats.end())
02465         {
02466             // It's an untimed event, so fix it
02467             QTime oldTime = event->dtStart().time();
02468             int adjustment = oldTime.secsTo(startOfDay);
02469             if (adjustment)
02470             {
02471                 event->setDtStart(QDateTime(event->dtStart().date(), startOfDay));
02472                 Alarm::List alarms = event->alarms();
02473                 int deferralOffset = 0;
02474                 for (Alarm::List::ConstIterator alit = alarms.begin();  alit != alarms.end();  ++alit)
02475                 {
02476                     // Parse the next alarm's text
02477                     Alarm& alarm = **alit;
02478                     AlarmData data;
02479                     readAlarm(alarm, data);
02480                     if (data.type & KAAlarm::TIMED_DEFERRAL_FLAG)
02481                     {
02482                         // Timed deferral alarm, so adjust the offset
02483                         deferralOffset = alarm.startOffset().asSeconds();
02484                         alarm.setStartOffset(deferralOffset - adjustment);
02485                     }
02486                     else if (data.type == KAAlarm::AUDIO__ALARM
02487                     &&       alarm.startOffset().asSeconds() == deferralOffset)
02488                     {
02489                         // Audio alarm is set for the same time as the deferral alarm
02490                         alarm.setStartOffset(deferralOffset - adjustment);
02491                     }
02492                 }
02493                 changed = true;
02494             }
02495         }
02496         else
02497         {
02498             // It's a timed event. Fix any untimed alarms.
02499             int deferralOffset = 0;
02500             int newDeferralOffset = 0;
02501             AlarmMap alarmMap;
02502             readAlarms(*event, &alarmMap);
02503             for (AlarmMap::Iterator it = alarmMap.begin();  it != alarmMap.end();  ++it)
02504             {
02505                 const AlarmData& data = it.data();
02506                 if ((data.type & KAAlarm::DEFERRED_ALARM)
02507                 &&  !(data.type & KAAlarm::TIMED_DEFERRAL_FLAG))
02508                 {
02509                     // Date-only deferral alarm, so adjust its time
02510                     QDateTime altime = data.alarm->time();
02511                     altime.setTime(startOfDay);
02512                     deferralOffset = data.alarm->startOffset().asSeconds();
02513                     newDeferralOffset = event->dtStart().secsTo(altime);
02514                     const_cast<Alarm*>(data.alarm)->setStartOffset(newDeferralOffset);
02515                     changed = true;
02516                 }
02517                 else if (data.type == KAAlarm::AUDIO__ALARM
02518                 &&       data.alarm->startOffset().asSeconds() == deferralOffset)
02519                 {
02520                     // Audio alarm is set for the same time as the deferral alarm
02521                     const_cast<Alarm*>(data.alarm)->setStartOffset(newDeferralOffset);
02522                     changed = true;
02523                 }
02524             }
02525         }
02526     }
02527     return changed;
02528 }
02529 
02530 /******************************************************************************
02531  * If the calendar was written by a previous version of KAlarm, do any
02532  * necessary format conversions on the events to ensure that when the calendar
02533  * is saved, no information is lost or corrupted.
02534  */
02535 void KAEvent::convertKCalEvents(KCal::Calendar& calendar, int version, bool adjustSummerTime)
02536 {
02537     // KAlarm pre-0.9 codes held in the alarm's DESCRIPTION property
02538     static const QChar   SEPARATOR        = ';';
02539     static const QChar   LATE_CANCEL_CODE = 'C';
02540     static const QChar   AT_LOGIN_CODE    = 'L';   // subsidiary alarm at every login
02541     static const QChar   DEFERRAL_CODE    = 'D';   // extra deferred alarm
02542     static const QString TEXT_PREFIX      = QString::fromLatin1("TEXT:");
02543     static const QString FILE_PREFIX      = QString::fromLatin1("FILE:");
02544     static const QString COMMAND_PREFIX   = QString::fromLatin1("CMD:");
02545 
02546     // KAlarm pre-0.9.2 codes held in the event's CATEGORY property
02547     static const QString BEEP_CATEGORY    = QString::fromLatin1("BEEP");
02548 
02549     // KAlarm pre-1.1.1 LATECANCEL category with no parameter
02550     static const QString LATE_CANCEL_CAT = QString::fromLatin1("LATECANCEL");
02551 
02552     // KAlarm pre-1.3.0 TMPLDEFTIME category with no parameter
02553     static const QString TEMPL_DEF_TIME_CAT = QString::fromLatin1("TMPLDEFTIME");
02554 
02555     // KAlarm pre-1.3.1 XTERM category
02556     static const QString EXEC_IN_XTERM_CAT  = QString::fromLatin1("XTERM");
02557 
02558     if (version >= calVersion())
02559         return;
02560 
02561     kdDebug(5950) << "KAEvent::convertKCalEvents(): adjusting version " << version << endl;
02562     bool pre_0_7   = (version < KAlarm::Version(0,7,0));
02563     bool pre_0_9   = (version < KAlarm::Version(0,9,0));
02564     bool pre_0_9_2 = (version < KAlarm::Version(0,9,2));
02565     bool pre_1_1_1 = (version < KAlarm::Version(1,1,1));
02566     bool pre_1_2_1 = (version < KAlarm::Version(1,2,1));
02567     bool pre_1_3_0 = (version < KAlarm::Version(1,3,0));
02568     bool pre_1_3_1 = (version < KAlarm::Version(1,3,1));
02569     Q_ASSERT(calVersion() == KAlarm::Version(1,3,1));
02570 
02571     QDateTime dt0(QDate(1970,1,1), QTime(0,0,0));
02572     QTime startOfDay = Preferences::startOfDay();
02573 
02574     Event::List events = calendar.rawEvents();
02575     for (Event::List::ConstIterator evit = events.begin();  evit != events.end();  ++evit)
02576     {
02577         Event* event = *evit;
02578         Alarm::List alarms = event->alarms();
02579         if (alarms.isEmpty())
02580             continue;    // KAlarm isn't interested in events without alarms
02581         QStringList cats = event->categories();
02582         bool addLateCancel = false;
02583 
02584         if (pre_0_7  &&  event->doesFloat())
02585         {
02586             // It's a KAlarm pre-0.7 calendar file.
02587             // Ensure that when the calendar is saved, the alarm time isn't lost.
02588             event->setFloats(false);
02589         }
02590 
02591         if (pre_0_9)
02592         {
02593             /*
02594              * It's a KAlarm pre-0.9 calendar file.
02595              * All alarms were of type DISPLAY. Instead of the X-KDE-KALARM-TYPE
02596              * alarm property, characteristics were stored as a prefix to the
02597              * alarm DESCRIPTION property, as follows:
02598              *   SEQNO;[FLAGS];TYPE:TEXT
02599              * where
02600              *   SEQNO = sequence number of alarm within the event
02601              *   FLAGS = C for late-cancel, L for repeat-at-login, D for deferral
02602              *   TYPE = TEXT or FILE or CMD
02603              *   TEXT = message text, file name/URL or command
02604              */
02605             for (Alarm::List::ConstIterator alit = alarms.begin();  alit != alarms.end();  ++alit)
02606             {
02607                 Alarm* alarm = *alit;
02608                 bool atLogin    = false;
02609                 bool deferral   = false;
02610                 bool lateCancel = false;
02611                 KAAlarmEventBase::Type action = T_MESSAGE;
02612                 QString txt = alarm->text();
02613                 int length = txt.length();
02614                 int i = 0;
02615                 if (txt[0].isDigit())
02616                 {
02617                     while (++i < length  &&  txt[i].isDigit()) ;
02618                     if (i < length  &&  txt[i++] == SEPARATOR)
02619                     {
02620                         while (i < length)
02621                         {
02622                             QChar ch = txt[i++];
02623                             if (ch == SEPARATOR)
02624                                 break;
02625                             if (ch == LATE_CANCEL_CODE)
02626                                 lateCancel = true;
02627                             else if (ch == AT_LOGIN_CODE)
02628                                 atLogin = true;
02629                             else if (ch == DEFERRAL_CODE)
02630                                 deferral = true;
02631                         }
02632                     }
02633                     else
02634                         i = 0;     // invalid prefix
02635                 }
02636                 if (txt.find(TEXT_PREFIX, i) == i)
02637                     i += TEXT_PREFIX.length();
02638                 else if (txt.find(FILE_PREFIX, i) == i)
02639                 {
02640                     action = T_FILE;
02641                     i += FILE_PREFIX.length();
02642                 }
02643                 else if (txt.find(COMMAND_PREFIX, i) == i)
02644                 {
02645                     action = T_COMMAND;
02646                     i += COMMAND_PREFIX.length();
02647                 }
02648                 else
02649                     i = 0;
02650                 txt = txt.mid(i);
02651 
02652                 QStringList types;
02653                 switch (action)
02654                 {
02655                     case T_FILE:
02656                         types += FILE_TYPE;
02657                         // fall through to T_MESSAGE
02658                     case T_MESSAGE:
02659                         alarm->setDisplayAlarm(txt);
02660                         break;
02661                     case T_COMMAND:
02662                         setProcedureAlarm(alarm, txt);
02663                         break;
02664                     case T_EMAIL:     // email alarms were introduced in KAlarm 0.9
02665                     case T_AUDIO:     // never occurs in this context
02666                         break;
02667                 }
02668                 if (atLogin)
02669                 {
02670                     types += AT_LOGIN_TYPE;
02671                     lateCancel = false;
02672                 }
02673                 else if (deferral)
02674                     types += TIME_DEFERRAL_TYPE;
02675                 if (lateCancel)
02676                     addLateCancel = true;
02677                 if (types.count() > 0)
02678                     alarm->setCustomProperty(APPNAME, TYPE_PROPERTY, types.join(","));
02679 
02680                 if (pre_0_7  &&  alarm->repeatCount() > 0  &&  alarm->snoozeTime() > 0)
02681                 {
02682                     // It's a KAlarm pre-0.7 calendar file.
02683                     // Minutely recurrences were stored differently.
02684                     Recurrence* recur = event->recurrence();
02685                     if (recur  &&  recur->doesRecur())
02686                     {
02687                         recur->setMinutely(alarm->snoozeTime());
02688                         recur->setDuration(alarm->repeatCount() + 1);
02689                         alarm->setRepeatCount(0);
02690                         alarm->setSnoozeTime(0);
02691                     }
02692                 }
02693 
02694                 if (adjustSummerTime)
02695                 {
02696                     // The calendar file was written by the KDE 3.0.0 version of KAlarm 0.5.7.
02697                     // Summer time was ignored when converting to UTC.
02698                     QDateTime dt = alarm->time();
02699                     time_t t = dt0.secsTo(dt);
02700                     struct tm* dtm = localtime(&t);
02701                     if (dtm->tm_isdst)
02702                     {
02703                         dt = dt.addSecs(-3600);
02704                         alarm->setTime(dt);
02705                     }
02706                 }
02707             }
02708         }
02709 
02710         if (pre_0_9_2)
02711         {
02712             /*
02713              * It's a KAlarm pre-0.9.2 calendar file.
02714              * For the expired calendar, set the CREATED time to the DTEND value.
02715              * Convert date-only DTSTART to date/time, and add category "DATE".
02716              * Set the DTEND time to the DTSTART time.
02717              * Convert all alarm times to DTSTART offsets.
02718              * For display alarms, convert the first unlabelled category to an
02719              * X-KDE-KALARM-FONTCOLOUR property.
02720              * Convert BEEP category into an audio alarm with no audio file.
02721              */
02722             if (uidStatus(event->uid()) == EXPIRED)
02723                 event->setCreated(event->dtEnd());
02724             QDateTime start = event->dtStart();
02725             if (event->doesFloat())
02726             {
02727                 event->setFloats(false);
02728                 start.setTime(startOfDay);
02729                 cats.append(DATE_ONLY_CATEGORY);
02730             }
02731             event->setHasEndDate(false);
02732 
02733             Alarm::List::ConstIterator alit;
02734             for (alit = alarms.begin();  alit != alarms.end();  ++alit)
02735             {
02736                 Alarm* alarm = *alit;
02737                 QDateTime dt = alarm->time();
02738                 alarm->setStartOffset(start.secsTo(dt));
02739             }
02740 
02741             if (cats.count() > 0)
02742             {
02743                 for (alit = alarms.begin();  alit != alarms.end();  ++alit)
02744                 {
02745                     Alarm* alarm = *alit;
02746                     if (alarm->type() == Alarm::Display)
02747                         alarm->setCustomProperty(APPNAME, FONT_COLOUR_PROPERTY,
02748                                                  QString::fromLatin1("%1;;").arg(cats[0]));
02749                 }
02750                 cats.remove(cats.begin());
02751             }
02752 
02753             for (QStringList::Iterator it = cats.begin();  it != cats.end();  ++it)
02754             {
02755                 if (*it == BEEP_CATEGORY)
02756                 {
02757                     cats.remove(it);
02758 
02759                     Alarm* alarm = event->newAlarm();
02760                     alarm->setEnabled(true);
02761                     alarm->setAudioAlarm();
02762                     QDateTime dt = event->dtStart();    // default
02763 
02764                     // Parse and order the alarms to know which one's date/time to use
02765                     AlarmMap alarmMap;
02766                     readAlarms(*event, &alarmMap);
02767                     AlarmMap::ConstIterator it = alarmMap.begin();
02768                     if (it != alarmMap.end())
02769                     {
02770                         dt = it.data().alarm->time();
02771                         break;
02772                     }
02773                     alarm->setStartOffset(start.secsTo(dt));
02774                     break;
02775                 }
02776             }
02777 
02778         }
02779 
02780         if (pre_1_1_1)
02781         {
02782             /*
02783              * It's a KAlarm pre-1.1.1 calendar file.
02784              * Convert simple LATECANCEL category to LATECANCEL:n where n = minutes late.
02785              */
02786             QStringList::Iterator it;
02787             while ((it = cats.find(LATE_CANCEL_CAT)) != cats.end())
02788             {
02789                 cats.remove(it);
02790                 addLateCancel = true;
02791             }
02792         }
02793 
02794         if (pre_1_2_1)
02795         {
02796             /*
02797              * It's a KAlarm pre-1.2.1 calendar file.
02798              * Convert email display alarms from translated to untranslated header prefixes.
02799              */
02800             for (Alarm::List::ConstIterator alit = alarms.begin();  alit != alarms.end();  ++alit)
02801             {
02802                 Alarm* alarm = *alit;
02803                 if (alarm->type() == Alarm::Display)
02804                 {
02805                     QString oldtext = alarm->text();
02806                     QString newtext = AlarmText::toCalendarText(oldtext);
02807                     if (oldtext != newtext)
02808                         alarm->setDisplayAlarm(newtext);
02809                 }
02810             }
02811         }
02812 
02813         if (pre_1_3_0)
02814         {
02815             /*
02816              * It's a KAlarm pre-1.3.0 calendar file.
02817              * Convert simple TMPLDEFTIME category to TMPLAFTTIME:n where n = minutes after.
02818              */
02819             QStringList::Iterator it;
02820             while ((it = cats.find(TEMPL_DEF_TIME_CAT)) != cats.end())
02821             {
02822                 cats.remove(it);
02823                 cats.append(QString("%1%2").arg(TEMPL_AFTER_TIME_CATEGORY).arg(0));
02824             }
02825         }
02826 
02827         if (pre_1_3_1)
02828         {
02829             /*
02830              * It's a KAlarm pre-1.3.1 calendar file.
02831              * Convert simple XTERM category to LOG:xterm:
02832              */
02833             QStringList::Iterator it;
02834             while ((it = cats.find(EXEC_IN_XTERM_CAT)) != cats.end())
02835             {
02836                 cats.remove(it);
02837                 cats.append(LOG_CATEGORY + xtermURL);
02838             }
02839         }
02840 
02841         if (addLateCancel)
02842             cats.append(QString("%1%2").arg(LATE_CANCEL_CATEGORY).arg(1));
02843 
02844         event->setCategories(cats);
02845     }
02846 }
02847 
02848 #ifndef NDEBUG
02849 void KAEvent::dumpDebug() const
02850 {
02851     kdDebug(5950) << "KAEvent dump:\n";
02852     KAAlarmEventBase::dumpDebug();
02853     if (!mTemplateName.isEmpty())
02854     {
02855         kdDebug(5950) << "-- mTemplateName:" << mTemplateName << ":\n";
02856         kdDebug(5950) << "-- mTemplateAfterTime:" << mTemplateAfterTime << ":\n";
02857     }
02858     if (mActionType == T_MESSAGE  ||  mActionType == T_FILE)
02859     {
02860         kdDebug(5950) << "-- mAudioFile:" << mAudioFile << ":\n";
02861         kdDebug(5950) << "-- mPreAction:" << mPreAction << ":\n";
02862         kdDebug(5950) << "-- mPostAction:" << mPostAction << ":\n";
02863     }
02864     else if (mActionType == T_COMMAND)
02865     {
02866         kdDebug(5950) << "-- mCommandXterm:" << (mCommandXterm ? "true" : "false") << ":\n";
02867         kdDebug(5950) << "-- mLogFile:" << mLogFile << ":\n";
02868     }
02869     kdDebug(5950) << "-- mKMailSerialNumber:" << mKMailSerialNumber << ":\n";
02870     kdDebug(5950) << "-- mCopyToKOrganizer:" << (mCopyToKOrganizer ? "true" : "false") << ":\n";
02871     kdDebug(5950) << "-- mStartDateTime:" << mStartDateTime.toString() << ":\n";
02872     kdDebug(5950) << "-- mSaveDateTime:" << mSaveDateTime.toString() << ":\n";
02873     if (mRepeatAtLogin)
02874         kdDebug(5950) << "-- mAtLoginDateTime:" << mAtLoginDateTime.toString() << ":\n";
02875     kdDebug(5950) << "-- mArchiveRepeatAtLogin:" << (mArchiveRepeatAtLogin ? "true" : "false") << ":\n";
02876     kdDebug(5950) << "-- mEnabled:" << (mEnabled ? "true" : "false") << ":\n";
02877     if (mReminderMinutes)
02878         kdDebug(5950) << "-- mReminderMinutes:" << mReminderMinutes << ":\n";
02879     if (mArchiveReminderMinutes)
02880         kdDebug(5950) << "-- mArchiveReminderMinutes:" << mArchiveReminderMinutes << ":\n";
02881     if (mReminderMinutes  ||  mArchiveReminderMinutes)
02882         kdDebug(5950) << "-- mReminderOnceOnly:" << mReminderOnceOnly << ":\n";
02883     else if (mDeferral > 0)
02884     {
02885         kdDebug(5950) << "-- mDeferral:" << (mDeferral == NORMAL_DEFERRAL ? "normal" : "reminder") << ":\n";
02886         kdDebug(5950) << "-- mDeferralTime:" << mDeferralTime.toString() << ":\n";
02887     }
02888     else if (mDeferral == CANCEL_DEFERRAL)
02889         kdDebug(5950) << "-- mDeferral:cancel:\n";
02890     kdDebug(5950) << "-- mDeferDefaultMinutes:" << mDeferDefaultMinutes << ":\n";
02891     if (mDisplaying)
02892     {
02893         kdDebug(5950) << "-- mDisplayingTime:" << mDisplayingTime.toString() << ":\n";
02894         kdDebug(5950) << "-- mDisplayingFlags:" << mDisplayingFlags << ":\n";
02895     }
02896     kdDebug(5950) << "-- mRevision:" << mRevision << ":\n";
02897     kdDebug(5950) << "-- mRecurrence:" << (mRecurrence ? "true" : "false") << ":\n";
02898     if (mRecurrence)
02899         kdDebug(5950) << "-- mRemainingRecurrences:" << mRemainingRecurrences << ":\n";
02900     kdDebug(5950) << "-- mAlarmCount:" << mAlarmCount << ":\n";
02901     kdDebug(5950) << "-- mMainExpired:" << (mMainExpired ? "true" : "false") << ":\n";
02902     kdDebug(5950) << "KAEvent dump end\n";
02903 }
02904 #endif
02905 
02906 
02907 /*=============================================================================
02908 = Class KAAlarm
02909 = Corresponds to a single KCal::Alarm instance.
02910 =============================================================================*/
02911 
02912 KAAlarm::KAAlarm(const KAAlarm& alarm)
02913     : KAAlarmEventBase(alarm),
02914       mType(alarm.mType),
02915       mRecurs(alarm.mRecurs),
02916       mDeferred(alarm.mDeferred)
02917 { }
02918 
02919 
02920 int KAAlarm::flags() const
02921 {
02922     return KAAlarmEventBase::flags()
02923          | (mDeferred ? KAEvent::DEFERRAL : 0);
02924 
02925 }
02926 
02927 #ifndef NDEBUG
02928 void KAAlarm::dumpDebug() const
02929 {
02930     kdDebug(5950) << "KAAlarm dump:\n";
02931     KAAlarmEventBase::dumpDebug();
02932     const char* altype = 0;
02933     switch (mType)
02934     {
02935         case MAIN__ALARM:                    altype = "MAIN";  break;
02936         case REMINDER__ALARM:                altype = "REMINDER";  break;
02937         case DEFERRED_DATE__ALARM:           altype = "DEFERRED(DATE)";  break;
02938         case DEFERRED_TIME__ALARM:           altype = "DEFERRED(TIME)";  break;
02939         case DEFERRED_REMINDER_DATE__ALARM:  altype = "DEFERRED_REMINDER(DATE)";  break;
02940         case DEFERRED_REMINDER_TIME__ALARM:  altype = "DEFERRED_REMINDER(TIME)";  break;
02941         case AT_LOGIN__ALARM:                altype = "LOGIN";  break;
02942         case DISPLAYING__ALARM:              altype = "DISPLAYING";  break;
02943         case AUDIO__ALARM:                   altype = "AUDIO";  break;
02944         case PRE_ACTION__ALARM:              altype = "PRE_ACTION";  break;
02945         case POST_ACTION__ALARM:             altype = "POST_ACTION";  break;
02946         default:                             altype = "INVALID";  break;
02947     }
02948     kdDebug(5950) << "-- mType:" << altype << ":\n";
02949     kdDebug(5950) << "-- mRecurs:" << (mRecurs ? "true" : "false") << ":\n";
02950     kdDebug(5950) << "-- mDeferred:" << (mDeferred ? "true" : "false") << ":\n";
02951     kdDebug(5950) << "KAAlarm dump end\n";
02952 }
02953 
02954 const char* KAAlarm::debugType(Type type)
02955 {
02956     switch (type)
02957     {
02958         case MAIN_ALARM:               return "MAIN";
02959         case REMINDER_ALARM:           return "REMINDER";
02960         case DEFERRED_ALARM:           return "DEFERRED";
02961         case DEFERRED_REMINDER_ALARM:  return "DEFERRED_REMINDER";
02962         case AT_LOGIN_ALARM:           return "LOGIN";
02963         case DISPLAYING_ALARM:         return "DISPLAYING";
02964         case AUDIO_ALARM:              return "AUDIO";
02965         case PRE_ACTION_ALARM:         return "PRE_ACTION";
02966         case POST_ACTION_ALARM:        return "POST_ACTION";
02967         default:                       return "INVALID";
02968     }
02969 }
02970 #endif
02971 
02972 
02973 /*=============================================================================
02974 = Class KAAlarmEventBase
02975 =============================================================================*/
02976 
02977 void KAAlarmEventBase::copy(const KAAlarmEventBase& rhs)
02978 {
02979     mEventID          = rhs.mEventID;
02980     mText             = rhs.mText;
02981     mNextMainDateTime = rhs.mNextMainDateTime;
02982     mBgColour         = rhs.mBgColour;
02983     mFgColour         = rhs.mFgColour;
02984     mFont             = rhs.mFont;
02985     mEmailFromKMail   = rhs.mEmailFromKMail;
02986     mEmailAddresses   = rhs.mEmailAddresses;
02987     mEmailSubject     = rhs.mEmailSubject;
02988     mEmailAttachments = rhs.mEmailAttachments;
02989     mSoundVolume      = rhs.mSoundVolume;
02990     mFadeVolume       = rhs.mFadeVolume;
02991     mFadeSeconds      = rhs.mFadeSeconds;
02992     mActionType       = rhs.mActionType;
02993     mCommandScript    = rhs.mCommandScript;
02994     mRepeatCount      = rhs.mRepeatCount;
02995     mRepeatInterval   = rhs.mRepeatInterval;
02996     mBeep             = rhs.mBeep;
02997     mSpeak            = rhs.mSpeak;
02998     mRepeatSound      = rhs.mRepeatSound;
02999     mRepeatAtLogin    = rhs.mRepeatAtLogin;
03000     mDisplaying       = rhs.mDisplaying;
03001     mLateCancel       = rhs.mLateCancel;
03002     mAutoClose        = rhs.mAutoClose;
03003     mEmailBcc         = rhs.mEmailBcc;
03004     mConfirmAck       = rhs.mConfirmAck;
03005     mDefaultFont      = rhs.mDefaultFont;
03006 }
03007 
03008 void KAAlarmEventBase::set(int flags)
03009 {
03010     mSpeak         = flags & KAEvent::SPEAK;
03011     mBeep          = (flags & KAEvent::BEEP) && !mSpeak;
03012     mRepeatSound   = flags & KAEvent::REPEAT_SOUND;
03013     mRepeatAtLogin = flags & KAEvent::REPEAT_AT_LOGIN;
03014     mAutoClose     = (flags & KAEvent::AUTO_CLOSE) && mLateCancel;
03015     mEmailBcc      = flags & KAEvent::EMAIL_BCC;
03016     mConfirmAck    = flags & KAEvent::CONFIRM_ACK;
03017     mDisplaying    = flags & KAEvent::DISPLAYING_;
03018     mDefaultFont   = flags & KAEvent::DEFAULT_FONT;
03019     mCommandScript = flags & KAEvent::SCRIPT;
03020 }
03021 
03022 int KAAlarmEventBase::flags() const
03023 {
03024     return (mBeep && !mSpeak ? KAEvent::BEEP : 0)
03025          | (mSpeak           ? KAEvent::SPEAK : 0)
03026          | (mRepeatSound     ? KAEvent::REPEAT_SOUND : 0)
03027          | (mRepeatAtLogin   ? KAEvent::REPEAT_AT_LOGIN : 0)
03028          | (mAutoClose       ? KAEvent::AUTO_CLOSE : 0)
03029          | (mEmailBcc        ? KAEvent::EMAIL_BCC : 0)
03030          | (mConfirmAck      ? KAEvent::CONFIRM_ACK : 0)
03031          | (mDisplaying      ? KAEvent::DISPLAYING_ : 0)
03032          | (mDefaultFont     ? KAEvent::DEFAULT_FONT : 0)
03033          | (mCommandScript   ? KAEvent::SCRIPT : 0);
03034 }
03035 
03036 const QFont& KAAlarmEventBase::font() const
03037 {
03038     return mDefaultFont ? Preferences::messageFont() : mFont;
03039 }
03040 
03041 #ifndef NDEBUG
03042 void KAAlarmEventBase::dumpDebug() const
03043 {
03044     kdDebug(5950) << "-- mEventID:" << mEventID << ":\n";
03045     kdDebug(5950) << "-- mActionType:" << (mActionType == T_MESSAGE ? "MESSAGE" : mActionType == T_FILE ? "FILE" : mActionType == T_COMMAND ? "COMMAND" : mActionType == T_EMAIL ? "EMAIL" : mActionType == T_AUDIO ? "AUDIO" : "??") << ":\n";
03046     kdDebug(5950) << "-- mText:" << mText << ":\n";
03047     if (mActionType == T_COMMAND)
03048         kdDebug(5950) << "-- mCommandScript:" << (mCommandScript ? "true" : "false") << ":\n";
03049     kdDebug(5950) << "-- mNextMainDateTime:" << mNextMainDateTime.toString() << ":\n";
03050     if (mActionType == T_EMAIL)
03051     {
03052         kdDebug(5950) << "-- mEmail: FromKMail:" << mEmailFromKMail << ":\n";
03053         kdDebug(5950) << "--         Addresses:" << mEmailAddresses.join(", ") << ":\n";
03054         kdDebug(5950) << "--         Subject:" << mEmailSubject << ":\n";
03055         kdDebug(5950) << "--         Attachments:" << mEmailAttachments.join(", ") << ":\n";
03056         kdDebug(5950) << "--         Bcc:" << (mEmailBcc ? "true" : "false") << ":\n";
03057     }
03058     kdDebug(5950) << "-- mBgColour:" << mBgColour.name() << ":\n";
03059     kdDebug(5950) << "-- mFgColour:" << mFgColour.name() << ":\n";
03060     kdDebug(5950) << "-- mDefaultFont:" << (mDefaultFont ? "true" : "false") << ":\n";
03061     if (!mDefaultFont)
03062         kdDebug(5950) << "-- mFont:" << mFont.toString() << ":\n";
03063     kdDebug(5950) << "-- mBeep:" << (mBeep ? "true" : "false") << ":\n";
03064     kdDebug(5950) << "-- mSpeak:" << (mSpeak ? "true" : "false") << ":\n";
03065     if (mActionType == T_AUDIO)
03066     {
03067         if (mSoundVolume >= 0)
03068         {
03069             kdDebug(5950) << "-- mSoundVolume:" << mSoundVolume << ":\n";
03070             if (mFadeVolume >= 0)
03071             {
03072                 kdDebug(5950) << "-- mFadeVolume:" << mFadeVolume << ":\n";
03073                 kdDebug(5950) << "-- mFadeSeconds:" << mFadeSeconds << ":\n";
03074             }
03075             else
03076                 kdDebug(5950) << "-- mFadeVolume:-:\n";
03077         }
03078         else
03079             kdDebug(5950) << "-- mSoundVolume:-:\n";
03080         kdDebug(5950) << "-- mRepeatSound:" << (mRepeatSound ? "true" : "false") << ":\n";
03081     }
03082     kdDebug(5950) << "-- mConfirmAck:" << (mConfirmAck ? "true" : "false") << ":\n";
03083     kdDebug(5950) << "-- mRepeatAtLogin:" << (mRepeatAtLogin ? "true" : "false") << ":\n";
03084     kdDebug(5950) << "-- mRepeatCount:" << mRepeatCount << ":\n";
03085     kdDebug(5950) << "-- mRepeatInterval:" << mRepeatInterval << ":\n";
03086     kdDebug(5950) << "-- mDisplaying:" << (mDisplaying ? "true" : "false") << ":\n";
03087     kdDebug(5950) << "-- mLateCancel:" << mLateCancel << ":\n";
03088     kdDebug(5950) << "-- mAutoClose:" << (mAutoClose ? "true" : "false") << ":\n";
03089 }
03090 #endif
03091 
03092 
03093 /*=============================================================================
03094 = Class EmailAddressList
03095 =============================================================================*/
03096 
03097 /******************************************************************************
03098  * Sets the list of email addresses, removing any empty addresses.
03099  * Reply = false if empty addresses were found.
03100  */
03101 EmailAddressList& EmailAddressList::operator=(const QValueList<Person>& addresses)
03102 {
03103     clear();
03104     for (QValueList<Person>::ConstIterator it = addresses.begin();  it != addresses.end();  ++it)
03105     {
03106         if (!(*it).email().isEmpty())
03107             append(*it);
03108     }
03109     return *this;
03110 }
03111 
03112 /******************************************************************************
03113  * Return the email address list as a string, each address being delimited by
03114  * the specified separator string.
03115  */
03116 QString EmailAddressList::join(const QString& separator) const
03117 {
03118     QString result;
03119     bool first = true;
03120     for (QValueList<Person>::ConstIterator it = begin();  it != end();  ++it)
03121     {
03122         if (first)
03123             first = false;
03124         else
03125             result += separator;
03126 
03127         bool quote = false;
03128         QString name = (*it).name();
03129         if (!name.isEmpty())
03130         {
03131             // Need to enclose the name in quotes if it has any special characters
03132             int len = name.length();
03133             for (int i = 0;  i < len;  ++i)
03134             {
03135                 QChar ch = name[i];
03136                 if (!ch.isLetterOrNumber())
03137                 {
03138                     quote = true;
03139                     result += '\"';
03140                     break;
03141                 }
03142             }
03143             result += (*it).name();
03144             result += (quote ? "\" <" : " <");
03145             quote = true;    // need angle brackets round email address
03146         }
03147 
03148         result += (*it).email();
03149         if (quote)
03150             result += ">";
03151     }
03152     return result;
03153 }
03154 
03155 
03156 /*=============================================================================
03157 = Static functions
03158 =============================================================================*/
03159 
03160 /******************************************************************************
03161  * Set the specified alarm to be a procedure alarm with the given command line.
03162  * The command line is first split into its program file and arguments before
03163  * initialising the alarm.
03164  */
03165 static void setProcedureAlarm(Alarm* alarm, const QString& commandLine)
03166 {
03167     QString command   = QString::null;
03168     QString arguments = QString::null;
03169     QChar quoteChar;
03170     bool quoted = false;
03171     uint posMax = commandLine.length();
03172     uint pos;
03173     for (pos = 0;  pos < posMax;  ++pos)
03174     {
03175         QChar ch = commandLine[pos];
03176         if (quoted)
03177         {
03178             if (ch == quoteChar)
03179             {
03180                 ++pos;    // omit the quote character
03181                 break;
03182             }
03183             command += ch;
03184         }
03185         else
03186         {
03187             bool done = false;
03188             switch (ch)
03189             {
03190                 case ' ':
03191                 case ';':
03192                 case '|':
03193                 case '<':
03194                 case '>':
03195                     done = !command.isEmpty();
03196                     break;
03197                 case '\'':
03198                 case '"':
03199                     if (command.isEmpty())
03200                     {
03201                         // Start of a quoted string. Omit the quote character.
03202                         quoted = true;
03203                         quoteChar = ch;
03204                         break;
03205                     }
03206                     // fall through to default
03207                 default:
03208                     command += ch;
03209                     break;
03210             }
03211             if (done)
03212                 break;
03213         }
03214     }
03215 
03216     // Skip any spaces after the command
03217     for ( ;  pos < posMax  &&  commandLine[pos] == ' ';  ++pos) ;
03218     arguments = commandLine.mid(pos);
03219 
03220     alarm->setProcedureAlarm(command, arguments);
03221 }
KDE Home | KDE Accessibility Home | Description of Access Keys