kalarm

alarmevent.cpp
1/*
2 * alarmevent.cpp - represents calendar alarms and events
3 * Program: kalarm
4 * Copyright © 2001-2009 by David Jarvie <djarvie@kde.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include "kalarm.h"
22
23#include <stdlib.h>
24#include <time.h>
25#include <ctype.h>
26#include <tqcolor.h>
27#include <tqregexp.h>
28
29#include <tdelocale.h>
30#include <kdebug.h>
31
32#include "alarmtext.h"
33#include "functions.h"
34#include "kalarmapp.h"
35#include "kamail.h"
36#include "preferences.h"
37#include "alarmcalendar.h"
38#include "alarmevent.h"
39using namespace KCal;
40
41
42const TQCString APPNAME("KALARM");
43
44// KAlarm version which first used the current calendar/event format.
45// If this changes, KAEvent::convertKCalEvents() must be changed correspondingly.
46// The string version is the KAlarm version string used in the calendar file.
47TQString KAEvent::calVersionString() { return TQString::fromLatin1("1.5.0"); }
48int KAEvent::calVersion() { return KAlarm::Version(1,5,0); }
49
50// Custom calendar properties.
51// Note that all custom property names are prefixed with X-TDE-KALARM- in the calendar file.
52// - Event properties
53static const TQCString NEXT_RECUR_PROPERTY("NEXTRECUR"); // X-TDE-KALARM-NEXTRECUR property
54static const TQCString REPEAT_PROPERTY("REPEAT"); // X-TDE-KALARM-REPEAT property
55// - General alarm properties
56static const TQCString TYPE_PROPERTY("TYPE"); // X-TDE-KALARM-TYPE property
57static const TQString FILE_TYPE = TQString::fromLatin1("FILE");
58static const TQString AT_LOGIN_TYPE = TQString::fromLatin1("LOGIN");
59static const TQString REMINDER_TYPE = TQString::fromLatin1("REMINDER");
60static const TQString REMINDER_ONCE_TYPE = TQString::fromLatin1("REMINDER_ONCE");
61static const TQString ARCHIVE_REMINDER_ONCE_TYPE = TQString::fromLatin1("ONCE");
62static const TQString TIME_DEFERRAL_TYPE = TQString::fromLatin1("DEFERRAL");
63static const TQString DATE_DEFERRAL_TYPE = TQString::fromLatin1("DATE_DEFERRAL");
64static const TQString DISPLAYING_TYPE = TQString::fromLatin1("DISPLAYING"); // used only in displaying calendar
65static const TQString PRE_ACTION_TYPE = TQString::fromLatin1("PRE");
66static const TQString POST_ACTION_TYPE = TQString::fromLatin1("POST");
67static const TQCString NEXT_REPEAT_PROPERTY("NEXTREPEAT"); // X-TDE-KALARM-NEXTREPEAT property
68// - Display alarm properties
69static const TQCString FONT_COLOUR_PROPERTY("FONTCOLOR"); // X-TDE-KALARM-FONTCOLOR property
70// - Email alarm properties
71static const TQCString EMAIL_ID_PROPERTY("EMAILID"); // X-TDE-KALARM-EMAILID property
72// - Audio alarm properties
73static const TQCString VOLUME_PROPERTY("VOLUME"); // X-TDE-KALARM-VOLUME property
74static const TQCString SPEAK_PROPERTY("SPEAK"); // X-TDE-KALARM-SPEAK property
75
76// Event categories
77static const TQString DATE_ONLY_CATEGORY = TQString::fromLatin1("DATE");
78static const TQString EMAIL_BCC_CATEGORY = TQString::fromLatin1("BCC");
79static const TQString CONFIRM_ACK_CATEGORY = TQString::fromLatin1("ACKCONF");
80static const TQString LATE_CANCEL_CATEGORY = TQString::fromLatin1("LATECANCEL;");
81static const TQString AUTO_CLOSE_CATEGORY = TQString::fromLatin1("LATECLOSE;");
82static const TQString TEMPL_AFTER_TIME_CATEGORY = TQString::fromLatin1("TMPLAFTTIME;");
83static const TQString KMAIL_SERNUM_CATEGORY = TQString::fromLatin1("KMAIL:");
84static const TQString KORGANIZER_CATEGORY = TQString::fromLatin1("KORG");
85static const TQString DEFER_CATEGORY = TQString::fromLatin1("DEFER;");
86static const TQString ARCHIVE_CATEGORY = TQString::fromLatin1("SAVE");
87static const TQString ARCHIVE_CATEGORIES = TQString::fromLatin1("SAVE:");
88static const TQString LOG_CATEGORY = TQString::fromLatin1("LOG:");
89static const TQString xtermURL = TQString::fromLatin1("xterm:");
90
91// Event status strings
92static const TQString DISABLED_STATUS = TQString::fromLatin1("DISABLED");
93
94static const TQString EXPIRED_UID = TQString::fromLatin1("-exp-");
95static const TQString DISPLAYING_UID = TQString::fromLatin1("-disp-");
96static const TQString TEMPLATE_UID = TQString::fromLatin1("-tmpl-");
97static const TQString KORGANIZER_UID = TQString::fromLatin1("-korg-");
98
99struct AlarmData
100{
101 const Alarm* alarm;
102 TQString cleanText; // text or audio file name
103 uint emailFromId;
104 EmailAddressList emailAddresses;
105 TQString emailSubject;
106 TQStringList emailAttachments;
107 TQFont font;
108 TQColor bgColour, fgColour;
109 float soundVolume;
110 float fadeVolume;
111 int fadeSeconds;
112 int startOffsetSecs;
113 bool speak;
114 KAAlarm::SubType type;
115 KAAlarmEventBase::Type action;
116 int displayingFlags;
117 bool defaultFont;
118 bool reminderOnceOnly;
119 bool isEmailText;
120 bool commandScript;
121 int repeatCount;
122 int repeatInterval;
123 int nextRepeat;
124};
125typedef TQMap<KAAlarm::SubType, AlarmData> AlarmMap;
126
127static void setProcedureAlarm(Alarm*, const TQString& commandLine);
128
129
130/*=============================================================================
131= Class KAEvent
132= Corresponds to a KCal::Event instance.
133=============================================================================*/
134
135inline void KAEvent::set_deferral(DeferType type)
136{
137 if (type)
138 {
139 if (!mDeferral)
140 ++mAlarmCount;
141 }
142 else
143 {
144 if (mDeferral)
145 --mAlarmCount;
146 }
147 mDeferral = type;
148}
149
150inline void KAEvent::set_reminder(int minutes)
151{
152 if (minutes && !mReminderMinutes)
153 ++mAlarmCount;
154 else if (!minutes && mReminderMinutes)
155 --mAlarmCount;
156 mReminderMinutes = minutes;
157 mArchiveReminderMinutes = 0;
158}
159
160inline void KAEvent::set_archiveReminder()
161{
162 if (mReminderMinutes)
163 --mAlarmCount;
164 mArchiveReminderMinutes = mReminderMinutes;
165 mReminderMinutes = 0;
166}
167
168
169void KAEvent::copy(const KAEvent& event)
170{
171 KAAlarmEventBase::copy(event);
172 mTemplateName = event.mTemplateName;
173 mAudioFile = event.mAudioFile;
174 mPreAction = event.mPreAction;
175 mPostAction = event.mPostAction;
176 mStartDateTime = event.mStartDateTime;
177 mSaveDateTime = event.mSaveDateTime;
178 mAtLoginDateTime = event.mAtLoginDateTime;
179 mDeferralTime = event.mDeferralTime;
180 mDisplayingTime = event.mDisplayingTime;
181 mDisplayingFlags = event.mDisplayingFlags;
182 mReminderMinutes = event.mReminderMinutes;
183 mArchiveReminderMinutes = event.mArchiveReminderMinutes;
184 mDeferDefaultMinutes = event.mDeferDefaultMinutes;
185 mRevision = event.mRevision;
186 mAlarmCount = event.mAlarmCount;
187 mDeferral = event.mDeferral;
188 mLogFile = event.mLogFile;
189 mCommandXterm = event.mCommandXterm;
190 mKMailSerialNumber = event.mKMailSerialNumber;
191 mCopyToKOrganizer = event.mCopyToKOrganizer;
192 mReminderOnceOnly = event.mReminderOnceOnly;
193 mMainExpired = event.mMainExpired;
194 mArchiveRepeatAtLogin = event.mArchiveRepeatAtLogin;
195 mArchive = event.mArchive;
196 mTemplateAfterTime = event.mTemplateAfterTime;
197 mEnabled = event.mEnabled;
198 mUpdated = event.mUpdated;
199 delete mRecurrence;
200 if (event.mRecurrence)
201 mRecurrence = new KARecurrence(*event.mRecurrence);
202 else
203 mRecurrence = 0;
204}
205
206/******************************************************************************
207 * Initialise the KAEvent from a KCal::Event.
208 */
209void KAEvent::set(const Event& event)
210{
211 // Extract status from the event
212 mEventID = event.uid();
213 mRevision = event.revision();
214 mTemplateName = TQString();
215 mLogFile = TQString();
216 mTemplateAfterTime = -1;
217 mBeep = false;
218 mSpeak = false;
219 mEmailBcc = false;
220 mCommandXterm = false;
221 mCopyToKOrganizer = false;
222 mConfirmAck = false;
223 mArchive = false;
224 mReminderOnceOnly = false;
225 mAutoClose = false;
226 mArchiveRepeatAtLogin = false;
227 mArchiveReminderMinutes = 0;
228 mDeferDefaultMinutes = 0;
229 mLateCancel = 0;
230 mKMailSerialNumber = 0;
231 mBgColour = TQColor(255, 255, 255); // missing/invalid colour - return white background
232 mFgColour = TQColor(0, 0, 0); // and black foreground
233 mDefaultFont = true;
234 mEnabled = true;
235 clearRecur();
236 bool ok;
237 bool dateOnly = false;
238 const TQStringList cats = event.categories();
239 for (unsigned int i = 0; i < cats.count(); ++i)
240 {
241 if (cats[i] == DATE_ONLY_CATEGORY)
242 dateOnly = true;
243 else if (cats[i] == CONFIRM_ACK_CATEGORY)
244 mConfirmAck = true;
245 else if (cats[i] == EMAIL_BCC_CATEGORY)
246 mEmailBcc = true;
247 else if (cats[i] == ARCHIVE_CATEGORY)
248 mArchive = true;
249 else if (cats[i] == KORGANIZER_CATEGORY)
250 mCopyToKOrganizer = true;
251 else if (cats[i].startsWith(KMAIL_SERNUM_CATEGORY))
252 mKMailSerialNumber = cats[i].mid(KMAIL_SERNUM_CATEGORY.length()).toULong();
253 else if (cats[i].startsWith(LOG_CATEGORY))
254 {
255 TQString logUrl = cats[i].mid(LOG_CATEGORY.length());
256 if (logUrl == xtermURL)
257 mCommandXterm = true;
258 else
259 mLogFile = logUrl;
260 }
261 else if (cats[i].startsWith(ARCHIVE_CATEGORIES))
262 {
263 // It's the archive flag plus a reminder time and/or repeat-at-login flag
264 mArchive = true;
265 TQStringList list = TQStringList::split(';', cats[i].mid(ARCHIVE_CATEGORIES.length()));
266 for (unsigned int j = 0; j < list.count(); ++j)
267 {
268 if (list[j] == AT_LOGIN_TYPE)
269 mArchiveRepeatAtLogin = true;
270 else if (list[j] == ARCHIVE_REMINDER_ONCE_TYPE)
271 mReminderOnceOnly = true;
272 else
273 {
274 char ch;
275 const char* cat = list[j].latin1();
276 while ((ch = *cat) != 0 && (ch < '0' || ch > '9'))
277 ++cat;
278 if (ch)
279 {
280 mArchiveReminderMinutes = ch - '0';
281 while ((ch = *++cat) >= '0' && ch <= '9')
282 mArchiveReminderMinutes = mArchiveReminderMinutes * 10 + ch - '0';
283 switch (ch)
284 {
285 case 'M': break;
286 case 'H': mArchiveReminderMinutes *= 60; break;
287 case 'D': mArchiveReminderMinutes *= 1440; break;
288 }
289 }
290 }
291 }
292 }
293 else if (cats[i].startsWith(DEFER_CATEGORY))
294 {
295 mDeferDefaultMinutes = static_cast<int>(cats[i].mid(DEFER_CATEGORY.length()).toUInt(&ok));
296 if (!ok)
297 mDeferDefaultMinutes = 0; // invalid parameter
298 }
299 else if (cats[i].startsWith(TEMPL_AFTER_TIME_CATEGORY))
300 {
301 mTemplateAfterTime = static_cast<int>(cats[i].mid(TEMPL_AFTER_TIME_CATEGORY.length()).toUInt(&ok));
302 if (!ok)
303 mTemplateAfterTime = -1; // invalid parameter
304 }
305 else if (cats[i].startsWith(LATE_CANCEL_CATEGORY))
306 {
307 mLateCancel = static_cast<int>(cats[i].mid(LATE_CANCEL_CATEGORY.length()).toUInt(&ok));
308 if (!ok || !mLateCancel)
309 mLateCancel = 1; // invalid parameter defaults to 1 minute
310 }
311 else if (cats[i].startsWith(AUTO_CLOSE_CATEGORY))
312 {
313 mLateCancel = static_cast<int>(cats[i].mid(AUTO_CLOSE_CATEGORY.length()).toUInt(&ok));
314 if (!ok || !mLateCancel)
315 mLateCancel = 1; // invalid parameter defaults to 1 minute
316 mAutoClose = true;
317 }
318 }
319 TQString prop = event.customProperty(APPNAME, REPEAT_PROPERTY);
320 if (!prop.isEmpty())
321 {
322 // This property is used when the main alarm has expired
323 TQStringList list = TQStringList::split(':', prop);
324 if (list.count() >= 2)
325 {
326 int interval = static_cast<int>(list[0].toUInt());
327 int count = static_cast<int>(list[1].toUInt());
328 if (interval && count)
329 {
330 mRepeatInterval = interval;
331 mRepeatCount = count;
332 }
333 }
334 }
335 mNextMainDateTime = readDateTime(event, dateOnly, mStartDateTime);
336 mSaveDateTime = event.created();
337 if (uidStatus() == TEMPLATE)
338 mTemplateName = event.summary();
339 if (event.statusStr() == DISABLED_STATUS)
340 mEnabled = false;
341
342 // Extract status from the event's alarms.
343 // First set up defaults.
344 mActionType = T_MESSAGE;
345 mMainExpired = true;
346 mRepeatAtLogin = false;
347 mDisplaying = false;
348 mRepeatSound = false;
349 mCommandScript = false;
350 mDeferral = NO_DEFERRAL;
351 mSoundVolume = -1;
352 mFadeVolume = -1;
353 mFadeSeconds = 0;
354 mReminderMinutes = 0;
355 mEmailFromIdentity = 0;
356 mText = "";
357 mAudioFile = "";
358 mPreAction = "";
359 mPostAction = "";
360 mEmailSubject = "";
361 mEmailAddresses.clear();
362 mEmailAttachments.clear();
363
364 // Extract data from all the event's alarms and index the alarms by sequence number
365 AlarmMap alarmMap;
366 readAlarms(event, &alarmMap);
367
368 // Incorporate the alarms' details into the overall event
369 mAlarmCount = 0; // initialise as invalid
370 DateTime alTime;
371 bool set = false;
372 bool isEmailText = false;
373 bool setDeferralTime = false;
374 Duration deferralOffset;
375 for (AlarmMap::ConstIterator it = alarmMap.begin(); it != alarmMap.end(); ++it)
376 {
377 const AlarmData& data = it.data();
378 DateTime dateTime = data.alarm->hasStartOffset() ? mNextMainDateTime.addSecs(data.alarm->startOffset().asSeconds()) : data.alarm->time();
379 switch (data.type)
380 {
381 case KAAlarm::MAIN__ALARM:
382 mMainExpired = false;
383 alTime = dateTime;
384 alTime.setDateOnly(mStartDateTime.isDateOnly());
385 if (data.repeatCount && data.repeatInterval)
386 {
387 mRepeatInterval = data.repeatInterval; // values may be adjusted in setRecurrence()
388 mRepeatCount = data.repeatCount;
389 mNextRepeat = data.nextRepeat;
390 }
391 break;
392 case KAAlarm::AT_LOGIN__ALARM:
393 mRepeatAtLogin = true;
394 mAtLoginDateTime = dateTime.rawDateTime();
395 alTime = mAtLoginDateTime;
396 break;
397 case KAAlarm::REMINDER__ALARM:
398 mReminderMinutes = -(data.startOffsetSecs / 60);
399 if (mReminderMinutes)
400 mArchiveReminderMinutes = 0;
401 break;
402 case KAAlarm::DEFERRED_REMINDER_DATE__ALARM:
403 case KAAlarm::DEFERRED_DATE__ALARM:
404 mDeferral = (data.type == KAAlarm::DEFERRED_REMINDER_DATE__ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL;
405 mDeferralTime = dateTime;
406 mDeferralTime.setDateOnly(true);
407 if (data.alarm->hasStartOffset())
408 deferralOffset = data.alarm->startOffset();
409 break;
410 case KAAlarm::DEFERRED_REMINDER_TIME__ALARM:
411 case KAAlarm::DEFERRED_TIME__ALARM:
412 mDeferral = (data.type == KAAlarm::DEFERRED_REMINDER_TIME__ALARM) ? REMINDER_DEFERRAL : NORMAL_DEFERRAL;
413 mDeferralTime = dateTime;
414 if (data.alarm->hasStartOffset())
415 deferralOffset = data.alarm->startOffset();
416 break;
417 case KAAlarm::DISPLAYING__ALARM:
418 {
419 mDisplaying = true;
420 mDisplayingFlags = data.displayingFlags;
421 bool dateOnly = (mDisplayingFlags & DEFERRAL) ? !(mDisplayingFlags & TIMED_FLAG)
422 : mStartDateTime.isDateOnly();
423 mDisplayingTime = dateTime;
424 mDisplayingTime.setDateOnly(dateOnly);
425 alTime = mDisplayingTime;
426 break;
427 }
428 case KAAlarm::AUDIO__ALARM:
429 mAudioFile = data.cleanText;
430 mSpeak = data.speak && mAudioFile.isEmpty();
431 mBeep = !mSpeak && mAudioFile.isEmpty();
432 mSoundVolume = (!mBeep && !mSpeak) ? data.soundVolume : -1;
433 mFadeVolume = (mSoundVolume >= 0 && data.fadeSeconds > 0) ? data.fadeVolume : -1;
434 mFadeSeconds = (mFadeVolume >= 0) ? data.fadeSeconds : 0;
435 mRepeatSound = (!mBeep && !mSpeak) && (data.repeatCount < 0);
436 break;
437 case KAAlarm::PRE_ACTION__ALARM:
438 mPreAction = data.cleanText;
439 break;
440 case KAAlarm::POST_ACTION__ALARM:
441 mPostAction = data.cleanText;
442 break;
443 case KAAlarm::INVALID__ALARM:
444 default:
445 break;
446 }
447
448 if (data.reminderOnceOnly)
449 mReminderOnceOnly = true;
450 bool noSetNextTime = false;
451 switch (data.type)
452 {
453 case KAAlarm::DEFERRED_REMINDER_DATE__ALARM:
454 case KAAlarm::DEFERRED_DATE__ALARM:
455 case KAAlarm::DEFERRED_REMINDER_TIME__ALARM:
456 case KAAlarm::DEFERRED_TIME__ALARM:
457 if (!set)
458 {
459 // The recurrence has to be evaluated before we can
460 // calculate the time of a deferral alarm.
461 setDeferralTime = true;
462 noSetNextTime = true;
463 }
464 // fall through to AT_LOGIN__ALARM etc.
465 case KAAlarm::AT_LOGIN__ALARM:
466 case KAAlarm::REMINDER__ALARM:
467 case KAAlarm::DISPLAYING__ALARM:
468 if (!set && !noSetNextTime)
469 mNextMainDateTime = alTime;
470 // fall through to MAIN__ALARM
471 case KAAlarm::MAIN__ALARM:
472 // Ensure that the basic fields are set up even if there is no main
473 // alarm in the event (if it has expired and then been deferred)
474 if (!set)
475 {
476 mActionType = data.action;
477 mText = (mActionType == T_COMMAND) ? data.cleanText.stripWhiteSpace() : data.cleanText;
478 switch (data.action)
479 {
480 case T_MESSAGE:
481 mFont = data.font;
482 mDefaultFont = data.defaultFont;
483 if (data.isEmailText)
484 isEmailText = true;
485 // fall through to T_FILE
486 case T_FILE:
487 mBgColour = data.bgColour;
488 mFgColour = data.fgColour;
489 break;
490 case T_COMMAND:
491 mCommandScript = data.commandScript;
492 break;
493 case T_EMAIL:
494 mEmailFromIdentity = data.emailFromId;
495 mEmailAddresses = data.emailAddresses;
496 mEmailSubject = data.emailSubject;
497 mEmailAttachments = data.emailAttachments;
498 break;
499 default:
500 break;
501 }
502 set = true;
503 }
504 if (data.action == T_FILE && mActionType == T_MESSAGE)
505 mActionType = T_FILE;
506 ++mAlarmCount;
507 break;
508 case KAAlarm::AUDIO__ALARM:
509 case KAAlarm::PRE_ACTION__ALARM:
510 case KAAlarm::POST_ACTION__ALARM:
511 case KAAlarm::INVALID__ALARM:
512 default:
513 break;
514 }
515 }
516 if (!isEmailText)
517 mKMailSerialNumber = 0;
518 if (mRepeatAtLogin)
519 mArchiveRepeatAtLogin = false;
520
521 Recurrence* recur = event.recurrence();
522 if (recur && recur->doesRecur())
523 {
524 int nextRepeat = mNextRepeat; // setRecurrence() clears mNextRepeat
525 setRecurrence(*recur);
526 if (nextRepeat <= mRepeatCount)
527 mNextRepeat = nextRepeat;
528 }
529 else
530 checkRepetition();
531
532 if (mMainExpired && deferralOffset.asSeconds() && checkRecur() != KARecurrence::NO_RECUR)
533 {
534 // Adjust the deferral time for an expired recurrence, since the
535 // offset is relative to the first actual occurrence.
536 DateTime dt = mRecurrence->getNextDateTime(mStartDateTime.dateTime().addDays(-1));
537 dt.setDateOnly(mStartDateTime.isDateOnly());
538 if (mDeferralTime.isDateOnly())
539 {
540 mDeferralTime = dt.addSecs(deferralOffset.asSeconds());
541 mDeferralTime.setDateOnly(true);
542 }
543 else
544 mDeferralTime = deferralOffset.end(dt.dateTime());
545 }
546 if (mDeferral)
547 {
548 if (mNextMainDateTime == mDeferralTime)
549 mDeferral = CANCEL_DEFERRAL; // it's a cancelled deferral
550 if (setDeferralTime)
551 mNextMainDateTime = mDeferralTime;
552 }
553
554 mUpdated = false;
555}
556
557/******************************************************************************
558* Fetch the start and next date/time for a KCal::Event.
559* Reply = next main date/time.
560*/
561DateTime KAEvent::readDateTime(const Event& event, bool dateOnly, DateTime& start)
562{
563 start.set(event.dtStart(), dateOnly);
564 DateTime next = start;
565 TQString prop = event.customProperty(APPNAME, NEXT_RECUR_PROPERTY);
566 if (prop.length() >= 8)
567 {
568 // The next due recurrence time is specified
569 TQDate d(prop.left(4).toInt(), prop.mid(4,2).toInt(), prop.mid(6,2).toInt());
570 if (d.isValid())
571 {
572 if (dateOnly && prop.length() == 8)
573 next = d;
574 else if (!dateOnly && prop.length() == 15 && prop[8] == TQChar('T'))
575 {
576 TQTime t(prop.mid(9,2).toInt(), prop.mid(11,2).toInt(), prop.mid(13,2).toInt());
577 if (t.isValid())
578 next = TQDateTime(d, t);
579 }
580 }
581 }
582 return next;
583}
584
585/******************************************************************************
586 * Parse the alarms for a KCal::Event.
587 * Reply = map of alarm data, indexed by KAAlarm::Type
588 */
589void KAEvent::readAlarms(const Event& event, void* almap)
590{
591 AlarmMap* alarmMap = (AlarmMap*)almap;
592 Alarm::List alarms = event.alarms();
593 for (Alarm::List::ConstIterator it = alarms.begin(); it != alarms.end(); ++it)
594 {
595 // Parse the next alarm's text
596 AlarmData data;
597 readAlarm(**it, data);
598 if (data.type != KAAlarm::INVALID__ALARM)
599 alarmMap->insert(data.type, data);
600 }
601}
602
603/******************************************************************************
604 * Parse a KCal::Alarm.
605 * Reply = alarm ID (sequence number)
606 */
607void KAEvent::readAlarm(const Alarm& alarm, AlarmData& data)
608{
609 // Parse the next alarm's text
610 data.alarm = &alarm;
611 data.startOffsetSecs = alarm.startOffset().asSeconds(); // can have start offset but no valid date/time (e.g. reminder in template)
612 data.displayingFlags = 0;
613 data.isEmailText = false;
614 data.nextRepeat = 0;
615 data.repeatInterval = alarm.snoozeTime();
616 data.repeatCount = alarm.repeatCount();
617 if (data.repeatCount)
618 {
619 bool ok;
620 TQString property = alarm.customProperty(APPNAME, NEXT_REPEAT_PROPERTY);
621 int n = static_cast<int>(property.toUInt(&ok));
622 if (ok)
623 data.nextRepeat = n;
624 }
625 switch (alarm.type())
626 {
627 case Alarm::Procedure:
628 data.action = T_COMMAND;
629 data.cleanText = alarm.programFile();
630 data.commandScript = data.cleanText.isEmpty(); // blank command indicates a script
631 if (!alarm.programArguments().isEmpty())
632 {
633 if (!data.commandScript)
634 data.cleanText += ' ';
635 data.cleanText += alarm.programArguments();
636 }
637 break;
638 case Alarm::Email:
639 data.action = T_EMAIL;
640 data.emailFromId = alarm.customProperty(APPNAME, EMAIL_ID_PROPERTY).toUInt();
641 data.emailAddresses = alarm.mailAddresses();
642 data.emailSubject = alarm.mailSubject();
643 data.emailAttachments = alarm.mailAttachments();
644 data.cleanText = alarm.mailText();
645 break;
646 case Alarm::Display:
647 {
648 data.action = T_MESSAGE;
649 data.cleanText = AlarmText::fromCalendarText(alarm.text(), data.isEmailText);
650 TQString property = alarm.customProperty(APPNAME, FONT_COLOUR_PROPERTY);
651 TQStringList list = TQStringList::split(TQChar(';'), property, true);
652 data.bgColour = TQColor(255, 255, 255); // white
653 data.fgColour = TQColor(0, 0, 0); // black
654 int n = list.count();
655 if (n > 0)
656 {
657 if (!list[0].isEmpty())
658 {
659 TQColor c(list[0]);
660 if (c.isValid())
661 data.bgColour = c;
662 }
663 if (n > 1 && !list[1].isEmpty())
664 {
665 TQColor c(list[1]);
666 if (c.isValid())
667 data.fgColour = c;
668 }
669 }
670 data.defaultFont = (n <= 2 || list[2].isEmpty());
671 if (!data.defaultFont)
672 data.font.fromString(list[2]);
673 break;
674 }
675 case Alarm::Audio:
676 {
677 data.action = T_AUDIO;
678 data.cleanText = alarm.audioFile();
679 data.type = KAAlarm::AUDIO__ALARM;
680 data.soundVolume = -1;
681 data.fadeVolume = -1;
682 data.fadeSeconds = 0;
683 data.speak = !alarm.customProperty(APPNAME, SPEAK_PROPERTY).isNull();
684 TQString property = alarm.customProperty(APPNAME, VOLUME_PROPERTY);
685 if (!property.isEmpty())
686 {
687 bool ok;
688 float fadeVolume;
689 int fadeSecs = 0;
690 TQStringList list = TQStringList::split(TQChar(';'), property, true);
691 data.soundVolume = list[0].toFloat(&ok);
692 if (!ok)
693 data.soundVolume = -1;
694 if (data.soundVolume >= 0 && list.count() >= 3)
695 {
696 fadeVolume = list[1].toFloat(&ok);
697 if (ok)
698 fadeSecs = static_cast<int>(list[2].toUInt(&ok));
699 if (ok && fadeVolume >= 0 && fadeSecs > 0)
700 {
701 data.fadeVolume = fadeVolume;
702 data.fadeSeconds = fadeSecs;
703 }
704 }
705 }
706 return;
707 }
708 case Alarm::Invalid:
709 data.type = KAAlarm::INVALID__ALARM;
710 return;
711 }
712
713 bool atLogin = false;
714 bool reminder = false;
715 bool deferral = false;
716 bool dateDeferral = false;
717 data.reminderOnceOnly = false;
718 data.type = KAAlarm::MAIN__ALARM;
719 TQString property = alarm.customProperty(APPNAME, TYPE_PROPERTY);
720 TQStringList types = TQStringList::split(TQChar(','), property);
721 for (unsigned int i = 0; i < types.count(); ++i)
722 {
723 TQString type = types[i];
724 if (type == AT_LOGIN_TYPE)
725 atLogin = true;
726 else if (type == FILE_TYPE && data.action == T_MESSAGE)
727 data.action = T_FILE;
728 else if (type == REMINDER_TYPE)
729 reminder = true;
730 else if (type == REMINDER_ONCE_TYPE)
731 reminder = data.reminderOnceOnly = true;
732 else if (type == TIME_DEFERRAL_TYPE)
733 deferral = true;
734 else if (type == DATE_DEFERRAL_TYPE)
735 dateDeferral = deferral = true;
736 else if (type == DISPLAYING_TYPE)
737 data.type = KAAlarm::DISPLAYING__ALARM;
738 else if (type == PRE_ACTION_TYPE && data.action == T_COMMAND)
739 data.type = KAAlarm::PRE_ACTION__ALARM;
740 else if (type == POST_ACTION_TYPE && data.action == T_COMMAND)
741 data.type = KAAlarm::POST_ACTION__ALARM;
742 }
743
744 if (reminder)
745 {
746 if (data.type == KAAlarm::MAIN__ALARM)
747 data.type = dateDeferral ? KAAlarm::DEFERRED_REMINDER_DATE__ALARM
748 : deferral ? KAAlarm::DEFERRED_REMINDER_TIME__ALARM : KAAlarm::REMINDER__ALARM;
749 else if (data.type == KAAlarm::DISPLAYING__ALARM)
750 data.displayingFlags = dateDeferral ? REMINDER | DATE_DEFERRAL
751 : deferral ? REMINDER | TIME_DEFERRAL : REMINDER;
752 }
753 else if (deferral)
754 {
755 if (data.type == KAAlarm::MAIN__ALARM)
756 data.type = dateDeferral ? KAAlarm::DEFERRED_DATE__ALARM : KAAlarm::DEFERRED_TIME__ALARM;
757 else if (data.type == KAAlarm::DISPLAYING__ALARM)
758 data.displayingFlags = dateDeferral ? DATE_DEFERRAL : TIME_DEFERRAL;
759 }
760 if (atLogin)
761 {
762 if (data.type == KAAlarm::MAIN__ALARM)
763 data.type = KAAlarm::AT_LOGIN__ALARM;
764 else if (data.type == KAAlarm::DISPLAYING__ALARM)
765 data.displayingFlags = REPEAT_AT_LOGIN;
766 }
767//kdDebug(5950)<<"ReadAlarm(): text="<<alarm.text()<<", time="<<alarm.time().toString()<<", valid time="<<alarm.time().isValid()<<endl;
768}
769
770/******************************************************************************
771 * Initialise the KAEvent with the specified parameters.
772 */
773void KAEvent::set(const TQDateTime& dateTime, const TQString& text, const TQColor& bg, const TQColor& fg,
774 const TQFont& font, Action action, int lateCancel, int flags)
775{
776 clearRecur();
777 mStartDateTime.set(dateTime, flags & ANY_TIME);
778 mNextMainDateTime = mStartDateTime;
779 switch (action)
780 {
781 case MESSAGE:
782 case FILE:
783 case COMMAND:
784 case EMAIL:
785 mActionType = (KAAlarmEventBase::Type)action;
786 break;
787 default:
788 mActionType = T_MESSAGE;
789 break;
790 }
791 mText = (mActionType == T_COMMAND) ? text.stripWhiteSpace() : text;
792 mEventID = TQString();
793 mTemplateName = TQString();
794 mPreAction = TQString();
795 mPostAction = TQString();
796 mAudioFile = "";
797 mSoundVolume = -1;
798 mFadeVolume = -1;
799 mTemplateAfterTime = -1;
800 mFadeSeconds = 0;
801 mBgColour = bg;
802 mFgColour = fg;
803 mFont = font;
804 mAlarmCount = 1;
805 mLateCancel = lateCancel; // do this before setting flags
806 mDeferral = NO_DEFERRAL; // do this before setting flags
807
808 KAAlarmEventBase::set(flags & ~READ_ONLY_FLAGS);
809 mStartDateTime.setDateOnly(flags & ANY_TIME);
810 set_deferral((flags & DEFERRAL) ? NORMAL_DEFERRAL : NO_DEFERRAL);
811 mCommandXterm = flags & EXEC_IN_XTERM;
812 mCopyToKOrganizer = flags & COPY_KORGANIZER;
813 mEnabled = !(flags & DISABLED);
814
815 mKMailSerialNumber = 0;
816 mReminderMinutes = 0;
817 mArchiveReminderMinutes = 0;
818 mDeferDefaultMinutes = 0;
819 mArchiveRepeatAtLogin = false;
820 mReminderOnceOnly = false;
821 mDisplaying = false;
822 mMainExpired = false;
823 mArchive = false;
824 mUpdated = false;
825}
826
827void KAEvent::setLogFile(const TQString& logfile)
828{
829 mLogFile = logfile;
830 if (!logfile.isEmpty())
831 mCommandXterm = false;
832}
833
834void KAEvent::setEmail(uint from, const EmailAddressList& addresses, const TQString& subject, const TQStringList& attachments)
835{
836 mEmailFromIdentity = from;
837 mEmailAddresses = addresses;
838 mEmailSubject = subject;
839 mEmailAttachments = attachments;
840}
841
842void KAEvent::setAudioFile(const TQString& filename, float volume, float fadeVolume, int fadeSeconds)
843{
844 mAudioFile = filename;
845 mSoundVolume = filename.isEmpty() ? -1 : volume;
846 if (mSoundVolume >= 0)
847 {
848 mFadeVolume = (fadeSeconds > 0) ? fadeVolume : -1;
849 mFadeSeconds = (mFadeVolume >= 0) ? fadeSeconds : 0;
850 }
851 else
852 {
853 mFadeVolume = -1;
854 mFadeSeconds = 0;
855 }
856 mUpdated = true;
857}
858
859void KAEvent::setReminder(int minutes, bool onceOnly)
860{
861 if (minutes != mReminderMinutes)
862 {
863 set_reminder(minutes);
864 mReminderOnceOnly = onceOnly;
865 mUpdated = true;
866 }
867}
868
869/******************************************************************************
870 * Return the time of the next scheduled occurrence of the event.
871 * Reminders and deferred reminders can optionally be ignored.
872 */
873DateTime KAEvent::displayDateTime() const
874{
875 DateTime dt = mainDateTime(true);
876 if (mDeferral > 0 && mDeferral != REMINDER_DEFERRAL)
877 {
878 if (mMainExpired)
879 return mDeferralTime;
880 return TQMIN(mDeferralTime, dt);
881 }
882 return dt;
883}
884
885/******************************************************************************
886 * Convert a unique ID to indicate that the event is in a specified calendar file.
887 */
888TQString KAEvent::uid(const TQString& id, Status status)
889{
890 TQString result = id;
891 Status oldStatus;
892 int i, len;
893 if ((i = result.find(EXPIRED_UID)) > 0)
894 {
895 oldStatus = EXPIRED;
896 len = EXPIRED_UID.length();
897 }
898 else if ((i = result.find(DISPLAYING_UID)) > 0)
899 {
900 oldStatus = DISPLAYING;
901 len = DISPLAYING_UID.length();
902 }
903 else if ((i = result.find(TEMPLATE_UID)) > 0)
904 {
905 oldStatus = TEMPLATE;
906 len = TEMPLATE_UID.length();
907 }
908 else if ((i = result.find(KORGANIZER_UID)) > 0)
909 {
910 oldStatus = KORGANIZER;
911 len = KORGANIZER_UID.length();
912 }
913 else
914 {
915 oldStatus = ACTIVE;
916 i = result.findRev('-');
917 len = 1;
918 }
919 if (status != oldStatus && i > 0)
920 {
921 TQString part;
922 switch (status)
923 {
924 case ACTIVE: part = "-"; break;
925 case EXPIRED: part = EXPIRED_UID; break;
926 case DISPLAYING: part = DISPLAYING_UID; break;
927 case TEMPLATE: part = TEMPLATE_UID; break;
928 case KORGANIZER: part = KORGANIZER_UID; break;
929 }
930 result.replace(i, len, part);
931 }
932 return result;
933}
934
935/******************************************************************************
936 * Get the calendar type for a unique ID.
937 */
938KAEvent::Status KAEvent::uidStatus(const TQString& uid)
939{
940 if (uid.find(EXPIRED_UID) > 0)
941 return EXPIRED;
942 if (uid.find(DISPLAYING_UID) > 0)
943 return DISPLAYING;
944 if (uid.find(TEMPLATE_UID) > 0)
945 return TEMPLATE;
946 if (uid.find(KORGANIZER_UID) > 0)
947 return KORGANIZER;
948 return ACTIVE;
949}
950
951int KAEvent::flags() const
952{
953 return KAAlarmEventBase::flags()
954 | (mStartDateTime.isDateOnly() ? ANY_TIME : 0)
955 | (mDeferral > 0 ? DEFERRAL : 0)
956 | (mCommandXterm ? EXEC_IN_XTERM : 0)
957 | (mCopyToKOrganizer ? COPY_KORGANIZER : 0)
958 | (mEnabled ? 0 : DISABLED);
959}
960
961/******************************************************************************
962 * Create a new Event from the KAEvent data.
963 */
964Event* KAEvent::event() const
965{
966 KCal::Event* ev = new KCal::Event;
967 ev->setUid(mEventID);
968 updateKCalEvent(*ev, false);
969 return ev;
970}
971
972/******************************************************************************
973 * Update an existing KCal::Event with the KAEvent data.
974 * If 'original' is true, the event start date/time is adjusted to its original
975 * value instead of its next occurrence, and the expired main alarm is
976 * reinstated.
977 */
978bool KAEvent::updateKCalEvent(Event& ev, bool checkUid, bool original, bool cancelCancelledDefer) const
979{
980 if ((checkUid && !mEventID.isEmpty() && mEventID != ev.uid())
981 || (!mAlarmCount && (!original || !mMainExpired)))
982 return false;
983
984 checkRecur(); // ensure recurrence/repetition data is consistent
985 bool readOnly = ev.isReadOnly();
986 ev.setReadOnly(false);
987 ev.setTransparency(Event::Transparent);
988
989 // Set up event-specific data
990
991 // Set up custom properties.
992 ev.removeCustomProperty(APPNAME, NEXT_RECUR_PROPERTY);
993 ev.removeCustomProperty(APPNAME, REPEAT_PROPERTY);
994
995 TQStringList cats;
996 if (mStartDateTime.isDateOnly())
997 cats.append(DATE_ONLY_CATEGORY);
998 if (mConfirmAck)
999 cats.append(CONFIRM_ACK_CATEGORY);
1000 if (mEmailBcc)
1001 cats.append(EMAIL_BCC_CATEGORY);
1002 if (mKMailSerialNumber)
1003 cats.append(TQString("%1%2").arg(KMAIL_SERNUM_CATEGORY).arg(mKMailSerialNumber));
1004 if (mCopyToKOrganizer)
1005 cats.append(KORGANIZER_CATEGORY);
1006 if (mCommandXterm)
1007 cats.append(LOG_CATEGORY + xtermURL);
1008 else if (!mLogFile.isEmpty())
1009 cats.append(LOG_CATEGORY + mLogFile);
1010 if (mLateCancel)
1011 cats.append(TQString("%1%2").arg(mAutoClose ? AUTO_CLOSE_CATEGORY : LATE_CANCEL_CATEGORY).arg(mLateCancel));
1012 if (mDeferDefaultMinutes)
1013 cats.append(TQString("%1%2").arg(DEFER_CATEGORY).arg(mDeferDefaultMinutes));
1014 if (!mTemplateName.isEmpty() && mTemplateAfterTime >= 0)
1015 cats.append(TQString("%1%2").arg(TEMPL_AFTER_TIME_CATEGORY).arg(mTemplateAfterTime));
1016 if (mArchive && !original)
1017 {
1018 TQStringList params;
1019 if (mArchiveReminderMinutes)
1020 {
1021 if (mReminderOnceOnly)
1022 params += ARCHIVE_REMINDER_ONCE_TYPE;
1023 char unit = 'M';
1024 int count = mArchiveReminderMinutes;
1025 if (count % 1440 == 0)
1026 {
1027 unit = 'D';
1028 count /= 1440;
1029 }
1030 else if (count % 60 == 0)
1031 {
1032 unit = 'H';
1033 count /= 60;
1034 }
1035 params += TQString("%1%2").arg(count).arg(unit);
1036 }
1037 if (mArchiveRepeatAtLogin)
1038 params += AT_LOGIN_TYPE;
1039 if (params.count() > 0)
1040 {
1041 TQString cat = ARCHIVE_CATEGORIES;
1042 cat += params.join(TQString::fromLatin1(";"));
1043 cats.append(cat);
1044 }
1045 else
1046 cats.append(ARCHIVE_CATEGORY);
1047 }
1048 ev.setCategories(cats);
1049 ev.setCustomStatus(mEnabled ? TQString() : DISABLED_STATUS);
1050 ev.setRevision(mRevision);
1051 ev.clearAlarms();
1052
1053 // Always set DTSTART as date/time, since alarm times can only be specified
1054 // in local time (instead of UTC) if they are relative to a DTSTART or DTEND
1055 // which is also specified in local time. Instead of calling setFloats() to
1056 // indicate a date-only event, the category "DATE" is included.
1057 ev.setDtStart(mStartDateTime.dateTime());
1058 ev.setFloats(false);
1059 ev.setHasEndDate(false);
1060
1061 DateTime dtMain = original ? mStartDateTime : mNextMainDateTime;
1062 int ancillaryType = 0; // 0 = invalid, 1 = time, 2 = offset
1063 DateTime ancillaryTime; // time for ancillary alarms (audio, pre-action, etc)
1064 int ancillaryOffset = 0; // start offset for ancillary alarms
1065 if (!mMainExpired || original)
1066 {
1067 /* The alarm offset must always be zero for the main alarm. To determine
1068 * which recurrence is due, the property X-TDE-KALARM_NEXTRECUR is used.
1069 * If the alarm offset was non-zero, exception dates and rules would not
1070 * work since they apply to the event time, not the alarm time.
1071 */
1072 if (!original && checkRecur() != KARecurrence::NO_RECUR)
1073 {
1074 TQDateTime dt = mNextMainDateTime.dateTime();
1075 ev.setCustomProperty(APPNAME, NEXT_RECUR_PROPERTY,
1076 dt.toString(mNextMainDateTime.isDateOnly() ? "yyyyMMdd" : "yyyyMMddThhmmss"));
1077 }
1078 // Add the main alarm
1079 initKCalAlarm(ev, 0, TQStringList(), KAAlarm::MAIN_ALARM);
1080 ancillaryOffset = 0;
1081 ancillaryType = dtMain.isValid() ? 2 : 0;
1082 }
1083 else if (mRepeatCount && mRepeatInterval)
1084 {
1085 // Alarm repetition is normally held in the main alarm, but since
1086 // the main alarm has expired, store in a custom property.
1087 TQString param = TQString("%1:%2").arg(mRepeatInterval).arg(mRepeatCount);
1088 ev.setCustomProperty(APPNAME, REPEAT_PROPERTY, param);
1089 }
1090
1091 // Add subsidiary alarms
1092 if (mRepeatAtLogin || (mArchiveRepeatAtLogin && original))
1093 {
1094 DateTime dtl;
1095 if (mArchiveRepeatAtLogin)
1096 dtl = mStartDateTime.dateTime().addDays(-1);
1097 else if (mAtLoginDateTime.isValid())
1098 dtl = mAtLoginDateTime;
1099 else if (mStartDateTime.isDateOnly())
1100 dtl = TQDate::currentDate().addDays(-1);
1101 else
1102 dtl = TQDateTime::currentDateTime();
1103 initKCalAlarm(ev, dtl, AT_LOGIN_TYPE);
1104 if (!ancillaryType && dtl.isValid())
1105 {
1106 ancillaryTime = dtl;
1107 ancillaryType = 1;
1108 }
1109 }
1110 if (mReminderMinutes || (mArchiveReminderMinutes && original))
1111 {
1112 int minutes = mReminderMinutes ? mReminderMinutes : mArchiveReminderMinutes;
1113 initKCalAlarm(ev, -minutes * 60, TQStringList(mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE));
1114 if (!ancillaryType)
1115 {
1116 ancillaryOffset = -minutes * 60;
1117 ancillaryType = 2;
1118 }
1119 }
1120 if (mDeferral > 0 || (mDeferral == CANCEL_DEFERRAL && !cancelCancelledDefer))
1121 {
1122 DateTime nextDateTime = mNextMainDateTime;
1123 if (mMainExpired)
1124 {
1125 if (checkRecur() == KARecurrence::NO_RECUR)
1126 nextDateTime = mStartDateTime;
1127 else if (!original)
1128 {
1129 // It's a deferral of an expired recurrence.
1130 // Need to ensure that the alarm offset is to an occurrence
1131 // which isn't excluded by an exception - otherwise, it will
1132 // never be triggered. So choose the first recurrence which
1133 // isn't an exception.
1134 nextDateTime = mRecurrence->getNextDateTime(mStartDateTime.dateTime().addDays(-1));
1135 nextDateTime.setDateOnly(mStartDateTime.isDateOnly());
1136 }
1137 }
1138 int startOffset;
1139 TQStringList list;
1140 if (mDeferralTime.isDateOnly())
1141 {
1142 startOffset = nextDateTime.secsTo(mDeferralTime.dateTime());
1143 list += DATE_DEFERRAL_TYPE;
1144 }
1145 else
1146 {
1147 startOffset = nextDateTime.dateTime().secsTo(mDeferralTime.dateTime());
1148 list += TIME_DEFERRAL_TYPE;
1149 }
1150 if (mDeferral == REMINDER_DEFERRAL)
1151 list += mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE;
1152 initKCalAlarm(ev, startOffset, list);
1153 if (!ancillaryType && mDeferralTime.isValid())
1154 {
1155 ancillaryOffset = startOffset;
1156 ancillaryType = 2;
1157 }
1158 }
1159 if (!mTemplateName.isEmpty())
1160 ev.setSummary(mTemplateName);
1161 else if (mDisplaying)
1162 {
1163 TQStringList list(DISPLAYING_TYPE);
1164 if (mDisplayingFlags & REPEAT_AT_LOGIN)
1165 list += AT_LOGIN_TYPE;
1166 else if (mDisplayingFlags & DEFERRAL)
1167 {
1168 if (mDisplayingFlags & TIMED_FLAG)
1169 list += TIME_DEFERRAL_TYPE;
1170 else
1171 list += DATE_DEFERRAL_TYPE;
1172 }
1173 if (mDisplayingFlags & REMINDER)
1174 list += mReminderOnceOnly ? REMINDER_ONCE_TYPE : REMINDER_TYPE;
1175 initKCalAlarm(ev, mDisplayingTime, list);
1176 if (!ancillaryType && mDisplayingTime.isValid())
1177 {
1178 ancillaryTime = mDisplayingTime;
1179 ancillaryType = 1;
1180 }
1181 }
1182 if (mBeep || mSpeak || !mAudioFile.isEmpty())
1183 {
1184 // A sound is specified
1185 if (ancillaryType == 2)
1186 initKCalAlarm(ev, ancillaryOffset, TQStringList(), KAAlarm::AUDIO_ALARM);
1187 else
1188 initKCalAlarm(ev, ancillaryTime, TQStringList(), KAAlarm::AUDIO_ALARM);
1189 }
1190 if (!mPreAction.isEmpty())
1191 {
1192 // A pre-display action is specified
1193 if (ancillaryType == 2)
1194 initKCalAlarm(ev, ancillaryOffset, TQStringList(PRE_ACTION_TYPE), KAAlarm::PRE_ACTION_ALARM);
1195 else
1196 initKCalAlarm(ev, ancillaryTime, TQStringList(PRE_ACTION_TYPE), KAAlarm::PRE_ACTION_ALARM);
1197 }
1198 if (!mPostAction.isEmpty())
1199 {
1200 // A post-display action is specified
1201 if (ancillaryType == 2)
1202 initKCalAlarm(ev, ancillaryOffset, TQStringList(POST_ACTION_TYPE), KAAlarm::POST_ACTION_ALARM);
1203 else
1204 initKCalAlarm(ev, ancillaryTime, TQStringList(POST_ACTION_TYPE), KAAlarm::POST_ACTION_ALARM);
1205 }
1206
1207 if (mRecurrence)
1208 mRecurrence->writeRecurrence(*ev.recurrence());
1209 else
1210 ev.clearRecurrence();
1211 if (mSaveDateTime.isValid())
1212 ev.setCreated(mSaveDateTime);
1213 ev.setReadOnly(readOnly);
1214 return true;
1215}
1216
1217/******************************************************************************
1218 * Create a new alarm for a libkcal event, and initialise it according to the
1219 * alarm action. If 'types' is non-null, it is appended to the X-TDE-KALARM-TYPE
1220 * property value list.
1221 */
1222Alarm* KAEvent::initKCalAlarm(Event& event, const DateTime& dt, const TQStringList& types, KAAlarm::Type type) const
1223{
1224 int startOffset = dt.isDateOnly() ? mStartDateTime.secsTo(dt)
1225 : mStartDateTime.dateTime().secsTo(dt.dateTime());
1226 return initKCalAlarm(event, startOffset, types, type);
1227}
1228
1229Alarm* KAEvent::initKCalAlarm(Event& event, int startOffsetSecs, const TQStringList& types, KAAlarm::Type type) const
1230{
1231 TQStringList alltypes;
1232 Alarm* alarm = event.newAlarm();
1233 alarm->setEnabled(true);
1234 if (type != KAAlarm::MAIN_ALARM)
1235 {
1236 // RFC2445 specifies that absolute alarm times must be stored as UTC.
1237 // So, in order to store local times, set the alarm time as an offset to DTSTART.
1238 alarm->setStartOffset(startOffsetSecs);
1239 }
1240
1241 switch (type)
1242 {
1243 case KAAlarm::AUDIO_ALARM:
1244 alarm->setAudioAlarm(mAudioFile); // empty for a beep or for speaking
1245 if (mSpeak)
1246 alarm->setCustomProperty(APPNAME, SPEAK_PROPERTY, TQString::fromLatin1("Y"));
1247 if (mRepeatSound)
1248 {
1249 alarm->setRepeatCount(-1);
1250 alarm->setSnoozeTime(0);
1251 }
1252 if (!mAudioFile.isEmpty() && mSoundVolume >= 0)
1253 alarm->setCustomProperty(APPNAME, VOLUME_PROPERTY,
1254 TQString::fromLatin1("%1;%2;%3").arg(TQString::number(mSoundVolume, 'f', 2))
1255 .arg(TQString::number(mFadeVolume, 'f', 2))
1256 .arg(mFadeSeconds));
1257 break;
1258 case KAAlarm::PRE_ACTION_ALARM:
1259 setProcedureAlarm(alarm, mPreAction);
1260 break;
1261 case KAAlarm::POST_ACTION_ALARM:
1262 setProcedureAlarm(alarm, mPostAction);
1263 break;
1264 case KAAlarm::MAIN_ALARM:
1265 alarm->setSnoozeTime(mRepeatInterval);
1266 alarm->setRepeatCount(mRepeatCount);
1267 if (mRepeatCount)
1268 alarm->setCustomProperty(APPNAME, NEXT_REPEAT_PROPERTY,
1269 TQString::number(mNextRepeat));
1270 // fall through to INVALID_ALARM
1271 case KAAlarm::INVALID_ALARM:
1272 switch (mActionType)
1273 {
1274 case T_FILE:
1275 alltypes += FILE_TYPE;
1276 // fall through to T_MESSAGE
1277 case T_MESSAGE:
1278 alarm->setDisplayAlarm(AlarmText::toCalendarText(mText));
1279 alarm->setCustomProperty(APPNAME, FONT_COLOUR_PROPERTY,
1280 TQString::fromLatin1("%1;%2;%3").arg(mBgColour.name())
1281 .arg(mFgColour.name())
1282 .arg(mDefaultFont ? TQString() : mFont.toString()));
1283 break;
1284 case T_COMMAND:
1285 if (mCommandScript)
1286 alarm->setProcedureAlarm("", mText);
1287 else
1288 setProcedureAlarm(alarm, mText);
1289 break;
1290 case T_EMAIL:
1291 alarm->setEmailAlarm(mEmailSubject, mText, mEmailAddresses, mEmailAttachments);
1292 if (mEmailFromIdentity)
1293 alarm->setCustomProperty(APPNAME, EMAIL_ID_PROPERTY, TQString::number(mEmailFromIdentity));
1294 break;
1295 case T_AUDIO:
1296 break;
1297 }
1298 break;
1299 case KAAlarm::REMINDER_ALARM:
1300 case KAAlarm::DEFERRED_ALARM:
1301 case KAAlarm::DEFERRED_REMINDER_ALARM:
1302 case KAAlarm::AT_LOGIN_ALARM:
1303 case KAAlarm::DISPLAYING_ALARM:
1304 break;
1305 }
1306 alltypes += types;
1307 if (alltypes.count() > 0)
1308 alarm->setCustomProperty(APPNAME, TYPE_PROPERTY, alltypes.join(","));
1309 return alarm;
1310}
1311
1312/******************************************************************************
1313 * Return the alarm of the specified type.
1314 */
1315KAAlarm KAEvent::alarm(KAAlarm::Type type) const
1316{
1317 checkRecur(); // ensure recurrence/repetition data is consistent
1318 KAAlarm al; // this sets type to INVALID_ALARM
1319 if (mAlarmCount)
1320 {
1321 al.mEventID = mEventID;
1322 al.mActionType = mActionType;
1323 al.mText = mText;
1324 al.mBgColour = mBgColour;
1325 al.mFgColour = mFgColour;
1326 al.mFont = mFont;
1327 al.mDefaultFont = mDefaultFont;
1328 al.mBeep = mBeep;
1329 al.mSpeak = mSpeak;
1330 al.mSoundVolume = mSoundVolume;
1331 al.mFadeVolume = mFadeVolume;
1332 al.mFadeSeconds = mFadeSeconds;
1333 al.mRepeatSound = mRepeatSound;
1334 al.mConfirmAck = mConfirmAck;
1335 al.mRepeatCount = 0;
1336 al.mRepeatInterval = 0;
1337 al.mRepeatAtLogin = false;
1338 al.mDeferred = false;
1339 al.mLateCancel = mLateCancel;
1340 al.mAutoClose = mAutoClose;
1341 al.mEmailBcc = mEmailBcc;
1342 al.mCommandScript = mCommandScript;
1343 if (mActionType == T_EMAIL)
1344 {
1345 al.mEmailFromIdentity = mEmailFromIdentity;
1346 al.mEmailAddresses = mEmailAddresses;
1347 al.mEmailSubject = mEmailSubject;
1348 al.mEmailAttachments = mEmailAttachments;
1349 }
1350 switch (type)
1351 {
1352 case KAAlarm::MAIN_ALARM:
1353 if (!mMainExpired)
1354 {
1355 al.mType = KAAlarm::MAIN__ALARM;
1356 al.mNextMainDateTime = mNextMainDateTime;
1357 al.mRepeatCount = mRepeatCount;
1358 al.mRepeatInterval = mRepeatInterval;
1359 al.mNextRepeat = mNextRepeat;
1360 }
1361 break;
1362 case KAAlarm::REMINDER_ALARM:
1363 if (mReminderMinutes)
1364 {
1365 al.mType = KAAlarm::REMINDER__ALARM;
1366 if (mReminderOnceOnly)
1367 al.mNextMainDateTime = mStartDateTime.addMins(-mReminderMinutes);
1368 else
1369 al.mNextMainDateTime = mNextMainDateTime.addMins(-mReminderMinutes);
1370 }
1371 break;
1372 case KAAlarm::DEFERRED_REMINDER_ALARM:
1373 if (mDeferral != REMINDER_DEFERRAL)
1374 break;
1375 // fall through to DEFERRED_ALARM
1376 case KAAlarm::DEFERRED_ALARM:
1377 if (mDeferral > 0)
1378 {
1379 al.mType = static_cast<KAAlarm::SubType>((mDeferral == REMINDER_DEFERRAL ? KAAlarm::DEFERRED_REMINDER_ALARM : KAAlarm::DEFERRED_ALARM)
1380 | (mDeferralTime.isDateOnly() ? 0 : KAAlarm::TIMED_DEFERRAL_FLAG));
1381 al.mNextMainDateTime = mDeferralTime;
1382 al.mDeferred = true;
1383 }
1384 break;
1385 case KAAlarm::AT_LOGIN_ALARM:
1386 if (mRepeatAtLogin)
1387 {
1388 al.mType = KAAlarm::AT_LOGIN__ALARM;
1389 al.mNextMainDateTime = mAtLoginDateTime;
1390 al.mRepeatAtLogin = true;
1391 al.mLateCancel = 0;
1392 al.mAutoClose = false;
1393 }
1394 break;
1395 case KAAlarm::DISPLAYING_ALARM:
1396 if (mDisplaying)
1397 {
1398 al.mType = KAAlarm::DISPLAYING__ALARM;
1399 al.mNextMainDateTime = mDisplayingTime;
1400 al.mDisplaying = true;
1401 }
1402 break;
1403 case KAAlarm::AUDIO_ALARM:
1404 case KAAlarm::PRE_ACTION_ALARM:
1405 case KAAlarm::POST_ACTION_ALARM:
1406 case KAAlarm::INVALID_ALARM:
1407 default:
1408 break;
1409 }
1410 }
1411 return al;
1412}
1413
1414/******************************************************************************
1415 * Return the main alarm for the event.
1416 * If the main alarm does not exist, one of the subsidiary ones is returned if
1417 * possible.
1418 * N.B. a repeat-at-login alarm can only be returned if it has been read from/
1419 * written to the calendar file.
1420 */
1421KAAlarm KAEvent::firstAlarm() const
1422{
1423 if (mAlarmCount)
1424 {
1425 if (!mMainExpired)
1426 return alarm(KAAlarm::MAIN_ALARM);
1427 return nextAlarm(KAAlarm::MAIN_ALARM);
1428 }
1429 return KAAlarm();
1430}
1431
1432/******************************************************************************
1433 * Return the next alarm for the event, after the specified alarm.
1434 * N.B. a repeat-at-login alarm can only be returned if it has been read from/
1435 * written to the calendar file.
1436 */
1437KAAlarm KAEvent::nextAlarm(KAAlarm::Type prevType) const
1438{
1439 switch (prevType)
1440 {
1441 case KAAlarm::MAIN_ALARM:
1442 if (mReminderMinutes)
1443 return alarm(KAAlarm::REMINDER_ALARM);
1444 // fall through to REMINDER_ALARM
1445 case KAAlarm::REMINDER_ALARM:
1446 // There can only be one deferral alarm
1447 if (mDeferral == REMINDER_DEFERRAL)
1448 return alarm(KAAlarm::DEFERRED_REMINDER_ALARM);
1449 if (mDeferral == NORMAL_DEFERRAL)
1450 return alarm(KAAlarm::DEFERRED_ALARM);
1451 // fall through to DEFERRED_ALARM
1452 case KAAlarm::DEFERRED_REMINDER_ALARM:
1453 case KAAlarm::DEFERRED_ALARM:
1454 if (mRepeatAtLogin)
1455 return alarm(KAAlarm::AT_LOGIN_ALARM);
1456 // fall through to AT_LOGIN_ALARM
1457 case KAAlarm::AT_LOGIN_ALARM:
1458 if (mDisplaying)
1459 return alarm(KAAlarm::DISPLAYING_ALARM);
1460 // fall through to DISPLAYING_ALARM
1461 case KAAlarm::DISPLAYING_ALARM:
1462 // fall through to default
1463 case KAAlarm::AUDIO_ALARM:
1464 case KAAlarm::PRE_ACTION_ALARM:
1465 case KAAlarm::POST_ACTION_ALARM:
1466 case KAAlarm::INVALID_ALARM:
1467 default:
1468 break;
1469 }
1470 return KAAlarm();
1471}
1472
1473/******************************************************************************
1474 * Remove the alarm of the specified type from the event.
1475 * This must only be called to remove an alarm which has expired, not to
1476 * reconfigure the event.
1477 */
1478void KAEvent::removeExpiredAlarm(KAAlarm::Type type)
1479{
1480 int count = mAlarmCount;
1481 switch (type)
1482 {
1483 case KAAlarm::MAIN_ALARM:
1484 mAlarmCount = 0; // removing main alarm - also remove subsidiary alarms
1485 break;
1486 case KAAlarm::AT_LOGIN_ALARM:
1487 if (mRepeatAtLogin)
1488 {
1489 // Remove the at-login alarm, but keep a note of it for archiving purposes
1490 mArchiveRepeatAtLogin = true;
1491 mRepeatAtLogin = false;
1492 --mAlarmCount;
1493 }
1494 break;
1495 case KAAlarm::REMINDER_ALARM:
1496 // Remove any reminder alarm, but keep a note of it for archiving purposes
1497 set_archiveReminder();
1498 break;
1499 case KAAlarm::DEFERRED_REMINDER_ALARM:
1500 case KAAlarm::DEFERRED_ALARM:
1501 set_deferral(NO_DEFERRAL);
1502 break;
1503 case KAAlarm::DISPLAYING_ALARM:
1504 if (mDisplaying)
1505 {
1506 mDisplaying = false;
1507 --mAlarmCount;
1508 }
1509 break;
1510 case KAAlarm::AUDIO_ALARM:
1511 case KAAlarm::PRE_ACTION_ALARM:
1512 case KAAlarm::POST_ACTION_ALARM:
1513 case KAAlarm::INVALID_ALARM:
1514 default:
1515 break;
1516 }
1517 if (mAlarmCount != count)
1518 mUpdated = true;
1519}
1520
1521/******************************************************************************
1522 * Defer the event to the specified time.
1523 * If the main alarm time has passed, the main alarm is marked as expired.
1524 * If 'adjustRecurrence' is true, ensure that the next scheduled recurrence is
1525 * after the current time.
1526 * Reply = true if a repetition has been deferred.
1527 */
1528bool KAEvent::defer(const DateTime& dateTime, bool reminder, bool adjustRecurrence)
1529{
1530 bool result = false;
1531 bool setNextRepetition = false;
1532 bool checkRepetition = false;
1533 cancelCancelledDeferral();
1534 if (checkRecur() == KARecurrence::NO_RECUR)
1535 {
1536 if (mReminderMinutes || mDeferral == REMINDER_DEFERRAL || mArchiveReminderMinutes)
1537 {
1538 if (dateTime < mNextMainDateTime.dateTime())
1539 {
1540 set_deferral(REMINDER_DEFERRAL); // defer reminder alarm
1541 mDeferralTime = dateTime;
1542 }
1543 else
1544 {
1545 // Deferring past the main alarm time, so adjust any existing deferral
1546 if (mReminderMinutes || mDeferral == REMINDER_DEFERRAL)
1547 set_deferral(NO_DEFERRAL);
1548 }
1549 // Remove any reminder alarm, but keep a note of it for archiving purposes
1550 if (mReminderMinutes)
1551 set_archiveReminder();
1552 }
1553 if (mDeferral != REMINDER_DEFERRAL)
1554 {
1555 // We're deferring the main alarm, not a reminder
1556 if (mRepeatCount && mRepeatInterval && dateTime < mainEndRepeatTime())
1557 {
1558 // The alarm is repeated, and we're deferring to a time before the last repetition
1559 set_deferral(NORMAL_DEFERRAL);
1560 mDeferralTime = dateTime;
1561 result = true;
1562 setNextRepetition = true;
1563 }
1564 else
1565 {
1566 // Main alarm has now expired
1567 mNextMainDateTime = mDeferralTime = dateTime;
1568 set_deferral(NORMAL_DEFERRAL);
1569 if (!mMainExpired)
1570 {
1571 // Mark the alarm as expired now
1572 mMainExpired = true;
1573 --mAlarmCount;
1574 if (mRepeatAtLogin)
1575 {
1576 // Remove the repeat-at-login alarm, but keep a note of it for archiving purposes
1577 mArchiveRepeatAtLogin = true;
1578 mRepeatAtLogin = false;
1579 --mAlarmCount;
1580 }
1581 }
1582 }
1583 }
1584 }
1585 else if (reminder)
1586 {
1587 // Deferring a reminder for a recurring alarm
1588 if (dateTime >= mNextMainDateTime.dateTime())
1589 set_deferral(NO_DEFERRAL); // (error)
1590 else
1591 {
1592 set_deferral(REMINDER_DEFERRAL);
1593 mDeferralTime = dateTime;
1594 checkRepetition = true;
1595 }
1596 }
1597 else
1598 {
1599 mDeferralTime = dateTime;
1600 if (mDeferral <= 0)
1601 set_deferral(NORMAL_DEFERRAL);
1602 if (adjustRecurrence)
1603 {
1604 TQDateTime now = TQDateTime::currentDateTime();
1605 if (mainEndRepeatTime() < now)
1606 {
1607 // The last repetition (if any) of the current recurrence has already passed.
1608 // Adjust to the next scheduled recurrence after now.
1609 if (!mMainExpired && setNextOccurrence(now) == NO_OCCURRENCE)
1610 {
1611 mMainExpired = true;
1612 --mAlarmCount;
1613 }
1614 }
1615 else
1616 setNextRepetition = (mRepeatCount && mRepeatInterval);
1617 }
1618 else
1619 checkRepetition = true;
1620 }
1621 if (checkRepetition)
1622 setNextRepetition = (mRepeatCount && mRepeatInterval && mDeferralTime < mainEndRepeatTime());
1623 if (setNextRepetition)
1624 {
1625 // The alarm is repeated, and we're deferring to a time before the last repetition.
1626 // Set the next scheduled repetition to the one after the deferral.
1627 mNextRepeat = (mNextMainDateTime < mDeferralTime)
1628 ? mNextMainDateTime.secsTo(mDeferralTime) / (mRepeatInterval * 60) + 1 : 0;
1629 }
1630 mUpdated = true;
1631 return result;
1632}
1633
1634/******************************************************************************
1635 * Cancel any deferral alarm.
1636 */
1637void KAEvent::cancelDefer()
1638{
1639 if (mDeferral > 0)
1640 {
1641 // Set the deferral time to be the same as the next recurrence/repetition.
1642 // This prevents an immediate retriggering of the alarm.
1643 if (mMainExpired
1644 || nextOccurrence(TQDateTime::currentDateTime(), mDeferralTime, RETURN_REPETITION) == NO_OCCURRENCE)
1645 {
1646 // The main alarm has expired, so simply delete the deferral
1647 mDeferralTime = DateTime();
1648 set_deferral(NO_DEFERRAL);
1649 }
1650 else
1651 set_deferral(CANCEL_DEFERRAL);
1652 mUpdated = true;
1653 }
1654}
1655
1656/******************************************************************************
1657 * Cancel any cancelled deferral alarm.
1658 */
1659void KAEvent::cancelCancelledDeferral()
1660{
1661 if (mDeferral == CANCEL_DEFERRAL)
1662 {
1663 mDeferralTime = DateTime();
1664 set_deferral(NO_DEFERRAL);
1665 }
1666}
1667
1668/******************************************************************************
1669* Find the latest time which the alarm can currently be deferred to.
1670*/
1671DateTime KAEvent::deferralLimit(KAEvent::DeferLimitType* limitType) const
1672{
1673 DeferLimitType ltype;
1674 DateTime endTime;
1675 bool recurs = (checkRecur() != KARecurrence::NO_RECUR);
1676 if (recurs || mRepeatCount)
1677 {
1678 // It's a repeated alarm. Don't allow it to be deferred past its
1679 // next occurrence or repetition.
1680 DateTime reminderTime;
1681 TQDateTime now = TQDateTime::currentDateTime();
1682 OccurType type = nextOccurrence(now, endTime, RETURN_REPETITION);
1683 if (type & OCCURRENCE_REPEAT)
1684 ltype = LIMIT_REPETITION;
1685 else if (type == NO_OCCURRENCE)
1686 ltype = LIMIT_NONE;
1687 else if (mReminderMinutes && (now < (reminderTime = endTime.addMins(-mReminderMinutes))))
1688 {
1689 endTime = reminderTime;
1690 ltype = LIMIT_REMINDER;
1691 }
1692 else if (type == FIRST_OR_ONLY_OCCURRENCE && !recurs)
1693 ltype = LIMIT_REPETITION;
1694 else
1695 ltype = LIMIT_RECURRENCE;
1696 }
1697 else if ((mReminderMinutes || mDeferral == REMINDER_DEFERRAL || mArchiveReminderMinutes)
1698 && TQDateTime::currentDateTime() < mNextMainDateTime.dateTime())
1699 {
1700 // It's an reminder alarm. Don't allow it to be deferred past its main alarm time.
1701 endTime = mNextMainDateTime;
1702 ltype = LIMIT_REMINDER;
1703 }
1704 else
1705 ltype = LIMIT_NONE;
1706 if (ltype != LIMIT_NONE)
1707 endTime = endTime.addMins(-1);
1708 if (limitType)
1709 *limitType = ltype;
1710 return endTime;
1711}
1712
1713/******************************************************************************
1714 * Set the event to be a copy of the specified event, making the specified
1715 * alarm the 'displaying' alarm.
1716 * The purpose of setting up a 'displaying' alarm is to be able to reinstate
1717 * the alarm message in case of a crash, or to reinstate it should the user
1718 * choose to defer the alarm. Note that even repeat-at-login alarms need to be
1719 * saved in case their end time expires before the next login.
1720 * Reply = true if successful, false if alarm was not copied.
1721 */
1722bool KAEvent::setDisplaying(const KAEvent& event, KAAlarm::Type alarmType, const TQDateTime& repeatAtLoginTime)
1723{
1724 if (!mDisplaying
1725 && (alarmType == KAAlarm::MAIN_ALARM
1726 || alarmType == KAAlarm::REMINDER_ALARM
1727 || alarmType == KAAlarm::DEFERRED_REMINDER_ALARM
1728 || alarmType == KAAlarm::DEFERRED_ALARM
1729 || alarmType == KAAlarm::AT_LOGIN_ALARM))
1730 {
1731//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;
1732 KAAlarm al = event.alarm(alarmType);
1733 if (al.valid())
1734 {
1735 *this = event;
1736 setUid(DISPLAYING);
1737 mDisplaying = true;
1738 mDisplayingTime = (alarmType == KAAlarm::AT_LOGIN_ALARM) ? repeatAtLoginTime : al.dateTime();
1739 switch (al.subType())
1740 {
1741 case KAAlarm::AT_LOGIN__ALARM: mDisplayingFlags = REPEAT_AT_LOGIN; break;
1742 case KAAlarm::REMINDER__ALARM: mDisplayingFlags = REMINDER; break;
1743 case KAAlarm::DEFERRED_REMINDER_TIME__ALARM: mDisplayingFlags = REMINDER | TIME_DEFERRAL; break;
1744 case KAAlarm::DEFERRED_REMINDER_DATE__ALARM: mDisplayingFlags = REMINDER | DATE_DEFERRAL; break;
1745 case KAAlarm::DEFERRED_TIME__ALARM: mDisplayingFlags = TIME_DEFERRAL; break;
1746 case KAAlarm::DEFERRED_DATE__ALARM: mDisplayingFlags = DATE_DEFERRAL; break;
1747 default: mDisplayingFlags = 0; break;
1748 }
1749 ++mAlarmCount;
1750 mUpdated = true;
1751 return true;
1752 }
1753 }
1754 return false;
1755}
1756
1757/******************************************************************************
1758 * Return the original alarm which the displaying alarm refers to.
1759 */
1760KAAlarm KAEvent::convertDisplayingAlarm() const
1761{
1762 KAAlarm al;
1763 if (mDisplaying)
1764 {
1765 al = alarm(KAAlarm::DISPLAYING_ALARM);
1766 if (mDisplayingFlags & REPEAT_AT_LOGIN)
1767 {
1768 al.mRepeatAtLogin = true;
1769 al.mType = KAAlarm::AT_LOGIN__ALARM;
1770 }
1771 else if (mDisplayingFlags & DEFERRAL)
1772 {
1773 al.mDeferred = true;
1774 al.mType = (mDisplayingFlags == (REMINDER | DATE_DEFERRAL)) ? KAAlarm::DEFERRED_REMINDER_DATE__ALARM
1775 : (mDisplayingFlags == (REMINDER | TIME_DEFERRAL)) ? KAAlarm::DEFERRED_REMINDER_TIME__ALARM
1776 : (mDisplayingFlags == DATE_DEFERRAL) ? KAAlarm::DEFERRED_DATE__ALARM
1777 : KAAlarm::DEFERRED_TIME__ALARM;
1778 }
1779 else if (mDisplayingFlags & REMINDER)
1780 al.mType = KAAlarm::REMINDER__ALARM;
1781 else
1782 al.mType = KAAlarm::MAIN__ALARM;
1783 }
1784 return al;
1785}
1786
1787/******************************************************************************
1788 * Reinstate the original event from the 'displaying' event.
1789 */
1790void KAEvent::reinstateFromDisplaying(const KAEvent& dispEvent)
1791{
1792 if (dispEvent.mDisplaying)
1793 {
1794 *this = dispEvent;
1795 setUid(ACTIVE);
1796 mDisplaying = false;
1797 --mAlarmCount;
1798 mUpdated = true;
1799 }
1800}
1801
1802/******************************************************************************
1803 * Determine whether the event will occur after the specified date/time.
1804 * If 'includeRepetitions' is true and the alarm has a sub-repetition, it
1805 * returns true if any repetitions occur after the specified date/time.
1806 */
1807bool KAEvent::occursAfter(const TQDateTime& preDateTime, bool includeRepetitions) const
1808{
1809 TQDateTime dt;
1810 if (checkRecur() != KARecurrence::NO_RECUR)
1811 {
1812 if (mRecurrence->duration() < 0)
1813 return true; // infinite recurrence
1814 dt = mRecurrence->endDateTime();
1815 }
1816 else
1817 dt = mNextMainDateTime.dateTime();
1818 if (mStartDateTime.isDateOnly())
1819 {
1820 TQDate pre = preDateTime.date();
1821 if (preDateTime.time() < Preferences::startOfDay())
1822 pre = pre.addDays(-1); // today's recurrence (if today recurs) is still to come
1823 if (pre < dt.date())
1824 return true;
1825 }
1826 else if (preDateTime < dt)
1827 return true;
1828
1829 if (includeRepetitions && mRepeatCount)
1830 {
1831 if (preDateTime < dt.addSecs(mRepeatCount * mRepeatInterval * 60))
1832 return true;
1833 }
1834 return false;
1835}
1836
1837/******************************************************************************
1838 * Get the date/time of the next occurrence of the event, after the specified
1839 * date/time.
1840 * 'result' = date/time of next occurrence, or invalid date/time if none.
1841 */
1842KAEvent::OccurType KAEvent::nextOccurrence(const TQDateTime& preDateTime, DateTime& result,
1843 KAEvent::OccurOption includeRepetitions) const
1844{
1845 int repeatSecs = 0;
1846 TQDateTime pre = preDateTime;
1847 if (includeRepetitions != IGNORE_REPETITION)
1848 {
1849 if (!mRepeatCount || !mRepeatInterval)
1850 includeRepetitions = IGNORE_REPETITION;
1851 else
1852 {
1853 repeatSecs = mRepeatInterval * 60;
1854 pre = preDateTime.addSecs(-mRepeatCount * repeatSecs);
1855 }
1856 }
1857
1858 OccurType type;
1859 bool recurs = (checkRecur() != KARecurrence::NO_RECUR);
1860 if (recurs)
1861 type = nextRecurrence(pre, result);
1862 else if (pre < mNextMainDateTime.dateTime())
1863 {
1864 result = mNextMainDateTime;
1865 type = FIRST_OR_ONLY_OCCURRENCE;
1866 }
1867 else
1868 {
1869 result = DateTime();
1870 type = NO_OCCURRENCE;
1871 }
1872
1873 if (type != NO_OCCURRENCE && result <= preDateTime && includeRepetitions != IGNORE_REPETITION)
1874 {
1875 // The next occurrence is a sub-repetition
1876 int repetition = result.secsTo(preDateTime) / repeatSecs + 1;
1877 DateTime repeatDT = result.addSecs(repetition * repeatSecs);
1878 if (recurs)
1879 {
1880 // We've found a recurrence before the specified date/time, which has
1881 // a sub-repetition after the date/time.
1882 // However, if the intervals between recurrences vary, we could possibly
1883 // have missed a later recurrence, which fits the criterion, so check again.
1884 DateTime dt;
1885 OccurType newType = previousOccurrence(repeatDT.dateTime(), dt, false);
1886 if (dt > result)
1887 {
1888 type = newType;
1889 result = dt;
1890 if (includeRepetitions == RETURN_REPETITION && result <= preDateTime)
1891 {
1892 // The next occurrence is a sub-repetition
1893 int repetition = result.secsTo(preDateTime) / repeatSecs + 1;
1894 result = result.addSecs(repetition * repeatSecs);
1895 type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
1896 }
1897 return type;
1898 }
1899 }
1900 if (includeRepetitions == RETURN_REPETITION)
1901 {
1902 // The next occurrence is a sub-repetition
1903 result = repeatDT;
1904 type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
1905 }
1906 }
1907 return type;
1908}
1909
1910/******************************************************************************
1911 * Get the date/time of the last previous occurrence of the event, before the
1912 * specified date/time.
1913 * If 'includeRepetitions' is true and the alarm has a sub-repetition, the
1914 * last previous repetition is returned if appropriate.
1915 * 'result' = date/time of previous occurrence, or invalid date/time if none.
1916 */
1917KAEvent::OccurType KAEvent::previousOccurrence(const TQDateTime& afterDateTime, DateTime& result, bool includeRepetitions) const
1918{
1919 if (mStartDateTime >= afterDateTime)
1920 {
1921 result = TQDateTime();
1922 return NO_OCCURRENCE; // the event starts after the specified date/time
1923 }
1924
1925 // Find the latest recurrence of the event
1926 OccurType type;
1927 if (checkRecur() == KARecurrence::NO_RECUR)
1928 {
1929 result = mStartDateTime;
1930 type = FIRST_OR_ONLY_OCCURRENCE;
1931 }
1932 else
1933 {
1934 TQDateTime recurStart = mRecurrence->startDateTime();
1935 TQDateTime after = afterDateTime;
1936 if (mStartDateTime.isDateOnly() && afterDateTime.time() > Preferences::startOfDay())
1937 after = after.addDays(1); // today's recurrence (if today recurs) has passed
1938 TQDateTime dt = mRecurrence->getPreviousDateTime(after);
1939 result.set(dt, mStartDateTime.isDateOnly());
1940 if (!dt.isValid())
1941 return NO_OCCURRENCE;
1942 if (dt == recurStart)
1943 type = FIRST_OR_ONLY_OCCURRENCE;
1944 else if (mRecurrence->getNextDateTime(dt).isValid())
1945 type = result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
1946 else
1947 type = LAST_RECURRENCE;
1948 }
1949
1950 if (includeRepetitions && mRepeatCount)
1951 {
1952 // Find the latest repetition which is before the specified time.
1953 // N.B. This is coded to avoid 32-bit integer overflow which occurs
1954 // in TQDateTime::secsTo() for large enough time differences.
1955 int repeatSecs = mRepeatInterval * 60;
1956 DateTime lastRepetition = result.addSecs(mRepeatCount * repeatSecs);
1957 if (lastRepetition < afterDateTime)
1958 {
1959 result = lastRepetition;
1960 return static_cast<OccurType>(type | OCCURRENCE_REPEAT);
1961 }
1962 int repetition = (result.dateTime().secsTo(afterDateTime) - 1) / repeatSecs;
1963 if (repetition > 0)
1964 {
1965 result = result.addSecs(repetition * repeatSecs);
1966 return static_cast<OccurType>(type | OCCURRENCE_REPEAT);
1967 }
1968 }
1969 return type;
1970}
1971
1972/******************************************************************************
1973 * Set the date/time of the event to the next scheduled occurrence after the
1974 * specified date/time, provided that this is later than its current date/time.
1975 * Any reminder alarm is adjusted accordingly.
1976 * If the alarm has a sub-repetition, and a repetition of a previous
1977 * recurrence occurs after the specified date/time, that repetition is set as
1978 * the next occurrence.
1979 */
1980KAEvent::OccurType KAEvent::setNextOccurrence(const TQDateTime& preDateTime)
1981{
1982 if (preDateTime < mNextMainDateTime.dateTime())
1983 return FIRST_OR_ONLY_OCCURRENCE; // it might not be the first recurrence - tant pis
1984 TQDateTime pre = preDateTime;
1985 // If there are repetitions, adjust the comparison date/time so that
1986 // we find the earliest recurrence which has a repetition falling after
1987 // the specified preDateTime.
1988 if (mRepeatCount && mRepeatInterval)
1989 pre = preDateTime.addSecs(-mRepeatCount * mRepeatInterval * 60);
1990
1991 DateTime dt;
1992 OccurType type;
1993 if (pre < mNextMainDateTime.dateTime())
1994 {
1995 dt = mNextMainDateTime;
1996 type = FIRST_OR_ONLY_OCCURRENCE; // may not actually be the first occurrence
1997 }
1998 else if (checkRecur() != KARecurrence::NO_RECUR)
1999 {
2000 type = nextRecurrence(pre, dt);
2001 if (type == NO_OCCURRENCE)
2002 return NO_OCCURRENCE;
2003 if (type != FIRST_OR_ONLY_OCCURRENCE && dt != mNextMainDateTime)
2004 {
2005 // Need to reschedule the next trigger date/time
2006 mNextMainDateTime = dt;
2007 // Reinstate the reminder (if any) for the rescheduled recurrence
2008 if (mDeferral == REMINDER_DEFERRAL || mArchiveReminderMinutes)
2009 {
2010 if (mReminderOnceOnly)
2011 {
2012 if (mReminderMinutes)
2013 set_archiveReminder();
2014 }
2015 else
2016 set_reminder(mArchiveReminderMinutes);
2017 }
2018 if (mDeferral == REMINDER_DEFERRAL)
2019 set_deferral(NO_DEFERRAL);
2020 mUpdated = true;
2021 }
2022 }
2023 else
2024 return NO_OCCURRENCE;
2025
2026 if (mRepeatCount && mRepeatInterval)
2027 {
2028 int secs = dt.dateTime().secsTo(preDateTime);
2029 if (secs >= 0)
2030 {
2031 // The next occurrence is a sub-repetition.
2032 type = static_cast<OccurType>(type | OCCURRENCE_REPEAT);
2033 mNextRepeat = (secs / (60 * mRepeatInterval)) + 1;
2034 // Repetitions can't have a reminder, so remove any.
2035 if (mReminderMinutes)
2036 set_archiveReminder();
2037 if (mDeferral == REMINDER_DEFERRAL)
2038 set_deferral(NO_DEFERRAL);
2039 mUpdated = true;
2040 }
2041 else if (mNextRepeat)
2042 {
2043 // The next occurrence is the main occurrence, not a repetition
2044 mNextRepeat = 0;
2045 mUpdated = true;
2046 }
2047 }
2048 return type;
2049}
2050
2051/******************************************************************************
2052 * Get the date/time of the next recurrence of the event, after the specified
2053 * date/time.
2054 * 'result' = date/time of next occurrence, or invalid date/time if none.
2055 */
2056KAEvent::OccurType KAEvent::nextRecurrence(const TQDateTime& preDateTime, DateTime& result) const
2057{
2058 TQDateTime recurStart = mRecurrence->startDateTime();
2059 TQDateTime pre = preDateTime;
2060 if (mStartDateTime.isDateOnly() && preDateTime.time() < Preferences::startOfDay())
2061 {
2062 pre = pre.addDays(-1); // today's recurrence (if today recurs) is still to come
2063 pre.setTime(Preferences::startOfDay());
2064 }
2065 TQDateTime dt = mRecurrence->getNextDateTime(pre);
2066 result.set(dt, mStartDateTime.isDateOnly());
2067 if (!dt.isValid())
2068 return NO_OCCURRENCE;
2069 if (dt == recurStart)
2070 return FIRST_OR_ONLY_OCCURRENCE;
2071 if (mRecurrence->duration() >= 0 && dt == mRecurrence->endDateTime())
2072 return LAST_RECURRENCE;
2073 return result.isDateOnly() ? RECURRENCE_DATE : RECURRENCE_DATE_TIME;
2074}
2075
2076/******************************************************************************
2077 * Return the recurrence interval as text suitable for display.
2078 */
2079TQString KAEvent::recurrenceText(bool brief) const
2080{
2081 if (mRepeatAtLogin)
2082 return brief ? i18n("Brief form of 'At Login'", "Login") : i18n("At login");
2083 if (mRecurrence)
2084 {
2085 int frequency = mRecurrence->frequency();
2086 switch (mRecurrence->defaultRRuleConst()->recurrenceType())
2087 {
2088 case RecurrenceRule::rMinutely:
2089 if (frequency < 60)
2090 return i18n("1 Minute", "%n Minutes", frequency);
2091 else if (frequency % 60 == 0)
2092 return i18n("1 Hour", "%n Hours", frequency/60);
2093 else
2094 {
2095 TQString mins;
2096 return i18n("Hours and Minutes", "%1H %2M").arg(TQString::number(frequency/60)).arg(mins.sprintf("%02d", frequency%60));
2097 }
2098 case RecurrenceRule::rDaily:
2099 return i18n("1 Day", "%n Days", frequency);
2100 case RecurrenceRule::rWeekly:
2101 return i18n("1 Week", "%n Weeks", frequency);
2102 case RecurrenceRule::rMonthly:
2103 return i18n("1 Month", "%n Months", frequency);
2104 case RecurrenceRule::rYearly:
2105 return i18n("1 Year", "%n Years", frequency);
2106 case RecurrenceRule::rNone:
2107 default:
2108 break;
2109 }
2110 }
2111 return brief ? TQString() : i18n("None");
2112}
2113
2114/******************************************************************************
2115 * Return the repetition interval as text suitable for display.
2116 */
2117TQString KAEvent::repetitionText(bool brief) const
2118{
2119 if (mRepeatCount)
2120 {
2121 if (mRepeatInterval % 1440)
2122 {
2123 if (mRepeatInterval < 60)
2124 return i18n("1 Minute", "%n Minutes", mRepeatInterval);
2125 if (mRepeatInterval % 60 == 0)
2126 return i18n("1 Hour", "%n Hours", mRepeatInterval/60);
2127 TQString mins;
2128 return i18n("Hours and Minutes", "%1H %2M").arg(TQString::number(mRepeatInterval/60)).arg(mins.sprintf("%02d", mRepeatInterval%60));
2129 }
2130 if (mRepeatInterval % (7*1440))
2131 return i18n("1 Day", "%n Days", mRepeatInterval/1440);
2132 return i18n("1 Week", "%n Weeks", mRepeatInterval/(7*1440));
2133 }
2134 return brief ? TQString() : i18n("None");
2135}
2136
2137/******************************************************************************
2138 * Adjust the event date/time to the first recurrence of the event, on or after
2139 * start date/time. The event start date may not be a recurrence date, in which
2140 * case a later date will be set.
2141 */
2142void KAEvent::setFirstRecurrence()
2143{
2144 switch (checkRecur())
2145 {
2146 case KARecurrence::NO_RECUR:
2147 case KARecurrence::MINUTELY:
2148 return;
2149 case KARecurrence::ANNUAL_DATE:
2150 case KARecurrence::ANNUAL_POS:
2151 if (mRecurrence->yearMonths().isEmpty())
2152 return; // (presumably it's a template)
2153 break;
2154 case KARecurrence::DAILY:
2155 case KARecurrence::WEEKLY:
2156 case KARecurrence::MONTHLY_POS:
2157 case KARecurrence::MONTHLY_DAY:
2158 break;
2159 }
2160 TQDateTime recurStart = mRecurrence->startDateTime();
2161 if (mRecurrence->recursOn(recurStart.date()))
2162 return; // it already recurs on the start date
2163
2164 // Set the frequency to 1 to find the first possible occurrence
2165 int frequency = mRecurrence->frequency();
2166 mRecurrence->setFrequency(1);
2167 DateTime next;
2168 nextRecurrence(mNextMainDateTime.dateTime(), next);
2169 if (!next.isValid())
2170 mRecurrence->setStartDateTime(recurStart); // reinstate the old value
2171 else
2172 {
2173 mRecurrence->setStartDateTime(next.dateTime());
2174 mStartDateTime = mNextMainDateTime = next;
2175 mUpdated = true;
2176 }
2177 mRecurrence->setFrequency(frequency); // restore the frequency
2178}
2179
2180/******************************************************************************
2181* Initialise the event's recurrence from a KCal::Recurrence.
2182* The event's start date/time is not changed.
2183*/
2184void KAEvent::setRecurrence(const KARecurrence& recurrence)
2185{
2186 mUpdated = true;
2187 delete mRecurrence;
2188 if (recurrence.doesRecur())
2189 {
2190 mRecurrence = new KARecurrence(recurrence);
2191 mRecurrence->setStartDateTime(mStartDateTime.dateTime());
2192 mRecurrence->setFloats(mStartDateTime.isDateOnly());
2193 }
2194 else
2195 mRecurrence = 0;
2196
2197 // Adjust sub-repetition values to fit the recurrence
2198 setRepetition(mRepeatInterval, mRepeatCount);
2199}
2200
2201/******************************************************************************
2202* Initialise the event's sub-repetition.
2203* The repetition length is adjusted if necessary to fit any recurrence interval.
2204* Reply = false if a non-daily interval was specified for a date-only recurrence.
2205*/
2206bool KAEvent::setRepetition(int interval, int count)
2207{
2208 mUpdated = true;
2209 mRepeatInterval = 0;
2210 mRepeatCount = 0;
2211 mNextRepeat = 0;
2212 if (interval > 0 && count > 0 && !mRepeatAtLogin)
2213 {
2214 Q_ASSERT(checkRecur() != KARecurrence::NO_RECUR);
2215 if (interval % 1440 && mStartDateTime.isDateOnly())
2216 return false; // interval must be in units of days for date-only alarms
2217 if (checkRecur() != KARecurrence::NO_RECUR)
2218 {
2219 int longestInterval = mRecurrence->longestInterval() - 1;
2220 if (interval * count > longestInterval)
2221 count = longestInterval / interval;
2222 }
2223 mRepeatInterval = interval;
2224 mRepeatCount = count;
2225 }
2226 return true;
2227}
2228
2229/******************************************************************************
2230 * Set the recurrence to recur at a minutes interval.
2231 * Parameters:
2232 * freq = how many minutes between recurrences.
2233 * count = number of occurrences, including first and last.
2234 * = -1 to recur indefinitely.
2235 * = 0 to use 'end' instead.
2236 * end = end date/time (invalid to use 'count' instead).
2237 * Reply = false if no recurrence was set up.
2238 */
2239bool KAEvent::setRecurMinutely(int freq, int count, const TQDateTime& end)
2240{
2241 return setRecur(RecurrenceRule::rMinutely, freq, count, end);
2242}
2243
2244/******************************************************************************
2245 * Set the recurrence to recur daily.
2246 * Parameters:
2247 * freq = how many days between recurrences.
2248 * days = which days of the week alarms are allowed to occur on.
2249 * count = number of occurrences, including first and last.
2250 * = -1 to recur indefinitely.
2251 * = 0 to use 'end' instead.
2252 * end = end date (invalid to use 'count' instead).
2253 * Reply = false if no recurrence was set up.
2254 */
2255bool KAEvent::setRecurDaily(int freq, const TQBitArray& days, int count, const TQDate& end)
2256{
2257 if (!setRecur(RecurrenceRule::rDaily, freq, count, end))
2258 return false;
2259 int n = 0;
2260 for (int i = 0; i < 7; ++i)
2261 {
2262 if (days.testBit(i))
2263 ++n;
2264 }
2265 if (n < 7)
2266 mRecurrence->addWeeklyDays(days);
2267 return true;
2268}
2269
2270/******************************************************************************
2271 * Set the recurrence to recur weekly, on the specified weekdays.
2272 * Parameters:
2273 * freq = how many weeks between recurrences.
2274 * days = which days of the week alarms should occur on.
2275 * count = number of occurrences, including first and last.
2276 * = -1 to recur indefinitely.
2277 * = 0 to use 'end' instead.
2278 * end = end date (invalid to use 'count' instead).
2279 * Reply = false if no recurrence was set up.
2280 */
2281bool KAEvent::setRecurWeekly(int freq, const TQBitArray& days, int count, const TQDate& end)
2282{
2283 if (!setRecur(RecurrenceRule::rWeekly, freq, count, end))
2284 return false;
2285 mRecurrence->addWeeklyDays(days);
2286 return true;
2287}
2288
2289/******************************************************************************
2290 * Set the recurrence to recur monthly, on the specified days within the month.
2291 * Parameters:
2292 * freq = how many months between recurrences.
2293 * days = which days of the month alarms should occur on.
2294 * count = number of occurrences, including first and last.
2295 * = -1 to recur indefinitely.
2296 * = 0 to use 'end' instead.
2297 * end = end date (invalid to use 'count' instead).
2298 * Reply = false if no recurrence was set up.
2299 */
2300bool KAEvent::setRecurMonthlyByDate(int freq, const TQValueList<int>& days, int count, const TQDate& end)
2301{
2302 if (!setRecur(RecurrenceRule::rMonthly, freq, count, end))
2303 return false;
2304 for (TQValueListConstIterator<int> it = days.begin(); it != days.end(); ++it)
2305 mRecurrence->addMonthlyDate(*it);
2306 return true;
2307}
2308
2309/******************************************************************************
2310 * Set the recurrence to recur monthly, on the specified weekdays in the
2311 * specified weeks of the month.
2312 * Parameters:
2313 * freq = how many months between recurrences.
2314 * posns = which days of the week/weeks of the month alarms should occur on.
2315 * count = number of occurrences, including first and last.
2316 * = -1 to recur indefinitely.
2317 * = 0 to use 'end' instead.
2318 * end = end date (invalid to use 'count' instead).
2319 * Reply = false if no recurrence was set up.
2320 */
2321bool KAEvent::setRecurMonthlyByPos(int freq, const TQValueList<MonthPos>& posns, int count, const TQDate& end)
2322{
2323 if (!setRecur(RecurrenceRule::rMonthly, freq, count, end))
2324 return false;
2325 for (TQValueListConstIterator<MonthPos> it = posns.begin(); it != posns.end(); ++it)
2326 mRecurrence->addMonthlyPos((*it).weeknum, (*it).days);
2327 return true;
2328}
2329
2330/******************************************************************************
2331 * Set the recurrence to recur annually, on the specified start date in each
2332 * of the specified months.
2333 * Parameters:
2334 * freq = how many years between recurrences.
2335 * months = which months of the year alarms should occur on.
2336 * day = day of month, or 0 to use start date
2337 * feb29 = when February 29th should recur in non-leap years.
2338 * count = number of occurrences, including first and last.
2339 * = -1 to recur indefinitely.
2340 * = 0 to use 'end' instead.
2341 * end = end date (invalid to use 'count' instead).
2342 * Reply = false if no recurrence was set up.
2343 */
2344bool KAEvent::setRecurAnnualByDate(int freq, const TQValueList<int>& months, int day, KARecurrence::Feb29Type feb29, int count, const TQDate& end)
2345{
2346 if (!setRecur(RecurrenceRule::rYearly, freq, count, end, feb29))
2347 return false;
2348 for (TQValueListConstIterator<int> it = months.begin(); it != months.end(); ++it)
2349 mRecurrence->addYearlyMonth(*it);
2350 if (day)
2351 mRecurrence->addMonthlyDate(day);
2352 return true;
2353}
2354
2355/******************************************************************************
2356 * Set the recurrence to recur annually, on the specified weekdays in the
2357 * specified weeks of the specified months.
2358 * Parameters:
2359 * freq = how many years between recurrences.
2360 * posns = which days of the week/weeks of the month alarms should occur on.
2361 * months = which months of the year alarms should occur on.
2362 * count = number of occurrences, including first and last.
2363 * = -1 to recur indefinitely.
2364 * = 0 to use 'end' instead.
2365 * end = end date (invalid to use 'count' instead).
2366 * Reply = false if no recurrence was set up.
2367 */
2368bool KAEvent::setRecurAnnualByPos(int freq, const TQValueList<MonthPos>& posns, const TQValueList<int>& months, int count, const TQDate& end)
2369{
2370 if (!setRecur(RecurrenceRule::rYearly, freq, count, end))
2371 return false;
2372 for (TQValueListConstIterator<int> it = months.begin(); it != months.end(); ++it)
2373 mRecurrence->addYearlyMonth(*it);
2374 for (TQValueListConstIterator<MonthPos> it = posns.begin(); it != posns.end(); ++it)
2375 mRecurrence->addYearlyPos((*it).weeknum, (*it).days);
2376 return true;
2377}
2378
2379/******************************************************************************
2380 * Initialise the event's recurrence data.
2381 * Parameters:
2382 * freq = how many intervals between recurrences.
2383 * count = number of occurrences, including first and last.
2384 * = -1 to recur indefinitely.
2385 * = 0 to use 'end' instead.
2386 * end = end date/time (invalid to use 'count' instead).
2387 * Reply = false if no recurrence was set up.
2388 */
2389bool KAEvent::setRecur(RecurrenceRule::PeriodType recurType, int freq, int count, const TQDateTime& end, KARecurrence::Feb29Type feb29)
2390{
2391 if (count >= -1 && (count || end.date().isValid()))
2392 {
2393 if (!mRecurrence)
2394 mRecurrence = new KARecurrence;
2395 if (mRecurrence->init(recurType, freq, count, mNextMainDateTime, end, feb29))
2396 {
2397 mUpdated = true;
2398 return true;
2399 }
2400 }
2401 clearRecur();
2402 return false;
2403}
2404
2405/******************************************************************************
2406 * Clear the event's recurrence and alarm repetition data.
2407 */
2408void KAEvent::clearRecur()
2409{
2410 delete mRecurrence;
2411 mRecurrence = 0;
2412 mRepeatInterval = 0;
2413 mRepeatCount = 0;
2414 mNextRepeat = 0;
2415 mUpdated = true;
2416}
2417
2418/******************************************************************************
2419* Validate the event's recurrence data, correcting any inconsistencies (which
2420* should never occur!).
2421* Reply = true if a recurrence (as opposed to a login repetition) exists.
2422*/
2423KARecurrence::Type KAEvent::checkRecur() const
2424{
2425 if (mRecurrence)
2426 {
2427 KARecurrence::Type type = mRecurrence->type();
2428 switch (type)
2429 {
2430 case KARecurrence::MINUTELY: // hourly
2431 case KARecurrence::DAILY: // daily
2432 case KARecurrence::WEEKLY: // weekly on multiple days of week
2433 case KARecurrence::MONTHLY_DAY: // monthly on multiple dates in month
2434 case KARecurrence::MONTHLY_POS: // monthly on multiple nth day of week
2435 case KARecurrence::ANNUAL_DATE: // annually on multiple months (day of month = start date)
2436 case KARecurrence::ANNUAL_POS: // annually on multiple nth day of week in multiple months
2437 return type;
2438 default:
2439 if (mRecurrence)
2440 const_cast<KAEvent*>(this)->clearRecur(); // recurrence shouldn't exist!!
2441 break;
2442 }
2443 }
2444 return KARecurrence::NO_RECUR;
2445}
2446
2447
2448/******************************************************************************
2449 * Return the recurrence interval in units of the recurrence period type.
2450 */
2451int KAEvent::recurInterval() const
2452{
2453 if (mRecurrence)
2454 {
2455 switch (mRecurrence->type())
2456 {
2457 case KARecurrence::MINUTELY:
2458 case KARecurrence::DAILY:
2459 case KARecurrence::WEEKLY:
2460 case KARecurrence::MONTHLY_DAY:
2461 case KARecurrence::MONTHLY_POS:
2462 case KARecurrence::ANNUAL_DATE:
2463 case KARecurrence::ANNUAL_POS:
2464 return mRecurrence->frequency();
2465 default:
2466 break;
2467 }
2468 }
2469 return 0;
2470}
2471
2472/******************************************************************************
2473* Validate the event's alarm sub-repetition data, correcting any
2474* inconsistencies (which should never occur!).
2475*/
2476void KAEvent::checkRepetition() const
2477{
2478 if (mRepeatCount && !mRepeatInterval)
2479 const_cast<KAEvent*>(this)->mRepeatCount = 0;
2480 if (!mRepeatCount && mRepeatInterval)
2481 const_cast<KAEvent*>(this)->mRepeatInterval = 0;
2482}
2483
2484#if 0
2485/******************************************************************************
2486 * Convert a TQValueList<WDayPos> to TQValueList<MonthPos>.
2487 */
2488TQValueList<KAEvent::MonthPos> KAEvent::convRecurPos(const TQValueList<KCal::RecurrenceRule::WDayPos>& wdaypos)
2489{
2490 TQValueList<MonthPos> mposns;
2491 for (TQValueList<KCal::RecurrenceRule::WDayPos>::ConstIterator it = wdaypos.begin(); it != wdaypos.end(); ++it)
2492 {
2493 int daybit = (*it).day() - 1;
2494 int weeknum = (*it).pos();
2495 bool found = false;
2496 for (TQValueList<MonthPos>::Iterator mit = mposns.begin(); mit != mposns.end(); ++mit)
2497 {
2498 if ((*mit).weeknum == weeknum)
2499 {
2500 (*mit).days.setBit(daybit);
2501 found = true;
2502 break;
2503 }
2504 }
2505 if (!found)
2506 {
2507 MonthPos mpos;
2508 mpos.days.fill(false);
2509 mpos.days.setBit(daybit);
2510 mpos.weeknum = weeknum;
2511 mposns.append(mpos);
2512 }
2513 }
2514 return mposns;
2515}
2516#endif
2517
2518/******************************************************************************
2519 * Find the alarm template with the specified name.
2520 * Reply = invalid event if not found.
2521 */
2522KAEvent KAEvent::findTemplateName(AlarmCalendar& calendar, const TQString& name)
2523{
2524 KAEvent event;
2525 Event::List events = calendar.events();
2526 for (Event::List::ConstIterator evit = events.begin(); evit != events.end(); ++evit)
2527 {
2528 Event* ev = *evit;
2529 if (ev->summary() == name)
2530 {
2531 event.set(*ev);
2532 if (!event.isTemplate())
2533 return KAEvent(); // this shouldn't ever happen
2534 break;
2535 }
2536 }
2537 return event;
2538}
2539
2540/******************************************************************************
2541 * Adjust the time at which date-only events will occur for each of the events
2542 * in a list. Events for which both date and time are specified are left
2543 * unchanged.
2544 * Reply = true if any events have been updated.
2545 */
2546bool KAEvent::adjustStartOfDay(const Event::List& events)
2547{
2548 bool changed = false;
2549 TQTime startOfDay = Preferences::startOfDay();
2550 for (Event::List::ConstIterator evit = events.begin(); evit != events.end(); ++evit)
2551 {
2552 Event* event = *evit;
2553 const TQStringList cats = event->categories();
2554 if (cats.find(DATE_ONLY_CATEGORY) != cats.end())
2555 {
2556 // It's an untimed event, so fix it
2557 TQTime oldTime = event->dtStart().time();
2558 int adjustment = oldTime.secsTo(startOfDay);
2559 if (adjustment)
2560 {
2561 event->setDtStart(TQDateTime(event->dtStart().date(), startOfDay));
2562 Alarm::List alarms = event->alarms();
2563 int deferralOffset = 0;
2564 for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit)
2565 {
2566 // Parse the next alarm's text
2567 Alarm& alarm = **alit;
2568 AlarmData data;
2569 readAlarm(alarm, data);
2570 if (data.type & KAAlarm::TIMED_DEFERRAL_FLAG)
2571 {
2572 // Timed deferral alarm, so adjust the offset
2573 deferralOffset = alarm.startOffset().asSeconds();
2574 alarm.setStartOffset(deferralOffset - adjustment);
2575 }
2576 else if (data.type == KAAlarm::AUDIO__ALARM
2577 && alarm.startOffset().asSeconds() == deferralOffset)
2578 {
2579 // Audio alarm is set for the same time as the deferral alarm
2580 alarm.setStartOffset(deferralOffset - adjustment);
2581 }
2582 }
2583 changed = true;
2584 }
2585 }
2586 else
2587 {
2588 // It's a timed event. Fix any untimed alarms.
2589 int deferralOffset = 0;
2590 int newDeferralOffset = 0;
2591 DateTime start;
2592 TQDateTime nextMainDateTime = readDateTime(*event, false, start).rawDateTime();
2593 AlarmMap alarmMap;
2594 readAlarms(*event, &alarmMap);
2595 for (AlarmMap::Iterator it = alarmMap.begin(); it != alarmMap.end(); ++it)
2596 {
2597 const AlarmData& data = it.data();
2598 if (!data.alarm->hasStartOffset())
2599 continue;
2600 if ((data.type & KAAlarm::DEFERRED_ALARM)
2601 && !(data.type & KAAlarm::TIMED_DEFERRAL_FLAG))
2602 {
2603 // Date-only deferral alarm, so adjust its time
2604 TQDateTime altime = nextMainDateTime.addSecs(data.alarm->startOffset().asSeconds());
2605 altime.setTime(startOfDay);
2606 deferralOffset = data.alarm->startOffset().asSeconds();
2607 newDeferralOffset = event->dtStart().secsTo(altime);
2608 const_cast<Alarm*>(data.alarm)->setStartOffset(newDeferralOffset);
2609 changed = true;
2610 }
2611 else if (data.type == KAAlarm::AUDIO__ALARM
2612 && data.alarm->startOffset().asSeconds() == deferralOffset)
2613 {
2614 // Audio alarm is set for the same time as the deferral alarm
2615 const_cast<Alarm*>(data.alarm)->setStartOffset(newDeferralOffset);
2616 changed = true;
2617 }
2618 }
2619 }
2620 }
2621 return changed;
2622}
2623
2624/******************************************************************************
2625 * If the calendar was written by a previous version of KAlarm, do any
2626 * necessary format conversions on the events to ensure that when the calendar
2627 * is saved, no information is lost or corrupted.
2628 */
2629void KAEvent::convertKCalEvents(KCal::Calendar& calendar, int version, bool adjustSummerTime)
2630{
2631 // KAlarm pre-0.9 codes held in the alarm's DESCRIPTION property
2632 static const TQChar SEPARATOR = ';';
2633 static const TQChar LATE_CANCEL_CODE = 'C';
2634 static const TQChar AT_LOGIN_CODE = 'L'; // subsidiary alarm at every login
2635 static const TQChar DEFERRAL_CODE = 'D'; // extra deferred alarm
2636 static const TQString TEXT_PREFIX = TQString::fromLatin1("TEXT:");
2637 static const TQString FILE_PREFIX = TQString::fromLatin1("FILE:");
2638 static const TQString COMMAND_PREFIX = TQString::fromLatin1("CMD:");
2639
2640 // KAlarm pre-0.9.2 codes held in the event's CATEGORY property
2641 static const TQString BEEP_CATEGORY = TQString::fromLatin1("BEEP");
2642
2643 // KAlarm pre-1.1.1 LATECANCEL category with no parameter
2644 static const TQString LATE_CANCEL_CAT = TQString::fromLatin1("LATECANCEL");
2645
2646 // KAlarm pre-1.3.0 TMPLDEFTIME category with no parameter
2647 static const TQString TEMPL_DEF_TIME_CAT = TQString::fromLatin1("TMPLDEFTIME");
2648
2649 // KAlarm pre-1.3.1 XTERM category
2650 static const TQString EXEC_IN_XTERM_CAT = TQString::fromLatin1("XTERM");
2651
2652 // KAlarm pre-1.4.22 properties
2653 static const TQCString KMAIL_ID_PROPERTY("KMAILID"); // X-TDE-KALARM-KMAILID property
2654
2655 if (version >= calVersion())
2656 return;
2657
2658 kdDebug(5950) << "KAEvent::convertKCalEvents(): adjusting version " << version << endl;
2659 bool pre_0_7 = (version < KAlarm::Version(0,7,0));
2660 bool pre_0_9 = (version < KAlarm::Version(0,9,0));
2661 bool pre_0_9_2 = (version < KAlarm::Version(0,9,2));
2662 bool pre_1_1_1 = (version < KAlarm::Version(1,1,1));
2663 bool pre_1_2_1 = (version < KAlarm::Version(1,2,1));
2664 bool pre_1_3_0 = (version < KAlarm::Version(1,3,0));
2665 bool pre_1_3_1 = (version < KAlarm::Version(1,3,1));
2666 bool pre_1_4_14 = (version < KAlarm::Version(1,4,14));
2667 bool pre_1_5_0 = (version < KAlarm::Version(1,5,0));
2668 Q_ASSERT(calVersion() == KAlarm::Version(1,5,0));
2669
2670 TQDateTime dt0(TQDate(1970,1,1), TQTime(0,0,0));
2671 TQTime startOfDay = Preferences::startOfDay();
2672
2673 Event::List events = calendar.rawEvents();
2674 for (Event::List::ConstIterator evit = events.begin(); evit != events.end(); ++evit)
2675 {
2676 Event* event = *evit;
2677 Alarm::List alarms = event->alarms();
2678 if (alarms.isEmpty())
2679 continue; // KAlarm isn't interested in events without alarms
2680 TQStringList cats = event->categories();
2681 bool addLateCancel = false;
2682
2683 if (pre_0_7 && event->doesFloat())
2684 {
2685 // It's a KAlarm pre-0.7 calendar file.
2686 // Ensure that when the calendar is saved, the alarm time isn't lost.
2687 event->setFloats(false);
2688 }
2689
2690 if (pre_0_9)
2691 {
2692 /*
2693 * It's a KAlarm pre-0.9 calendar file.
2694 * All alarms were of type DISPLAY. Instead of the X-TDE-KALARM-TYPE
2695 * alarm property, characteristics were stored as a prefix to the
2696 * alarm DESCRIPTION property, as follows:
2697 * SEQNO;[FLAGS];TYPE:TEXT
2698 * where
2699 * SEQNO = sequence number of alarm within the event
2700 * FLAGS = C for late-cancel, L for repeat-at-login, D for deferral
2701 * TYPE = TEXT or FILE or CMD
2702 * TEXT = message text, file name/URL or command
2703 */
2704 for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit)
2705 {
2706 Alarm* alarm = *alit;
2707 bool atLogin = false;
2708 bool deferral = false;
2709 bool lateCancel = false;
2710 KAAlarmEventBase::Type action = T_MESSAGE;
2711 TQString txt = alarm->text();
2712 int length = txt.length();
2713 int i = 0;
2714 if (txt[0].isDigit())
2715 {
2716 while (++i < length && txt[i].isDigit()) ;
2717 if (i < length && txt[i++] == SEPARATOR)
2718 {
2719 while (i < length)
2720 {
2721 TQChar ch = txt[i++];
2722 if (ch == SEPARATOR)
2723 break;
2724 if (ch == LATE_CANCEL_CODE)
2725 lateCancel = true;
2726 else if (ch == AT_LOGIN_CODE)
2727 atLogin = true;
2728 else if (ch == DEFERRAL_CODE)
2729 deferral = true;
2730 }
2731 }
2732 else
2733 i = 0; // invalid prefix
2734 }
2735 if (txt.find(TEXT_PREFIX, i) == i)
2736 i += TEXT_PREFIX.length();
2737 else if (txt.find(FILE_PREFIX, i) == i)
2738 {
2739 action = T_FILE;
2740 i += FILE_PREFIX.length();
2741 }
2742 else if (txt.find(COMMAND_PREFIX, i) == i)
2743 {
2744 action = T_COMMAND;
2745 i += COMMAND_PREFIX.length();
2746 }
2747 else
2748 i = 0;
2749 txt = txt.mid(i);
2750
2751 TQStringList types;
2752 switch (action)
2753 {
2754 case T_FILE:
2755 types += FILE_TYPE;
2756 // fall through to T_MESSAGE
2757 case T_MESSAGE:
2758 alarm->setDisplayAlarm(txt);
2759 break;
2760 case T_COMMAND:
2761 setProcedureAlarm(alarm, txt);
2762 break;
2763 case T_EMAIL: // email alarms were introduced in KAlarm 0.9
2764 case T_AUDIO: // never occurs in this context
2765 break;
2766 }
2767 if (atLogin)
2768 {
2769 types += AT_LOGIN_TYPE;
2770 lateCancel = false;
2771 }
2772 else if (deferral)
2773 types += TIME_DEFERRAL_TYPE;
2774 if (lateCancel)
2775 addLateCancel = true;
2776 if (types.count() > 0)
2777 alarm->setCustomProperty(APPNAME, TYPE_PROPERTY, types.join(","));
2778
2779 if (pre_0_7 && alarm->repeatCount() > 0 && alarm->snoozeTime().value() > 0)
2780 {
2781 // It's a KAlarm pre-0.7 calendar file.
2782 // Minutely recurrences were stored differently.
2783 Recurrence* recur = event->recurrence();
2784 if (recur && recur->doesRecur())
2785 {
2786 recur->setMinutely(alarm->snoozeTime());
2787 recur->setDuration(alarm->repeatCount() + 1);
2788 alarm->setRepeatCount(0);
2789 alarm->setSnoozeTime(0);
2790 }
2791 }
2792
2793 if (adjustSummerTime)
2794 {
2795 // The calendar file was written by the KDE 3.0.0 version of KAlarm 0.5.7.
2796 // Summer time was ignored when converting to UTC.
2797 TQDateTime dt = alarm->time();
2798 time_t t = dt0.secsTo(dt);
2799 struct tm* dtm = localtime(&t);
2800 if (dtm->tm_isdst)
2801 {
2802 dt = dt.addSecs(-3600);
2803 alarm->setTime(dt);
2804 }
2805 }
2806 }
2807 }
2808
2809 if (pre_0_9_2)
2810 {
2811 /*
2812 * It's a KAlarm pre-0.9.2 calendar file.
2813 * For the expired calendar, set the CREATED time to the DTEND value.
2814 * Convert date-only DTSTART to date/time, and add category "DATE".
2815 * Set the DTEND time to the DTSTART time.
2816 * Convert all alarm times to DTSTART offsets.
2817 * For display alarms, convert the first unlabelled category to an
2818 * X-TDE-KALARM-FONTCOLOUR property.
2819 * Convert BEEP category into an audio alarm with no audio file.
2820 */
2821 if (uidStatus(event->uid()) == EXPIRED)
2822 event->setCreated(event->dtEnd());
2823 TQDateTime start = event->dtStart();
2824 if (event->doesFloat())
2825 {
2826 event->setFloats(false);
2827 start.setTime(startOfDay);
2828 cats.append(DATE_ONLY_CATEGORY);
2829 }
2830 event->setHasEndDate(false);
2831
2832 Alarm::List::ConstIterator alit;
2833 for (alit = alarms.begin(); alit != alarms.end(); ++alit)
2834 {
2835 Alarm* alarm = *alit;
2836 TQDateTime dt = alarm->time();
2837 alarm->setStartOffset(start.secsTo(dt));
2838 }
2839
2840 if (cats.count() > 0)
2841 {
2842 for (alit = alarms.begin(); alit != alarms.end(); ++alit)
2843 {
2844 Alarm* alarm = *alit;
2845 if (alarm->type() == Alarm::Display)
2846 alarm->setCustomProperty(APPNAME, FONT_COLOUR_PROPERTY,
2847 TQString::fromLatin1("%1;;").arg(cats[0]));
2848 }
2849 cats.remove(cats.begin());
2850 }
2851
2852 for (TQStringList::Iterator it = cats.begin(); it != cats.end(); ++it)
2853 {
2854 if (*it == BEEP_CATEGORY)
2855 {
2856 cats.remove(it);
2857
2858 Alarm* alarm = event->newAlarm();
2859 alarm->setEnabled(true);
2860 alarm->setAudioAlarm();
2861 TQDateTime dt = event->dtStart(); // default
2862
2863 // Parse and order the alarms to know which one's date/time to use
2864 AlarmMap alarmMap;
2865 readAlarms(*event, &alarmMap);
2866 AlarmMap::ConstIterator it = alarmMap.begin();
2867 if (it != alarmMap.end())
2868 {
2869 dt = it.data().alarm->time();
2870 break;
2871 }
2872 alarm->setStartOffset(start.secsTo(dt));
2873 break;
2874 }
2875 }
2876 }
2877
2878 if (pre_1_1_1)
2879 {
2880 /*
2881 * It's a KAlarm pre-1.1.1 calendar file.
2882 * Convert simple LATECANCEL category to LATECANCEL:n where n = minutes late.
2883 */
2884 TQStringList::Iterator it;
2885 while ((it = cats.find(LATE_CANCEL_CAT)) != cats.end())
2886 {
2887 cats.remove(it);
2888 addLateCancel = true;
2889 }
2890 }
2891
2892 if (pre_1_2_1)
2893 {
2894 /*
2895 * It's a KAlarm pre-1.2.1 calendar file.
2896 * Convert email display alarms from translated to untranslated header prefixes.
2897 */
2898 for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit)
2899 {
2900 Alarm* alarm = *alit;
2901 if (alarm->type() == Alarm::Display)
2902 {
2903 TQString oldtext = alarm->text();
2904 TQString newtext = AlarmText::toCalendarText(oldtext);
2905 if (oldtext != newtext)
2906 alarm->setDisplayAlarm(newtext);
2907 }
2908 }
2909 }
2910
2911 if (pre_1_3_0)
2912 {
2913 /*
2914 * It's a KAlarm pre-1.3.0 calendar file.
2915 * Convert simple TMPLDEFTIME category to TMPLAFTTIME:n where n = minutes after.
2916 */
2917 TQStringList::Iterator it;
2918 while ((it = cats.find(TEMPL_DEF_TIME_CAT)) != cats.end())
2919 {
2920 cats.remove(it);
2921 cats.append(TQString("%1%2").arg(TEMPL_AFTER_TIME_CATEGORY).arg(0));
2922 }
2923 }
2924
2925 if (pre_1_3_1)
2926 {
2927 /*
2928 * It's a KAlarm pre-1.3.1 calendar file.
2929 * Convert simple XTERM category to LOG:xterm:
2930 */
2931 TQStringList::Iterator it;
2932 while ((it = cats.find(EXEC_IN_XTERM_CAT)) != cats.end())
2933 {
2934 cats.remove(it);
2935 cats.append(LOG_CATEGORY + xtermURL);
2936 }
2937 }
2938
2939 if (addLateCancel)
2940 cats.append(TQString("%1%2").arg(LATE_CANCEL_CATEGORY).arg(1));
2941
2942 event->setCategories(cats);
2943
2944
2945 if (pre_1_4_14
2946 && event->recurrence() && event->recurrence()->doesRecur())
2947 {
2948 /*
2949 * It's a KAlarm pre-1.4.14 calendar file.
2950 * For recurring events, convert the main alarm offset to an absolute
2951 * time in the X-TDE-KALARM-NEXTRECUR property, and convert main
2952 * alarm offsets to zero and deferral alarm offsets to be relative to
2953 * the next recurrence.
2954 */
2955 bool dateOnly = (cats.find(DATE_ONLY_CATEGORY) != cats.end());
2956 DateTime startDateTime(event->dtStart(), dateOnly);
2957 // Convert the main alarm and get the next main trigger time from it
2958 DateTime nextMainDateTime;
2959 bool mainExpired = true;
2960 Alarm::List::ConstIterator alit;
2961 for (alit = alarms.begin(); alit != alarms.end(); ++alit)
2962 {
2963 Alarm* alarm = *alit;
2964 if (!alarm->hasStartOffset())
2965 continue;
2966 bool mainAlarm = true;
2967 TQString property = alarm->customProperty(APPNAME, TYPE_PROPERTY);
2968 TQStringList types = TQStringList::split(TQChar(','), property);
2969 for (unsigned int i = 0; i < types.count(); ++i)
2970 {
2971 TQString type = types[i];
2972 if (type == AT_LOGIN_TYPE
2973 || type == TIME_DEFERRAL_TYPE
2974 || type == DATE_DEFERRAL_TYPE
2975 || type == REMINDER_TYPE
2976 || type == REMINDER_ONCE_TYPE
2977 || type == DISPLAYING_TYPE
2978 || type == PRE_ACTION_TYPE
2979 || type == POST_ACTION_TYPE)
2980 mainAlarm = false;
2981 }
2982 if (mainAlarm)
2983 {
2984 mainExpired = false;
2985 nextMainDateTime = alarm->time();
2986 nextMainDateTime.setDateOnly(dateOnly);
2987 if (nextMainDateTime != startDateTime)
2988 {
2989 TQDateTime dt = nextMainDateTime.dateTime();
2990 event->setCustomProperty(APPNAME, NEXT_RECUR_PROPERTY,
2991 dt.toString(dateOnly ? "yyyyMMdd" : "yyyyMMddThhmmss"));
2992 }
2993 alarm->setStartOffset(0);
2994 }
2995 }
2996 int adjustment;
2997 if (mainExpired)
2998 {
2999 // It's an expired recurrence.
3000 // Set the alarm offset relative to the first actual occurrence
3001 // (taking account of possible exceptions).
3002 DateTime dt = event->recurrence()->getNextDateTime(startDateTime.dateTime().addDays(-1));
3003 dt.setDateOnly(dateOnly);
3004 adjustment = startDateTime.secsTo(dt);
3005 }
3006 else
3007 adjustment = startDateTime.secsTo(nextMainDateTime);
3008 if (adjustment)
3009 {
3010 // Convert deferred alarms
3011 for (alit = alarms.begin(); alit != alarms.end(); ++alit)
3012 {
3013 Alarm* alarm = *alit;
3014 if (!alarm->hasStartOffset())
3015 continue;
3016 TQString property = alarm->customProperty(APPNAME, TYPE_PROPERTY);
3017 TQStringList types = TQStringList::split(TQChar(','), property);
3018 for (unsigned int i = 0; i < types.count(); ++i)
3019 {
3020 TQString type = types[i];
3021 if (type == TIME_DEFERRAL_TYPE
3022 || type == DATE_DEFERRAL_TYPE)
3023 {
3024 alarm->setStartOffset(alarm->startOffset().asSeconds() - adjustment);
3025 break;
3026 }
3027 }
3028 }
3029 }
3030 }
3031
3032 if (pre_1_5_0)
3033 {
3034 /*
3035 * It's a KAlarm pre-1.5.0 calendar file.
3036 * Convert email identity names to uoids.
3037 * Convert simple repetitions without a recurrence, to a recurrence.
3038 */
3039 for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit)
3040 {
3041 Alarm* alarm = *alit;
3042 TQString name = alarm->customProperty(APPNAME, KMAIL_ID_PROPERTY);
3043 if (name.isEmpty())
3044 continue;
3045 uint id = KAMail::identityUoid(name);
3046 if (id)
3047 alarm->setCustomProperty(APPNAME, EMAIL_ID_PROPERTY, TQString::number(id));
3048 alarm->removeCustomProperty(APPNAME, KMAIL_ID_PROPERTY);
3049 }
3050 convertRepetition(event);
3051 }
3052 }
3053}
3054
3055/******************************************************************************
3056* If the calendar was written by a pre-1.4.22 version of KAlarm, or another
3057* program, convert simple repetitions in events without a recurrence, to a
3058* recurrence.
3059* Reply = true if any conversions were done.
3060*/
3061void KAEvent::convertRepetitions(KCal::CalendarLocal& calendar)
3062{
3063
3064 Event::List events = calendar.rawEvents();
3065 for (Event::List::ConstIterator ev = events.begin(); ev != events.end(); ++ev)
3066 convertRepetition(*ev);
3067}
3068
3069/******************************************************************************
3070* Convert simple repetitions in an event without a recurrence, to a
3071* recurrence. Repetitions which are an exact multiple of 24 hours are converted
3072* to daily recurrences; else they are converted to minutely recurrences. Note
3073* that daily and minutely recurrences produce different results when they span
3074* a daylight saving time change.
3075* Reply = true if any conversions were done.
3076*/
3077bool KAEvent::convertRepetition(KCal::Event* event)
3078{
3079 Alarm::List alarms = event->alarms();
3080 if (alarms.isEmpty())
3081 return false;
3082 Recurrence* recur = event->recurrence(); // guaranteed to return non-null
3083 if (!recur->doesRecur())
3084 return false;
3085 bool converted = false;
3086 bool readOnly = event->isReadOnly();
3087 for (Alarm::List::ConstIterator alit = alarms.begin(); alit != alarms.end(); ++alit)
3088 {
3089 Alarm* alarm = *alit;
3090 if (alarm->repeatCount() > 0 && alarm->snoozeTime().value() > 0)
3091 {
3092 if (!converted)
3093 {
3094 if (readOnly)
3095 event->setReadOnly(false);
3096 if (alarm->snoozeTime() % (24*3600))
3097 recur->setMinutely(alarm->snoozeTime());
3098 else
3099 recur->setDaily(alarm->snoozeTime() / (24*3600));
3100 recur->setDuration(alarm->repeatCount() + 1);
3101 converted = true;
3102 }
3103 alarm->setRepeatCount(0);
3104 alarm->setSnoozeTime(0);
3105 }
3106 }
3107 if (converted)
3108 {
3109 if (readOnly)
3110 event->setReadOnly(true);
3111 }
3112 return converted;
3113}
3114
3115#ifndef NDEBUG
3116void KAEvent::dumpDebug() const
3117{
3118 kdDebug(5950) << "KAEvent dump:\n";
3119 KAAlarmEventBase::dumpDebug();
3120 if (!mTemplateName.isEmpty())
3121 {
3122 kdDebug(5950) << "-- mTemplateName:" << mTemplateName << ":\n";
3123 kdDebug(5950) << "-- mTemplateAfterTime:" << mTemplateAfterTime << ":\n";
3124 }
3125 if (mActionType == T_MESSAGE || mActionType == T_FILE)
3126 {
3127 kdDebug(5950) << "-- mAudioFile:" << mAudioFile << ":\n";
3128 kdDebug(5950) << "-- mPreAction:" << mPreAction << ":\n";
3129 kdDebug(5950) << "-- mPostAction:" << mPostAction << ":\n";
3130 }
3131 else if (mActionType == T_COMMAND)
3132 {
3133 kdDebug(5950) << "-- mCommandXterm:" << (mCommandXterm ? "true" : "false") << ":\n";
3134 kdDebug(5950) << "-- mLogFile:" << mLogFile << ":\n";
3135 }
3136 kdDebug(5950) << "-- mKMailSerialNumber:" << mKMailSerialNumber << ":\n";
3137 kdDebug(5950) << "-- mCopyToKOrganizer:" << (mCopyToKOrganizer ? "true" : "false") << ":\n";
3138 kdDebug(5950) << "-- mStartDateTime:" << mStartDateTime.toString() << ":\n";
3139 kdDebug(5950) << "-- mSaveDateTime:" << mSaveDateTime.toString() << ":\n";
3140 if (mRepeatAtLogin)
3141 kdDebug(5950) << "-- mAtLoginDateTime:" << mAtLoginDateTime.toString() << ":\n";
3142 kdDebug(5950) << "-- mArchiveRepeatAtLogin:" << (mArchiveRepeatAtLogin ? "true" : "false") << ":\n";
3143 kdDebug(5950) << "-- mEnabled:" << (mEnabled ? "true" : "false") << ":\n";
3144 if (mReminderMinutes)
3145 kdDebug(5950) << "-- mReminderMinutes:" << mReminderMinutes << ":\n";
3146 if (mArchiveReminderMinutes)
3147 kdDebug(5950) << "-- mArchiveReminderMinutes:" << mArchiveReminderMinutes << ":\n";
3148 if (mReminderMinutes || mArchiveReminderMinutes)
3149 kdDebug(5950) << "-- mReminderOnceOnly:" << mReminderOnceOnly << ":\n";
3150 else if (mDeferral > 0)
3151 {
3152 kdDebug(5950) << "-- mDeferral:" << (mDeferral == NORMAL_DEFERRAL ? "normal" : "reminder") << ":\n";
3153 kdDebug(5950) << "-- mDeferralTime:" << mDeferralTime.toString() << ":\n";
3154 }
3155 else if (mDeferral == CANCEL_DEFERRAL)
3156 kdDebug(5950) << "-- mDeferral:cancel:\n";
3157 kdDebug(5950) << "-- mDeferDefaultMinutes:" << mDeferDefaultMinutes << ":\n";
3158 if (mDisplaying)
3159 {
3160 kdDebug(5950) << "-- mDisplayingTime:" << mDisplayingTime.toString() << ":\n";
3161 kdDebug(5950) << "-- mDisplayingFlags:" << mDisplayingFlags << ":\n";
3162 }
3163 kdDebug(5950) << "-- mRevision:" << mRevision << ":\n";
3164 kdDebug(5950) << "-- mRecurrence:" << (mRecurrence ? "true" : "false") << ":\n";
3165 kdDebug(5950) << "-- mAlarmCount:" << mAlarmCount << ":\n";
3166 kdDebug(5950) << "-- mMainExpired:" << (mMainExpired ? "true" : "false") << ":\n";
3167 kdDebug(5950) << "KAEvent dump end\n";
3168}
3169#endif
3170
3171
3172/*=============================================================================
3173= Class KAAlarm
3174= Corresponds to a single KCal::Alarm instance.
3175=============================================================================*/
3176
3177KAAlarm::KAAlarm(const KAAlarm& alarm)
3178 : KAAlarmEventBase(alarm),
3179 mType(alarm.mType),
3180 mRecurs(alarm.mRecurs),
3181 mDeferred(alarm.mDeferred)
3182{ }
3183
3184
3185int KAAlarm::flags() const
3186{
3187 return KAAlarmEventBase::flags()
3188 | (mDeferred ? KAEvent::DEFERRAL : 0);
3189
3190}
3191
3192#ifndef NDEBUG
3193void KAAlarm::dumpDebug() const
3194{
3195 kdDebug(5950) << "KAAlarm dump:\n";
3196 KAAlarmEventBase::dumpDebug();
3197 const char* altype = 0;
3198 switch (mType)
3199 {
3200 case MAIN__ALARM: altype = "MAIN"; break;
3201 case REMINDER__ALARM: altype = "REMINDER"; break;
3202 case DEFERRED_DATE__ALARM: altype = "DEFERRED(DATE)"; break;
3203 case DEFERRED_TIME__ALARM: altype = "DEFERRED(TIME)"; break;
3204 case DEFERRED_REMINDER_DATE__ALARM: altype = "DEFERRED_REMINDER(DATE)"; break;
3205 case DEFERRED_REMINDER_TIME__ALARM: altype = "DEFERRED_REMINDER(TIME)"; break;
3206 case AT_LOGIN__ALARM: altype = "LOGIN"; break;
3207 case DISPLAYING__ALARM: altype = "DISPLAYING"; break;
3208 case AUDIO__ALARM: altype = "AUDIO"; break;
3209 case PRE_ACTION__ALARM: altype = "PRE_ACTION"; break;
3210 case POST_ACTION__ALARM: altype = "POST_ACTION"; break;
3211 default: altype = "INVALID"; break;
3212 }
3213 kdDebug(5950) << "-- mType:" << altype << ":\n";
3214 kdDebug(5950) << "-- mRecurs:" << (mRecurs ? "true" : "false") << ":\n";
3215 kdDebug(5950) << "-- mDeferred:" << (mDeferred ? "true" : "false") << ":\n";
3216 kdDebug(5950) << "KAAlarm dump end\n";
3217}
3218
3219const char* KAAlarm::debugType(Type type)
3220{
3221 switch (type)
3222 {
3223 case MAIN_ALARM: return "MAIN";
3224 case REMINDER_ALARM: return "REMINDER";
3225 case DEFERRED_ALARM: return "DEFERRED";
3226 case DEFERRED_REMINDER_ALARM: return "DEFERRED_REMINDER";
3227 case AT_LOGIN_ALARM: return "LOGIN";
3228 case DISPLAYING_ALARM: return "DISPLAYING";
3229 case AUDIO_ALARM: return "AUDIO";
3230 case PRE_ACTION_ALARM: return "PRE_ACTION";
3231 case POST_ACTION_ALARM: return "POST_ACTION";
3232 default: return "INVALID";
3233 }
3234}
3235#endif
3236
3237
3238/*=============================================================================
3239= Class KAAlarmEventBase
3240=============================================================================*/
3241
3242void KAAlarmEventBase::copy(const KAAlarmEventBase& rhs)
3243{
3244 mEventID = rhs.mEventID;
3245 mText = rhs.mText;
3246 mNextMainDateTime = rhs.mNextMainDateTime;
3247 mBgColour = rhs.mBgColour;
3248 mFgColour = rhs.mFgColour;
3249 mFont = rhs.mFont;
3250 mEmailFromIdentity = rhs.mEmailFromIdentity;
3251 mEmailAddresses = rhs.mEmailAddresses;
3252 mEmailSubject = rhs.mEmailSubject;
3253 mEmailAttachments = rhs.mEmailAttachments;
3254 mSoundVolume = rhs.mSoundVolume;
3255 mFadeVolume = rhs.mFadeVolume;
3256 mFadeSeconds = rhs.mFadeSeconds;
3257 mActionType = rhs.mActionType;
3258 mCommandScript = rhs.mCommandScript;
3259 mRepeatCount = rhs.mRepeatCount;
3260 mRepeatInterval = rhs.mRepeatInterval;
3261 mNextRepeat = rhs.mNextRepeat;
3262 mBeep = rhs.mBeep;
3263 mSpeak = rhs.mSpeak;
3264 mRepeatSound = rhs.mRepeatSound;
3265 mRepeatAtLogin = rhs.mRepeatAtLogin;
3266 mDisplaying = rhs.mDisplaying;
3267 mLateCancel = rhs.mLateCancel;
3268 mAutoClose = rhs.mAutoClose;
3269 mEmailBcc = rhs.mEmailBcc;
3270 mConfirmAck = rhs.mConfirmAck;
3271 mDefaultFont = rhs.mDefaultFont;
3272}
3273
3274void KAAlarmEventBase::set(int flags)
3275{
3276 mSpeak = flags & KAEvent::SPEAK;
3277 mBeep = (flags & KAEvent::BEEP) && !mSpeak;
3278 mRepeatSound = flags & KAEvent::REPEAT_SOUND;
3279 mRepeatAtLogin = flags & KAEvent::REPEAT_AT_LOGIN;
3280 mAutoClose = (flags & KAEvent::AUTO_CLOSE) && mLateCancel;
3281 mEmailBcc = flags & KAEvent::EMAIL_BCC;
3282 mConfirmAck = flags & KAEvent::CONFIRM_ACK;
3283 mDisplaying = flags & KAEvent::DISPLAYING_;
3284 mDefaultFont = flags & KAEvent::DEFAULT_FONT;
3285 mCommandScript = flags & KAEvent::SCRIPT;
3286}
3287
3288int KAAlarmEventBase::flags() const
3289{
3290 return (mBeep && !mSpeak ? KAEvent::BEEP : 0)
3291 | (mSpeak ? KAEvent::SPEAK : 0)
3292 | (mRepeatSound ? KAEvent::REPEAT_SOUND : 0)
3293 | (mRepeatAtLogin ? KAEvent::REPEAT_AT_LOGIN : 0)
3294 | (mAutoClose ? KAEvent::AUTO_CLOSE : 0)
3295 | (mEmailBcc ? KAEvent::EMAIL_BCC : 0)
3296 | (mConfirmAck ? KAEvent::CONFIRM_ACK : 0)
3297 | (mDisplaying ? KAEvent::DISPLAYING_ : 0)
3298 | (mDefaultFont ? KAEvent::DEFAULT_FONT : 0)
3299 | (mCommandScript ? KAEvent::SCRIPT : 0);
3300}
3301
3302const TQFont& KAAlarmEventBase::font() const
3303{
3304 return mDefaultFont ? Preferences::messageFont() : mFont;
3305}
3306
3307#ifndef NDEBUG
3308void KAAlarmEventBase::dumpDebug() const
3309{
3310 kdDebug(5950) << "-- mEventID:" << mEventID << ":\n";
3311 kdDebug(5950) << "-- mActionType:" << (mActionType == T_MESSAGE ? "MESSAGE" : mActionType == T_FILE ? "FILE" : mActionType == T_COMMAND ? "COMMAND" : mActionType == T_EMAIL ? "EMAIL" : mActionType == T_AUDIO ? "AUDIO" : "??") << ":\n";
3312 kdDebug(5950) << "-- mText:" << mText << ":\n";
3313 if (mActionType == T_COMMAND)
3314 kdDebug(5950) << "-- mCommandScript:" << (mCommandScript ? "true" : "false") << ":\n";
3315 kdDebug(5950) << "-- mNextMainDateTime:" << mNextMainDateTime.toString() << ":\n";
3316 if (mActionType == T_EMAIL)
3317 {
3318 kdDebug(5950) << "-- mEmail: FromKMail:" << mEmailFromIdentity << ":\n";
3319 kdDebug(5950) << "-- Addresses:" << mEmailAddresses.join(", ") << ":\n";
3320 kdDebug(5950) << "-- Subject:" << mEmailSubject << ":\n";
3321 kdDebug(5950) << "-- Attachments:" << mEmailAttachments.join(", ") << ":\n";
3322 kdDebug(5950) << "-- Bcc:" << (mEmailBcc ? "true" : "false") << ":\n";
3323 }
3324 kdDebug(5950) << "-- mBgColour:" << TQString(mBgColour.name()) << ":\n";
3325 kdDebug(5950) << "-- mFgColour:" << TQString(mFgColour.name()) << ":\n";
3326 kdDebug(5950) << "-- mDefaultFont:" << (mDefaultFont ? "true" : "false") << ":\n";
3327 if (!mDefaultFont)
3328 kdDebug(5950) << "-- mFont:" << TQString(mFont.toString()) << ":\n";
3329 kdDebug(5950) << "-- mBeep:" << (mBeep ? "true" : "false") << ":\n";
3330 kdDebug(5950) << "-- mSpeak:" << (mSpeak ? "true" : "false") << ":\n";
3331 if (mActionType == T_AUDIO)
3332 {
3333 if (mSoundVolume >= 0)
3334 {
3335 kdDebug(5950) << "-- mSoundVolume:" << mSoundVolume << ":\n";
3336 if (mFadeVolume >= 0)
3337 {
3338 kdDebug(5950) << "-- mFadeVolume:" << mFadeVolume << ":\n";
3339 kdDebug(5950) << "-- mFadeSeconds:" << mFadeSeconds << ":\n";
3340 }
3341 else
3342 kdDebug(5950) << "-- mFadeVolume:-:\n";
3343 }
3344 else
3345 kdDebug(5950) << "-- mSoundVolume:-:\n";
3346 kdDebug(5950) << "-- mRepeatSound:" << (mRepeatSound ? "true" : "false") << ":\n";
3347 }
3348 kdDebug(5950) << "-- mConfirmAck:" << (mConfirmAck ? "true" : "false") << ":\n";
3349 kdDebug(5950) << "-- mRepeatAtLogin:" << (mRepeatAtLogin ? "true" : "false") << ":\n";
3350 kdDebug(5950) << "-- mRepeatCount:" << mRepeatCount << ":\n";
3351 kdDebug(5950) << "-- mRepeatInterval:" << mRepeatInterval << ":\n";
3352 kdDebug(5950) << "-- mNextRepeat:" << mNextRepeat << ":\n";
3353 kdDebug(5950) << "-- mDisplaying:" << (mDisplaying ? "true" : "false") << ":\n";
3354 kdDebug(5950) << "-- mLateCancel:" << mLateCancel << ":\n";
3355 kdDebug(5950) << "-- mAutoClose:" << (mAutoClose ? "true" : "false") << ":\n";
3356}
3357#endif
3358
3359
3360/*=============================================================================
3361= Class EmailAddressList
3362=============================================================================*/
3363
3364/******************************************************************************
3365 * Sets the list of email addresses, removing any empty addresses.
3366 * Reply = false if empty addresses were found.
3367 */
3368EmailAddressList& EmailAddressList::operator=(const TQValueList<Person>& addresses)
3369{
3370 clear();
3371 for (TQValueList<Person>::ConstIterator it = addresses.begin(); it != addresses.end(); ++it)
3372 {
3373 if (!(*it).email().isEmpty())
3374 append(*it);
3375 }
3376 return *this;
3377}
3378
3379/******************************************************************************
3380 * Return the email address list as a string, each address being delimited by
3381 * the specified separator string.
3382 */
3383TQString EmailAddressList::join(const TQString& separator) const
3384{
3385 TQString result;
3386 bool first = true;
3387 for (TQValueList<Person>::ConstIterator it = begin(); it != end(); ++it)
3388 {
3389 if (first)
3390 first = false;
3391 else
3392 result += separator;
3393
3394 bool quote = false;
3395 TQString name = (*it).name();
3396 if (!name.isEmpty())
3397 {
3398 // Need to enclose the name in quotes if it has any special characters
3399 int len = name.length();
3400 for (int i = 0; i < len; ++i)
3401 {
3402 TQChar ch = name[i];
3403 if (!ch.isLetterOrNumber())
3404 {
3405 quote = true;
3406 result += '\"';
3407 break;
3408 }
3409 }
3410 result += (*it).name();
3411 result += (quote ? "\" <" : " <");
3412 quote = true; // need angle brackets round email address
3413 }
3414
3415 result += (*it).email();
3416 if (quote)
3417 result += '>';
3418 }
3419 return result;
3420}
3421
3422
3423/*=============================================================================
3424= Static functions
3425=============================================================================*/
3426
3427/******************************************************************************
3428 * Set the specified alarm to be a procedure alarm with the given command line.
3429 * The command line is first split into its program file and arguments before
3430 * initialising the alarm.
3431 */
3432static void setProcedureAlarm(Alarm* alarm, const TQString& commandLine)
3433{
3434 TQString command = TQString();
3435 TQString arguments = TQString();
3436 TQChar quoteChar;
3437 bool quoted = false;
3438 uint posMax = commandLine.length();
3439 uint pos;
3440 for (pos = 0; pos < posMax; ++pos)
3441 {
3442 TQChar ch = commandLine[pos];
3443 if (quoted)
3444 {
3445 if (ch == quoteChar)
3446 {
3447 ++pos; // omit the quote character
3448 break;
3449 }
3450 command += ch;
3451 }
3452 else
3453 {
3454 bool done = false;
3455 switch (ch)
3456 {
3457 case ' ':
3458 case ';':
3459 case '|':
3460 case '<':
3461 case '>':
3462 done = !command.isEmpty();
3463 break;
3464 case '\'':
3465 case '"':
3466 if (command.isEmpty())
3467 {
3468 // Start of a quoted string. Omit the quote character.
3469 quoted = true;
3470 quoteChar = ch;
3471 break;
3472 }
3473 // fall through to default
3474 default:
3475 command += ch;
3476 break;
3477 }
3478 if (done)
3479 break;
3480 }
3481 }
3482
3483 // Skip any spaces after the command
3484 for ( ; pos < posMax && commandLine[pos] == ' '; ++pos) ;
3485 arguments = commandLine.mid(pos);
3486
3487 alarm->setProcedureAlarm(command, arguments);
3488}
represents calendar alarms and events
Provides read and write access to calendar files.
Definition: alarmcalendar.h:37
KAEvent corresponds to a KCal::Event instance.
Definition: alarmevent.h:232
Status
The category of an event, indicated by the middle part of its UID.
Definition: alarmevent.h:270
void setAudioAlarm(const TQString &audioFile=TQString())
bool hasStartOffset() const
TQString audioFile() const
Duration snoozeTime() const
TQString programFile() const
void setRepeatCount(int alarmRepeatCount)
TQString mailSubject() const
TQDateTime time() const
TQString text() const
void setEnabled(bool enable)
void setDisplayAlarm(const TQString &text=TQString())
Duration startOffset() const
void setSnoozeTime(const Duration &alarmSnoozeTime)
TQString programArguments() const
void setEmailAlarm(const TQString &subject, const TQString &text, const TQValueList< Person > &addressees, const TQStringList &attachments=TQStringList())
TQString mailText() const
void setStartOffset(const Duration &)
TQValueList< Person > mailAddresses() const
void setTime(const TQDateTime &alarmTime)
Type type() const
void setProcedureAlarm(const TQString &programFile, const TQString &arguments=TQString())
int repeatCount() const
TQStringList mailAttachments() const
Event::List rawEvents(EventSortField sortField=EventSortUnsorted, SortDirection sortDirection=SortDirectionAscending)
virtual Event::List rawEvents(EventSortField sortField=EventSortUnsorted, SortDirection sortDirection=SortDirectionAscending)=0
void setCustomProperty(const TQCString &app, const TQCString &key, const TQString &value)
TQString customProperty(const TQCString &app, const TQCString &key) const
void removeCustomProperty(const TQCString &app, const TQCString &key)
TQDateTime end(const TQDateTime &start) const
int value() const
int asSeconds() const
void setHasEndDate(bool)
void setTransparency(Transparency transparency)
TQString uid() const
void setUid(const TQString &)
virtual TQDateTime dtStart() const
bool isReadOnly() const
TQString statusStr() const
void setReadOnly(bool readonly)
const Alarm::List & alarms() const
void setSummary(const TQString &summary)
void setCustomStatus(const TQString &status)
TQStringList categories() const
void setFloats(bool f)
virtual void setDtStart(const TQDateTime &dtStart)
void setCategories(const TQStringList &categories)
void clearRecurrence()
TQString summary() const
Recurrence * recurrence() const
void setRevision(int rev)
void setCreated(const TQDateTime &)
void setMinutely(int freq)
void setDaily(int freq)
bool doesRecur() const
void setDuration(int duration)
miscellaneous functions
the KAlarm application object