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