KCalendarCore

recurrence.cpp
1/*
2 This file is part of kcalcore library.
3
4 SPDX-FileCopyrightText: 1998 Preston Brown <pbrown@kde.org>
5 SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org>
6 SPDX-FileCopyrightText: 2002, 2006 David Jarvie <djarvie@kde.org>
7 SPDX-FileCopyrightText: 2005 Reinhold Kainhofer <kainhofer@kde.org>
8
9 SPDX-License-Identifier: LGPL-2.0-or-later
10*/
11
12#include "incidencebase.h"
13#include "recurrence.h"
14#include "recurrencehelper_p.h"
15#include "utils_p.h"
16
17#include "kcalendarcore_debug.h"
18
19#include <QBitArray>
20#include <QDataStream>
21#include <QTime>
22#include <QTimeZone>
23#include <QHash>
24
25using namespace KCalendarCore;
26
27//@cond PRIVATE
28class Q_DECL_HIDDEN KCalendarCore::Recurrence::Private
29{
30public:
31 Private()
32 : mCachedType(rMax)
33 , mAllDay(false)
34 , mRecurReadOnly(false)
35 {
36 }
37
38 Private(const Private &p)
39 : mRDateTimes(p.mRDateTimes)
40 , mRDateTimePeriods(p.mRDateTimePeriods)
41 , mRDates(p.mRDates)
42 , mExDateTimes(p.mExDateTimes)
43 , mExDates(p.mExDates)
44 , mStartDateTime(p.mStartDateTime)
45 , mCachedType(p.mCachedType)
46 , mAllDay(p.mAllDay)
47 , mRecurReadOnly(p.mRecurReadOnly)
48 {
49 }
50
51 bool operator==(const Private &p) const;
52
53 RecurrenceRule::List mExRules;
55 QList<QDateTime> mRDateTimes;
56 QHash<QDateTime, Period> mRDateTimePeriods; // Map RDate starts with periods if any
57 DateList mRDates;
58 QList<QDateTime> mExDateTimes;
59 DateList mExDates;
60 QDateTime mStartDateTime; // date/time of first recurrence
62
63 // Cache the type of the recurrence with the old system (e.g. MonthlyPos)
64 mutable ushort mCachedType;
65
66 bool mAllDay = false; // the recurrence has no time, just a date
67 bool mRecurReadOnly = false;
68};
69
70bool Recurrence::Private::operator==(const Recurrence::Private &p) const
71{
72 // qCDebug(KCALCORE_LOG) << mStartDateTime << p.mStartDateTime;
73 if (!identical(mStartDateTime, p.mStartDateTime) || mAllDay != p.mAllDay
74 || mRecurReadOnly != p.mRecurReadOnly || mExDates != p.mExDates || mExDateTimes != p.mExDateTimes || mRDates != p.mRDates
75 || mRDateTimes != p.mRDateTimes || mRDateTimePeriods != p.mRDateTimePeriods) {
76 return false;
77 }
78
79 // Compare the rrules, exrules! Assume they have the same order... This only
80 // matters if we have more than one rule (which shouldn't be the default anyway)
81 int i;
82 int end = mRRules.count();
83 if (end != p.mRRules.count()) {
84 return false;
85 }
86 for (i = 0; i < end; ++i) {
87 if (*mRRules[i] != *p.mRRules[i]) {
88 return false;
89 }
90 }
91 end = mExRules.count();
92 if (end != p.mExRules.count()) {
93 return false;
94 }
95 for (i = 0; i < end; ++i) {
96 if (*mExRules[i] != *p.mExRules[i]) {
97 return false;
98 }
99 }
100 return true;
101}
102//@endcond
103
105 : d(new KCalendarCore::Recurrence::Private())
106{
107}
108
110 : RecurrenceRule::RuleObserver()
111 , d(new KCalendarCore::Recurrence::Private(*r.d))
112{
113 int i;
114 int end;
115 d->mRRules.reserve(r.d->mRRules.count());
116 for (i = 0, end = r.d->mRRules.count(); i < end; ++i) {
117 RecurrenceRule *rule = new RecurrenceRule(*r.d->mRRules[i]);
118 d->mRRules.append(rule);
119 rule->addObserver(this);
120 }
121 d->mExRules.reserve(r.d->mExRules.count());
122 for (i = 0, end = r.d->mExRules.count(); i < end; ++i) {
123 RecurrenceRule *rule = new RecurrenceRule(*r.d->mExRules[i]);
124 d->mExRules.append(rule);
125 rule->addObserver(this);
126 }
127}
128
130{
131 qDeleteAll(d->mExRules);
132 qDeleteAll(d->mRRules);
133 delete d;
134}
135
136bool Recurrence::operator==(const Recurrence &recurrence) const
137{
138 return *d == *recurrence.d;
139}
140
141void Recurrence::addObserver(RecurrenceObserver *observer)
142{
143 if (!d->mObservers.contains(observer)) {
144 d->mObservers.append(observer);
145 }
146}
147
148void Recurrence::removeObserver(RecurrenceObserver *observer)
149{
150 d->mObservers.removeAll(observer);
151}
152
154{
155 return d->mStartDateTime;
156}
157
159{
160 return d->mAllDay;
161}
162
163void Recurrence::setAllDay(bool allDay)
164{
165 if (d->mRecurReadOnly || allDay == d->mAllDay) {
166 return;
167 }
168
169 d->mAllDay = allDay;
170 for (int i = 0, end = d->mRRules.count(); i < end; ++i) {
171 d->mRRules[i]->setAllDay(allDay);
172 }
173 for (int i = 0, end = d->mExRules.count(); i < end; ++i) {
174 d->mExRules[i]->setAllDay(allDay);
175 }
176 updated();
177}
178
179RecurrenceRule *Recurrence::defaultRRule(bool create) const
180{
181 if (d->mRRules.isEmpty()) {
182 if (!create || d->mRecurReadOnly) {
183 return nullptr;
184 }
185 RecurrenceRule *rrule = new RecurrenceRule();
186 rrule->setStartDt(startDateTime());
187 const_cast<KCalendarCore::Recurrence *>(this)->addRRule(rrule);
188 return rrule;
189 } else {
190 return d->mRRules[0];
191 }
192}
193
194RecurrenceRule *Recurrence::defaultRRuleConst() const
195{
196 return d->mRRules.isEmpty() ? nullptr : d->mRRules[0];
197}
198
199void Recurrence::updated()
200{
201 // recurrenceType() re-calculates the type if it's rMax
202 d->mCachedType = rMax;
203 for (int i = 0, end = d->mObservers.count(); i < end; ++i) {
204 if (d->mObservers[i]) {
205 d->mObservers[i]->recurrenceUpdated(this);
206 }
207 }
208}
209
211{
212 return !d->mRRules.isEmpty() || !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty();
213}
214
216{
217 if (d->mCachedType == rMax) {
218 d->mCachedType = recurrenceType(defaultRRuleConst());
219 }
220 return d->mCachedType;
221}
222
224{
225 if (!rrule) {
226 return rNone;
227 }
228 RecurrenceRule::PeriodType type = rrule->recurrenceType();
229
230 // BYSETPOS, BYWEEKNUMBER and BYSECOND were not supported in old versions
231 if (!rrule->bySetPos().isEmpty() || !rrule->bySeconds().isEmpty() || !rrule->byWeekNumbers().isEmpty()) {
232 return rOther;
233 }
234
235 // It wasn't possible to set BYMINUTES, BYHOUR etc. by the old code. So if
236 // it's set, it's none of the old types
237 if (!rrule->byMinutes().isEmpty() || !rrule->byHours().isEmpty()) {
238 return rOther;
239 }
240
241 // Possible combinations were:
242 // BYDAY: with WEEKLY, MONTHLY, YEARLY
243 // BYMONTHDAY: with MONTHLY, YEARLY
244 // BYMONTH: with YEARLY
245 // BYYEARDAY: with YEARLY
246 if ((!rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly) || (!rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly)) {
247 return rOther;
248 }
249 if (!rrule->byDays().isEmpty()) {
250 if (type != RecurrenceRule::rYearly && type != RecurrenceRule::rMonthly && type != RecurrenceRule::rWeekly) {
251 return rOther;
252 }
253 }
254
255 switch (type) {
256 case RecurrenceRule::rNone:
257 return rNone;
258 case RecurrenceRule::rMinutely:
259 return rMinutely;
260 case RecurrenceRule::rHourly:
261 return rHourly;
262 case RecurrenceRule::rDaily:
263 return rDaily;
264 case RecurrenceRule::rWeekly:
265 return rWeekly;
266 case RecurrenceRule::rMonthly: {
267 if (rrule->byDays().isEmpty()) {
268 return rMonthlyDay;
269 } else if (rrule->byMonthDays().isEmpty()) {
270 return rMonthlyPos;
271 } else {
272 return rOther; // both position and date specified
273 }
274 }
275 case RecurrenceRule::rYearly: {
276 // Possible combinations:
277 // rYearlyMonth: [BYMONTH &] BYMONTHDAY
278 // rYearlyDay: BYYEARDAY
279 // rYearlyPos: [BYMONTH &] BYDAY
280 if (!rrule->byDays().isEmpty()) {
281 // can only by rYearlyPos
282 if (rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty()) {
283 return rYearlyPos;
284 } else {
285 return rOther;
286 }
287 } else if (!rrule->byYearDays().isEmpty()) {
288 // Can only be rYearlyDay
289 if (rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty()) {
290 return rYearlyDay;
291 } else {
292 return rOther;
293 }
294 } else {
295 return rYearlyMonth;
296 }
297 }
298 default:
299 return rOther;
300 }
301}
302
303bool Recurrence::recursOn(const QDate &qd, const QTimeZone &timeZone) const
304{
305 // Don't waste time if date is before the start of the recurrence
306 if (QDateTime(qd, QTime(23, 59, 59), timeZone) < d->mStartDateTime) {
307 return false;
308 }
309
310 // First handle dates. Exrules override
311 if (std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), qd)) {
312 return false;
313 }
314
315 int i;
316 int end;
317 // For all-day events a matching exrule excludes the whole day
318 // since exclusions take precedence over inclusions, we know it can't occur on that day.
319 if (allDay()) {
320 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
321 if (d->mExRules[i]->recursOn(qd, timeZone)) {
322 return false;
323 }
324 }
325 }
326
327 if (std::binary_search(d->mRDates.constBegin(), d->mRDates.constEnd(), qd)) {
328 return true;
329 }
330
331 // Check if it might recur today at all.
332 bool recurs = (startDate() == qd);
333 for (i = 0, end = d->mRDateTimes.count(); i < end && !recurs; ++i) {
334 recurs = (d->mRDateTimes[i].toTimeZone(timeZone).date() == qd);
335 }
336 for (i = 0, end = d->mRRules.count(); i < end && !recurs; ++i) {
337 recurs = d->mRRules[i]->recursOn(qd, timeZone);
338 }
339 // If the event wouldn't recur at all, simply return false, don't check ex*
340 if (!recurs) {
341 return false;
342 }
343
344 // Check if there are any times for this day excluded, either by exdate or exrule:
345 bool exon = false;
346 for (i = 0, end = d->mExDateTimes.count(); i < end && !exon; ++i) {
347 exon = (d->mExDateTimes[i].toTimeZone(timeZone).date() == qd);
348 }
349 if (!allDay()) { // we have already checked all-day times above
350 for (i = 0, end = d->mExRules.count(); i < end && !exon; ++i) {
351 exon = d->mExRules[i]->recursOn(qd, timeZone);
352 }
353 }
354
355 if (!exon) {
356 // Simple case, nothing on that day excluded, return the value from before
357 return recurs;
358 } else {
359 // Harder part: I don't think there is any way other than to calculate the
360 // whole list of items for that day.
361 // TODO: consider whether it would be more efficient to call
362 // Rule::recurTimesOn() instead of Rule::recursOn() from the start
363 TimeList timesForDay(recurTimesOn(qd, timeZone));
364 return !timesForDay.isEmpty();
365 }
366}
367
368bool Recurrence::recursAt(const QDateTime &dt) const
369{
370 // Convert to recurrence's time zone for date comparisons, and for more efficient time comparisons
371 const auto dtrecur = dt.toTimeZone(d->mStartDateTime.timeZone());
372
373 // if it's excluded anyway, don't bother to check if it recurs at all.
374 if (std::binary_search(d->mExDateTimes.constBegin(), d->mExDateTimes.constEnd(), dtrecur)
375 || std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), dtrecur.date())) {
376 return false;
377 }
378 int i;
379 int end;
380 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
381 if (d->mExRules[i]->recursAt(dtrecur)) {
382 return false;
383 }
384 }
385
386 // Check explicit recurrences, then rrules.
387 if (startDateTime() == dtrecur || std::binary_search(d->mRDateTimes.constBegin(), d->mRDateTimes.constEnd(), dtrecur)) {
388 return true;
389 }
390 for (i = 0, end = d->mRRules.count(); i < end; ++i) {
391 if (d->mRRules[i]->recursAt(dtrecur)) {
392 return true;
393 }
394 }
395
396 return false;
397}
398
399/** Calculates the cumulative end of the whole recurrence (rdates and rrules).
400 If any rrule is infinite, or the recurrence doesn't have any rrules or
401 rdates, an invalid date is returned. */
403{
405 dts << startDateTime();
406 if (!d->mRDates.isEmpty()) {
407 dts << QDateTime(d->mRDates.last(), QTime(0, 0, 0), d->mStartDateTime.timeZone());
408 }
409 if (!d->mRDateTimes.isEmpty()) {
410 dts << d->mRDateTimes.last();
411 }
412 for (int i = 0, end = d->mRRules.count(); i < end; ++i) {
413 auto rl = d->mRRules[i]->endDt();
414 // if any of the rules is infinite, the whole recurrence is
415 if (!rl.isValid()) {
416 return QDateTime();
417 }
418 dts << rl;
419 }
420 sortAndRemoveDuplicates(dts);
421 return dts.isEmpty() ? QDateTime() : dts.last();
422}
423
424/** Calculates the cumulative end of the whole recurrence (rdates and rrules).
425 If any rrule is infinite, or the recurrence doesn't have any rrules or
426 rdates, an invalid date is returned. */
428{
429 QDateTime end(endDateTime());
430 return end.isValid() ? end.date() : QDate();
431}
432
434{
435 QDateTime dt(date, d->mStartDateTime.time(), d->mStartDateTime.timeZone());
436 if (allDay()) {
437 dt.setTime(QTime(23, 59, 59));
438 }
439 setEndDateTime(dt);
440}
441
443{
444 if (d->mRecurReadOnly) {
445 return;
446 }
447 RecurrenceRule *rrule = defaultRRule(true);
448 if (!rrule) {
449 return;
450 }
451
452 // If the recurrence rule has a duration, and we're trying to set an invalid end date,
453 // we have to skip setting it to avoid setting the field dirty.
454 // The end date is already invalid since the duration is set and end date/duration
455 // are mutually exclusive.
456 // We can't use inequality check below, because endDt() also returns a valid date
457 // for a duration (it is calculated from the duration).
458 if (rrule->duration() > 0 && !dateTime.isValid()) {
459 return;
460 }
461
462 if (!identical(dateTime, rrule->endDt())) {
463 rrule->setEndDt(dateTime);
464 updated();
465 }
466}
467
469{
470 RecurrenceRule *rrule = defaultRRuleConst();
471 return rrule ? rrule->duration() : 0;
472}
473
474int Recurrence::durationTo(const QDateTime &datetime) const
475{
476 // Emulate old behavior: This is just an interface to the first rule!
477 RecurrenceRule *rrule = defaultRRuleConst();
478 return rrule ? rrule->durationTo(datetime) : 0;
479}
480
481int Recurrence::durationTo(const QDate &date) const
482{
483 return durationTo(QDateTime(date, QTime(23, 59, 59), d->mStartDateTime.timeZone()));
484}
485
486void Recurrence::setDuration(int duration)
487{
488 if (d->mRecurReadOnly) {
489 return;
490 }
491
492 RecurrenceRule *rrule = defaultRRule(true);
493 if (!rrule) {
494 return;
495 }
496
497 if (duration != rrule->duration()) {
498 rrule->setDuration(duration);
499 updated();
500 }
501}
502
503void Recurrence::shiftTimes(const QTimeZone &oldTz, const QTimeZone &newTz)
504{
505 if (d->mRecurReadOnly) {
506 return;
507 }
508
509 d->mStartDateTime = d->mStartDateTime.toTimeZone(oldTz);
510 d->mStartDateTime.setTimeZone(newTz);
511
512 QHash<QDateTime, Period> oldPeriods = d->mRDateTimePeriods;
513
514 for (auto &rDt : d->mRDateTimes) {
515 auto periodIt = oldPeriods.find(rDt);
516 periodIt->shiftTimes(oldTz, newTz);
517 rDt = rDt.toTimeZone(oldTz);
518 rDt.setTimeZone(newTz);
519 // Now there are QDateTime objects in the hash? is this shifting times?
520 d->mRDateTimePeriods.insert(rDt, *periodIt);
521 }
522
523 for (auto &exDt : d->mExDateTimes) {
524 exDt = exDt.toTimeZone(oldTz);
525 exDt.setTimeZone(newTz);
526 }
527
528 for (auto &rr : d->mRRules) {
529 rr->shiftTimes(oldTz, newTz);
530 }
531
532 for (auto exR : d->mExRules) {
533 exR->shiftTimes(oldTz, newTz);
534 }
535}
536
538{
539 if (d->mRecurReadOnly) {
540 return;
541 }
542 qDeleteAll(d->mRRules);
543 d->mRRules.clear();
544 updated();
545}
546
548{
549 if (d->mRecurReadOnly) {
550 return;
551 }
552 qDeleteAll(d->mRRules);
553 d->mRRules.clear();
554 qDeleteAll(d->mExRules);
555 d->mExRules.clear();
556 d->mRDates.clear();
557 d->mRDateTimes.clear();
558 d->mRDateTimePeriods.clear();
559 d->mExDates.clear();
560 d->mExDateTimes.clear();
561 d->mCachedType = rMax;
562 updated();
563}
564
566{
567 d->mRecurReadOnly = readOnly;
568}
569
571{
572 return d->mRecurReadOnly;
573}
574
576{
577 return d->mStartDateTime.date();
578}
579
581{
582 if (d->mRecurReadOnly) {
583 return;
584 }
585 d->mStartDateTime = start;
586 setAllDay(isAllDay); // set all RRULEs and EXRULEs
587
588 int i;
589 int end;
590 for (i = 0, end = d->mRRules.count(); i < end; ++i) {
591 d->mRRules[i]->setStartDt(start);
592 }
593 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
594 d->mExRules[i]->setStartDt(start);
595 }
596 updated();
597}
598
600{
601 RecurrenceRule *rrule = defaultRRuleConst();
602 return rrule ? rrule->frequency() : 0;
603}
604
605// Emulate the old behaviour. Make this methods just an interface to the
606// first rrule
608{
609 if (d->mRecurReadOnly || freq <= 0) {
610 return;
611 }
612
613 RecurrenceRule *rrule = defaultRRule(true);
614 if (rrule) {
615 rrule->setFrequency(freq);
616 }
617 updated();
618}
619
620// WEEKLY
621
623{
624 RecurrenceRule *rrule = defaultRRuleConst();
625 return rrule ? rrule->weekStart() : 1;
626}
627
628// Emulate the old behavior
630{
631 QBitArray days(7);
632 days.fill(0);
633 RecurrenceRule *rrule = defaultRRuleConst();
634 if (rrule) {
635 const QList<RecurrenceRule::WDayPos> &bydays = rrule->byDays();
636 for (int i = 0; i < bydays.size(); ++i) {
637 if (bydays.at(i).pos() == 0) {
638 days.setBit(bydays.at(i).day() - 1);
639 }
640 }
641 }
642 return days;
643}
644
645// MONTHLY
646
647// Emulate the old behavior
649{
650 RecurrenceRule *rrule = defaultRRuleConst();
651 if (rrule) {
652 return rrule->byMonthDays();
653 } else {
654 return QList<int>();
655 }
656}
657
658// Emulate the old behavior
660{
661 RecurrenceRule *rrule = defaultRRuleConst();
662 return rrule ? rrule->byDays() : QList<RecurrenceRule::WDayPos>();
663}
664
665// YEARLY
666
668{
669 RecurrenceRule *rrule = defaultRRuleConst();
670 return rrule ? rrule->byYearDays() : QList<int>();
671}
672
674{
675 return monthDays();
676}
677
679{
680 RecurrenceRule *rrule = defaultRRuleConst();
681 return rrule ? rrule->byMonths() : QList<int>();
682}
683
688
689RecurrenceRule *Recurrence::setNewRecurrenceType(RecurrenceRule::PeriodType type, int freq)
690{
691 if (d->mRecurReadOnly || freq <= 0) {
692 return nullptr;
693 }
694
695 // Ignore the call if nothing has change
696 if (defaultRRuleConst() && defaultRRuleConst()->recurrenceType() == type && frequency() == freq) {
697 return nullptr;
698 }
699
700 qDeleteAll(d->mRRules);
701 d->mRRules.clear();
702 updated();
703 RecurrenceRule *rrule = defaultRRule(true);
704 if (!rrule) {
705 return nullptr;
706 }
707 rrule->setRecurrenceType(type);
708 rrule->setFrequency(freq);
709 rrule->setDuration(-1);
710 return rrule;
711}
712
714{
715 if (setNewRecurrenceType(RecurrenceRule::rMinutely, _rFreq)) {
716 updated();
717 }
718}
719
720void Recurrence::setHourly(int _rFreq)
721{
722 if (setNewRecurrenceType(RecurrenceRule::rHourly, _rFreq)) {
723 updated();
724 }
725}
726
727void Recurrence::setDaily(int _rFreq)
728{
729 if (setNewRecurrenceType(RecurrenceRule::rDaily, _rFreq)) {
730 updated();
731 }
732}
733
734void Recurrence::setWeekly(int freq, int weekStart)
735{
736 RecurrenceRule *rrule = setNewRecurrenceType(RecurrenceRule::rWeekly, freq);
737 if (!rrule) {
738 return;
739 }
740 rrule->setWeekStart(weekStart);
741 updated();
742}
743
744void Recurrence::setWeekly(int freq, const QBitArray &days, int weekStart)
745{
746 setWeekly(freq, weekStart);
748}
749
751{
753}
754
756{
757 if (setNewRecurrenceType(RecurrenceRule::rMonthly, freq)) {
758 updated();
759 }
760}
761
762void Recurrence::addMonthlyPos(short pos, const QBitArray &days)
763{
764 // Allow 53 for yearly!
765 if (d->mRecurReadOnly || pos > 53 || pos < -53) {
766 return;
767 }
768
769 RecurrenceRule *rrule = defaultRRule(false);
770 if (!rrule) {
771 return;
772 }
773 bool changed = false;
774 QList<RecurrenceRule::WDayPos> positions = rrule->byDays();
775
776 for (int i = 0; i < 7; ++i) {
777 if (days.testBit(i)) {
778 RecurrenceRule::WDayPos p(pos, i + 1);
779 if (!positions.contains(p)) {
780 changed = true;
781 positions.append(p);
782 }
783 }
784 }
785 if (changed) {
786 rrule->setByDays(positions);
787 updated();
788 }
789}
790
791void Recurrence::addMonthlyPos(short pos, ushort day)
792{
793 // Allow 53 for yearly!
794 if (d->mRecurReadOnly || pos > 53 || pos < -53) {
795 return;
796 }
797
798 RecurrenceRule *rrule = defaultRRule(false);
799 if (!rrule) {
800 return;
801 }
802 QList<RecurrenceRule::WDayPos> positions = rrule->byDays();
803
804 RecurrenceRule::WDayPos p(pos, day);
805 if (!positions.contains(p)) {
806 positions.append(p);
807 setMonthlyPos(positions);
808 }
809}
810
811void Recurrence::setMonthlyPos(const QList<RecurrenceRule::WDayPos> &monthlyDays)
812{
813 if (d->mRecurReadOnly) {
814 return;
815 }
816
817 RecurrenceRule *rrule = defaultRRule(true);
818 if (!rrule) {
819 return;
820 }
821
822 // TODO: sort lists
823 // the position inside the list has no meaning, so sort the list before testing if it changed
824
825 if (monthlyDays != rrule->byDays()) {
826 rrule->setByDays(monthlyDays);
827 updated();
828 }
829}
830
832{
833 if (d->mRecurReadOnly || day > 31 || day < -31) {
834 return;
835 }
836
837 RecurrenceRule *rrule = defaultRRule(true);
838 if (!rrule) {
839 return;
840 }
841
842 QList<int> monthDays = rrule->byMonthDays();
843 if (!monthDays.contains(day)) {
844 monthDays.append(day);
845 setMonthlyDate(monthDays);
846 }
847}
848
849void Recurrence::setMonthlyDate(const QList<int> &monthlyDays)
850{
851 if (d->mRecurReadOnly) {
852 return;
853 }
854
855 RecurrenceRule *rrule = defaultRRule(true);
856 if (!rrule) {
857 return;
858 }
859
860 QList<int> mD(monthlyDays);
861 QList<int> rbD(rrule->byMonthDays());
862
863 sortAndRemoveDuplicates(mD);
864 sortAndRemoveDuplicates(rbD);
865
866 if (mD != rbD) {
867 rrule->setByMonthDays(monthlyDays);
868 updated();
869 }
870}
871
873{
874 if (setNewRecurrenceType(RecurrenceRule::rYearly, freq)) {
875 updated();
876 }
877}
878
879// Daynumber within year
881{
882 RecurrenceRule *rrule = defaultRRule(false); // It must already exist!
883 if (!rrule) {
884 return;
885 }
886
887 QList<int> days = rrule->byYearDays();
888 if (!days.contains(day)) {
889 days << day;
890 setYearlyDay(days);
891 }
892}
893
894void Recurrence::setYearlyDay(const QList<int> &days)
895{
896 RecurrenceRule *rrule = defaultRRule(false); // It must already exist!
897 if (!rrule) {
898 return;
899 }
900
901 QList<int> d(days);
902 QList<int> bYD(rrule->byYearDays());
903
904 sortAndRemoveDuplicates(d);
905 sortAndRemoveDuplicates(bYD);
906
907 if (d != bYD) {
908 rrule->setByYearDays(days);
909 updated();
910 }
911}
912
913// day part of date within year
915{
916 addMonthlyDate(day);
917}
918
919void Recurrence::setYearlyDate(const QList<int> &dates)
920{
921 setMonthlyDate(dates);
922}
923
924// day part of date within year, given as position (n-th weekday)
925void Recurrence::addYearlyPos(short pos, const QBitArray &days)
926{
927 addMonthlyPos(pos, days);
928}
929
930void Recurrence::setYearlyPos(const QList<RecurrenceRule::WDayPos> &days)
931{
932 setMonthlyPos(days);
933}
934
935// month part of date within year
937{
938 if (d->mRecurReadOnly || month < 1 || month > 12) {
939 return;
940 }
941
942 RecurrenceRule *rrule = defaultRRule(false);
943 if (!rrule) {
944 return;
945 }
946
947 QList<int> months = rrule->byMonths();
948 if (!months.contains(month)) {
949 months << month;
950 setYearlyMonth(months);
951 }
952}
953
954void Recurrence::setYearlyMonth(const QList<int> &months)
955{
956 if (d->mRecurReadOnly) {
957 return;
958 }
959
960 RecurrenceRule *rrule = defaultRRule(false);
961 if (!rrule) {
962 return;
963 }
964
965 QList<int> m(months);
966 QList<int> bM(rrule->byMonths());
967
968 sortAndRemoveDuplicates(m);
969 sortAndRemoveDuplicates(bM);
970
971 if (m != bM) {
972 rrule->setByMonths(months);
973 updated();
974 }
975}
976
977TimeList Recurrence::recurTimesOn(const QDate &date, const QTimeZone &timeZone) const
978{
979 // qCDebug(KCALCORE_LOG) << "recurTimesOn(" << date << ")";
980 int i;
981 int end;
982 TimeList times;
983
984 // The whole day is excepted
985 if (std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), date)) {
986 return times;
987 }
988
989 // EXRULE takes precedence over RDATE entries, so for all-day events,
990 // a matching excule also excludes the whole day automatically
991 if (allDay()) {
992 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
993 if (d->mExRules[i]->recursOn(date, timeZone)) {
994 return times;
995 }
996 }
997 }
998
999 QDateTime dt = startDateTime().toTimeZone(timeZone);
1000 if (dt.date() == date) {
1001 times << dt.time();
1002 }
1003
1004 bool foundDate = false;
1005 for (i = 0, end = d->mRDateTimes.count(); i < end; ++i) {
1006 dt = d->mRDateTimes[i].toTimeZone(timeZone);
1007 if (dt.date() == date) {
1008 times << dt.time();
1009 foundDate = true;
1010 } else if (foundDate) {
1011 break; // <= Assume that the rdatetime list is sorted
1012 }
1013 }
1014 for (i = 0, end = d->mRRules.count(); i < end; ++i) {
1015 times += d->mRRules[i]->recurTimesOn(date, timeZone);
1016 }
1017 sortAndRemoveDuplicates(times);
1018
1019 foundDate = false;
1020 TimeList extimes;
1021 for (i = 0, end = d->mExDateTimes.count(); i < end; ++i) {
1022 dt = d->mExDateTimes[i].toTimeZone(timeZone);
1023 if (dt.date() == date) {
1024 extimes << dt.time();
1025 foundDate = true;
1026 } else if (foundDate) {
1027 break;
1028 }
1029 }
1030 if (!allDay()) { // we have already checked all-day times above
1031 for (i = 0, end = d->mExRules.count(); i < end; ++i) {
1032 extimes += d->mExRules[i]->recurTimesOn(date, timeZone);
1033 }
1034 }
1035 sortAndRemoveDuplicates(extimes);
1036 inplaceSetDifference(times, extimes);
1037 return times;
1038}
1039
1041{
1042 int i;
1043 int count;
1044 QList<QDateTime> times;
1045 for (i = 0, count = d->mRRules.count(); i < count; ++i) {
1046 times += d->mRRules[i]->timesInInterval(start, end);
1047 }
1048
1049 // add rdatetimes that fit in the interval
1050 for (i = 0, count = d->mRDateTimes.count(); i < count; ++i) {
1051 if (d->mRDateTimes[i] >= start && d->mRDateTimes[i] <= end) {
1052 times += d->mRDateTimes[i];
1053 }
1054 }
1055
1056 // add rdates that fit in the interval
1057 QDateTime kdt = d->mStartDateTime;
1058 for (i = 0, count = d->mRDates.count(); i < count; ++i) {
1059 kdt.setDate(d->mRDates[i]);
1060 if (kdt >= start && kdt <= end) {
1061 times += kdt;
1062 }
1063 }
1064
1065 // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list
1066 // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include
1067 // mStartDateTime.
1068 // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly
1069 // add mStartDateTime to the list, otherwise we won't see the first occurrence.
1070 if ((!d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty()) && d->mRRules.isEmpty() && start <= d->mStartDateTime && end >= d->mStartDateTime) {
1071 times += d->mStartDateTime;
1072 }
1073
1074 sortAndRemoveDuplicates(times);
1075
1076 // Remove excluded times
1077 int idt = 0;
1078 int enddt = times.count();
1079 for (i = 0, count = d->mExDates.count(); i < count && idt < enddt; ++i) {
1080 while (idt < enddt && times[idt].date() < d->mExDates[i]) {
1081 ++idt;
1082 }
1083 while (idt < enddt && times[idt].date() == d->mExDates[i]) {
1084 times.removeAt(idt);
1085 --enddt;
1086 }
1087 }
1088 QList<QDateTime> extimes;
1089 for (i = 0, count = d->mExRules.count(); i < count; ++i) {
1090 extimes += d->mExRules[i]->timesInInterval(start, end);
1091 }
1092 extimes += d->mExDateTimes;
1093 sortAndRemoveDuplicates(extimes);
1094 inplaceSetDifference(times, extimes);
1095 return times;
1096}
1097
1099{
1100 QDateTime nextDT = preDateTime;
1101 // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
1102 // the exrule is identical to the rrule). If an occurrence is found, break
1103 // out of the loop by returning that QDateTime
1104 // TODO_Recurrence: Is a loop counter of 1000 really okay? I mean for secondly
1105 // recurrence, an exdate might exclude more than 1000 intervals!
1106 int loop = 0;
1107 while (loop < 1000) {
1108 // Outline of the algo:
1109 // 1) Find the next date/time after preDateTime when the event could recur
1110 // 1.0) Add the start date if it's after preDateTime
1111 // 1.1) Use the next occurrence from the explicit RDATE lists
1112 // 1.2) Add the next recurrence for each of the RRULEs
1113 // 2) Take the earliest recurrence of these = QDateTime nextDT
1114 // 3) If that date/time is not excluded, either explicitly by an EXDATE or
1115 // by an EXRULE, return nextDT as the next date/time of the recurrence
1116 // 4) If it's excluded, start all at 1), but starting at nextDT (instead
1117 // of preDateTime). Loop at most 1000 times.
1118 ++loop;
1119 // First, get the next recurrence from the RDate lists
1120 QList<QDateTime> dates;
1121 if (nextDT < startDateTime()) {
1122 dates << startDateTime();
1123 }
1124
1125 // Assume that the rdatetime list is sorted
1126 const auto it = std::upper_bound(d->mRDateTimes.constBegin(), d->mRDateTimes.constEnd(), nextDT);
1127 if (it != d->mRDateTimes.constEnd()) {
1128 dates << *it;
1129 }
1130
1131 QDateTime kdt(startDateTime());
1132 for (const auto &date : std::as_const(d->mRDates)) {
1133 kdt.setDate(date);
1134 if (kdt > nextDT) {
1135 dates << kdt;
1136 break;
1137 }
1138 }
1139
1140 // Add the next occurrences from all RRULEs.
1141 for (const auto &rule : std::as_const(d->mRRules)) {
1142 QDateTime dt = rule->getNextDate(nextDT);
1143 if (dt.isValid()) {
1144 dates << dt;
1145 }
1146 }
1147
1148 // Take the first of these (all others can't be used later on)
1149 sortAndRemoveDuplicates(dates);
1150 if (dates.isEmpty()) {
1151 return QDateTime();
1152 }
1153 nextDT = dates.first();
1154
1155 // Check if that date/time is excluded explicitly or by an exrule:
1156 if (!std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), nextDT.date())
1157 && !std::binary_search(d->mExDateTimes.constBegin(), d->mExDateTimes.constEnd(), nextDT)) {
1158 bool allowed = true;
1159 for (const auto &rule : std::as_const(d->mExRules)) {
1160 allowed = allowed && !rule->recursAt(nextDT);
1161 }
1162 if (allowed) {
1163 return nextDT;
1164 }
1165 }
1166 }
1167
1168 // Couldn't find a valid occurrences in 1000 loops, something is wrong!
1169 return QDateTime();
1170}
1171
1173{
1174 QDateTime prevDT = afterDateTime;
1175 // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
1176 // the exrule is identical to the rrule). If an occurrence is found, break
1177 // out of the loop by returning that QDateTime
1178 int loop = 0;
1179 while (loop < 1000) {
1180 // Outline of the algo:
1181 // 1) Find the next date/time after preDateTime when the event could recur
1182 // 1.1) Use the next occurrence from the explicit RDATE lists
1183 // 1.2) Add the next recurrence for each of the RRULEs
1184 // 2) Take the earliest recurrence of these = QDateTime nextDT
1185 // 3) If that date/time is not excluded, either explicitly by an EXDATE or
1186 // by an EXRULE, return nextDT as the next date/time of the recurrence
1187 // 4) If it's excluded, start all at 1), but starting at nextDT (instead
1188 // of preDateTime). Loop at most 1000 times.
1189 ++loop;
1190 // First, get the next recurrence from the RDate lists
1191 QList<QDateTime> dates;
1192 if (prevDT > startDateTime()) {
1193 dates << startDateTime();
1194 }
1195
1196 const auto it = strictLowerBound(d->mRDateTimes.constBegin(), d->mRDateTimes.constEnd(), prevDT);
1197 if (it != d->mRDateTimes.constEnd()) {
1198 dates << *it;
1199 }
1200
1201 QDateTime kdt(startDateTime());
1202 for (const auto &date : std::as_const(d->mRDates)) {
1203 kdt.setDate(date);
1204 if (kdt < prevDT) {
1205 dates << kdt;
1206 break;
1207 }
1208 }
1209
1210 // Add the previous occurrences from all RRULEs.
1211 for (const auto &rule : std::as_const(d->mRRules)) {
1212 QDateTime dt = rule->getPreviousDate(prevDT);
1213 if (dt.isValid()) {
1214 dates << dt;
1215 }
1216 }
1217
1218 // Take the last of these (all others can't be used later on)
1219 sortAndRemoveDuplicates(dates);
1220 if (dates.isEmpty()) {
1221 return QDateTime();
1222 }
1223 prevDT = dates.last();
1224
1225 // Check if that date/time is excluded explicitly or by an exrule:
1226 if (!std::binary_search(d->mExDates.constBegin(), d->mExDates.constEnd(), prevDT.date())
1227 && !std::binary_search(d->mExDateTimes.constBegin(), d->mExDateTimes.constEnd(), prevDT)) {
1228 bool allowed = true;
1229 for (const auto &rule : std::as_const(d->mExRules)) {
1230 allowed = allowed && !rule->recursAt(prevDT);
1231 }
1232 if (allowed) {
1233 return prevDT;
1234 }
1235 }
1236 }
1237
1238 // Couldn't find a valid occurrences in 1000 loops, something is wrong!
1239 return QDateTime();
1240}
1241
1242/***************************** PROTECTED FUNCTIONS ***************************/
1243
1244RecurrenceRule::List Recurrence::rRules() const
1245{
1246 return d->mRRules;
1247}
1248
1250{
1251 if (d->mRecurReadOnly || !rrule) {
1252 return;
1253 }
1254
1255 rrule->setAllDay(d->mAllDay);
1256 d->mRRules.append(rrule);
1257 rrule->addObserver(this);
1258 updated();
1259}
1260
1262{
1263 if (d->mRecurReadOnly) {
1264 return;
1265 }
1266
1267 d->mRRules.removeAll(rrule);
1268 rrule->removeObserver(this);
1269 updated();
1270}
1271
1273{
1274 if (d->mRecurReadOnly) {
1275 return;
1276 }
1277
1278 d->mRRules.removeAll(rrule);
1279 delete rrule;
1280 updated();
1281}
1282
1283RecurrenceRule::List Recurrence::exRules() const
1284{
1285 return d->mExRules;
1286}
1287
1289{
1290 if (d->mRecurReadOnly || !exrule) {
1291 return;
1292 }
1293
1294 exrule->setAllDay(d->mAllDay);
1295 d->mExRules.append(exrule);
1296 exrule->addObserver(this);
1297 updated();
1298}
1299
1301{
1302 if (d->mRecurReadOnly) {
1303 return;
1304 }
1305
1306 d->mExRules.removeAll(exrule);
1307 exrule->removeObserver(this);
1308 updated();
1309}
1310
1312{
1313 if (d->mRecurReadOnly) {
1314 return;
1315 }
1316
1317 d->mExRules.removeAll(exrule);
1318 delete exrule;
1319 updated();
1320}
1321
1322QList<QDateTime> Recurrence::rDateTimes() const
1323{
1324 return d->mRDateTimes;
1325}
1326
1327void Recurrence::setRDateTimes(const QList<QDateTime> &rdates)
1328{
1329 if (d->mRecurReadOnly) {
1330 return;
1331 }
1332
1333 d->mRDateTimes = rdates;
1334 sortAndRemoveDuplicates(d->mRDateTimes);
1335 d->mRDateTimePeriods.clear();
1336 updated();
1337}
1338
1339void Recurrence::addRDateTime(const QDateTime &rdate)
1340{
1341 if (d->mRecurReadOnly) {
1342 return;
1343 }
1344
1345 setInsert(d->mRDateTimes, rdate);
1346 updated();
1347}
1348
1350{
1351 if (d->mRecurReadOnly) {
1352 return;
1353 }
1354
1355 setInsert(d->mRDateTimes, period.start());
1356 d->mRDateTimePeriods.insert(period.start(), period);
1357 updated();
1358}
1359
1361{
1362 return d->mRDateTimePeriods.value(rdate);
1363}
1364
1365DateList Recurrence::rDates() const
1366{
1367 return d->mRDates;
1368}
1369
1370void Recurrence::setRDates(const DateList &rdates)
1371{
1372 if (d->mRecurReadOnly) {
1373 return;
1374 }
1375
1376 d->mRDates = rdates;
1377 sortAndRemoveDuplicates(d->mRDates);
1378 updated();
1379}
1380
1381void Recurrence::addRDate(const QDate &rdate)
1382{
1383 if (d->mRecurReadOnly) {
1384 return;
1385 }
1386
1387 setInsert(d->mRDates, rdate);
1388 updated();
1389}
1390
1391QList<QDateTime> Recurrence::exDateTimes() const
1392{
1393 return d->mExDateTimes;
1394}
1395
1396void Recurrence::setExDateTimes(const QList<QDateTime> &exdates)
1397{
1398 if (d->mRecurReadOnly) {
1399 return;
1400 }
1401
1402 d->mExDateTimes = exdates;
1403 sortAndRemoveDuplicates(d->mExDateTimes);
1404}
1405
1406void Recurrence::addExDateTime(const QDateTime &exdate)
1407{
1408 if (d->mRecurReadOnly) {
1409 return;
1410 }
1411
1412 setInsert(d->mExDateTimes, exdate);
1413 updated();
1414}
1415
1416DateList Recurrence::exDates() const
1417{
1418 return d->mExDates;
1419}
1420
1421void Recurrence::setExDates(const DateList &exdates)
1422{
1423 if (d->mRecurReadOnly) {
1424 return;
1425 }
1426
1427 DateList l = exdates;
1428 sortAndRemoveDuplicates(l);
1429
1430 if (d->mExDates != l) {
1431 d->mExDates = l;
1432 updated();
1433 }
1434}
1435
1436void Recurrence::addExDate(const QDate &exdate)
1437{
1438 if (d->mRecurReadOnly) {
1439 return;
1440 }
1441
1442 setInsert(d->mExDates, exdate);
1443 updated();
1444}
1445
1446void Recurrence::recurrenceChanged(RecurrenceRule *)
1447{
1448 updated();
1449}
1450
1451// %%%%%%%%%%%%%%%%%% end:Recurrencerule %%%%%%%%%%%%%%%%%%
1452
1454{
1455 int i;
1456 int count = d->mRRules.count();
1457 qCDebug(KCALCORE_LOG) << " -)" << count << "RRULEs:";
1458 for (i = 0; i < count; ++i) {
1459 qCDebug(KCALCORE_LOG) << " -) RecurrenceRule: ";
1460 d->mRRules[i]->dump();
1461 }
1462 count = d->mExRules.count();
1463 qCDebug(KCALCORE_LOG) << " -)" << count << "EXRULEs:";
1464 for (i = 0; i < count; ++i) {
1465 qCDebug(KCALCORE_LOG) << " -) ExceptionRule :";
1466 d->mExRules[i]->dump();
1467 }
1468
1469 count = d->mRDates.count();
1470 qCDebug(KCALCORE_LOG) << " -)" << count << "Recurrence Dates:";
1471 for (i = 0; i < count; ++i) {
1472 qCDebug(KCALCORE_LOG) << " " << d->mRDates[i];
1473 }
1474 count = d->mRDateTimes.count();
1475 qCDebug(KCALCORE_LOG) << " -)" << count << "Recurrence Date/Times:";
1476 for (i = 0; i < count; ++i) {
1477 qCDebug(KCALCORE_LOG) << " " << d->mRDateTimes[i];
1478 }
1479 count = d->mExDates.count();
1480 qCDebug(KCALCORE_LOG) << " -)" << count << "Exceptions Dates:";
1481 for (i = 0; i < count; ++i) {
1482 qCDebug(KCALCORE_LOG) << " " << d->mExDates[i];
1483 }
1484 count = d->mExDateTimes.count();
1485 qCDebug(KCALCORE_LOG) << " -)" << count << "Exception Date/Times:";
1486 for (i = 0; i < count; ++i) {
1487 qCDebug(KCALCORE_LOG) << " " << d->mExDateTimes[i];
1488 }
1489}
1490
1491Recurrence::RecurrenceObserver::~RecurrenceObserver()
1492{
1493}
1494
1496{
1497 if (!r) {
1498 return out;
1499 }
1500
1501 serializeQDateTimeList(out, r->d->mRDateTimes);
1502 out << (qint32)r->d->mRDateTimePeriods.size();
1503 for (auto it = r->d->mRDateTimePeriods.cbegin(); it != r->d->mRDateTimePeriods.cend(); ++it) {
1504 out << it.key() << it.value();
1505 }
1506 serializeQDateTimeList(out, r->d->mExDateTimes);
1507 out << r->d->mRDates;
1508 serializeQDateTimeAsKDateTime(out, r->d->mStartDateTime);
1509 out << r->d->mCachedType << r->d->mAllDay << r->d->mRecurReadOnly << r->d->mExDates << (qint32)r->d->mExRules.count() << (qint32)r->d->mRRules.count();
1510
1511 for (RecurrenceRule *rule : std::as_const(r->d->mExRules)) {
1512 out << rule;
1513 }
1514
1515 for (RecurrenceRule *rule : std::as_const(r->d->mRRules)) {
1516 out << rule;
1517 }
1518
1519 return out;
1520}
1521
1523{
1524 if (!r) {
1525 return in;
1526 }
1527
1528 int rruleCount;
1529 int exruleCount;
1530 int size;
1531
1532 deserializeQDateTimeList(in, r->d->mRDateTimes);
1533 in >> size;
1534 r->d->mRDateTimePeriods.clear();
1535 r->d->mRDateTimePeriods.reserve(size);
1536 for (int i = 0; i < size; ++i) {
1538 Period period;
1539 in >> start >> period;
1540 r->d->mRDateTimes << start;
1541 r->d->mRDateTimePeriods.insert(start, period);
1542 }
1543 deserializeQDateTimeList(in, r->d->mExDateTimes);
1544 in >> r->d->mRDates;
1545 deserializeKDateTimeAsQDateTime(in, r->d->mStartDateTime);
1546 in >> r->d->mCachedType >> r->d->mAllDay >> r->d->mRecurReadOnly >> r->d->mExDates >> exruleCount >> rruleCount;
1547
1548 r->d->mExRules.clear();
1549 r->d->mRRules.clear();
1550
1551 for (int i = 0; i < exruleCount; ++i) {
1552 RecurrenceRule *rule = new RecurrenceRule();
1553 rule->addObserver(r);
1554 in >> rule;
1555 r->d->mExRules.append(rule);
1556 }
1557
1558 for (int i = 0; i < rruleCount; ++i) {
1559 RecurrenceRule *rule = new RecurrenceRule();
1560 rule->addObserver(r);
1561 in >> rule;
1562 r->d->mRRules.append(rule);
1563 }
1564
1565 return in;
1566}
The period can be defined by either a start time and an end time or by a start time and a duration.
Definition period.h:38
QDateTime start() const
Returns when this period starts.
Definition period.cpp:105
structure for describing the n-th weekday of the month/year.
This class represents a recurrence rule for a calendar incidence.
void setAllDay(bool allDay)
Sets whether the dtstart is all-day (i.e.
QDateTime endDt(bool *result=nullptr) const
Returns the date and time of the last recurrence.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
void setFrequency(int freq)
Sets the recurrence frequency, in terms of the recurrence time period type.
void setEndDt(const QDateTime &endDateTime)
Sets the date and time of the last recurrence.
uint frequency() const
Returns the recurrence frequency, in terms of the recurrence time period type.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
PeriodType
enum for describing the frequency how an event recurs, if at all.
void setStartDt(const QDateTime &start)
Sets the recurrence start date/time.
int durationTo(const QDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
void addObserver(RuleObserver *observer)
Installs an observer.
void removeObserver(RuleObserver *observer)
Removes an observer that was added with addObserver.
This class represents a recurrence rule for a calendar incidence.
Definition recurrence.h:77
ushort recurrenceType() const
Returns the event's recurrence status.
void removeRRule(RecurrenceRule *rrule)
Remove a recurrence rule from the recurrence.
QList< RecurrenceRule::WDayPos > yearPositions() const
Returns the positions within a yearly recurrence.
void setRecurReadOnly(bool readOnly)
Set if recurrence is read-only or can be changed.
QList< int > yearDates() const
Returns the dates within a yearly recurrence.
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
void addYearlyDay(int day)
Adds day number of year within a yearly recurrence.
void setYearly(int freq)
Sets an event to recur yearly.
void setWeekly(int freq, int weekStart=1)
Sets an event to recur weekly.
Period rDateTimePeriod(const QDateTime &rdate) const
Inquire if the given RDATE is associated to a PERIOD.
bool recurs() const
Returns whether the event recurs at all.
void addRDateTimePeriod(const Period &period)
Add a RDATE defined as a PERIOD.
void setMinutely(int freq)
Sets an event to recur minutely.
void setMonthly(int freq)
Sets an event to recur monthly.
void setFrequency(int freq)
Sets the frequency of recurrence, in terms of the recurrence time period type.
QList< int > monthDays() const
Returns list of day numbers of a month.
void setStartDateTime(const QDateTime &start, bool isAllDay)
Set start of recurrence.
QDateTime getNextDateTime(const QDateTime &preDateTime) const
Returns the start date/time of the earliest recurrence with a start date/time after the specified dat...
void deleteExRule(RecurrenceRule *exrule)
Remove an exception rule from the recurrence and delete it.
void addYearlyMonth(short _rNum)
Adds month in yearly recurrence.
void deleteRRule(RecurrenceRule *rrule)
Remove a recurrence rule from the recurrence and delete it.
bool recurReadOnly() const
Returns true if the recurrence is read-only, or false if it can be changed.
void removeObserver(RecurrenceObserver *observer)
Removes an observer that was added with addObserver.
bool allDay() const
Set whether the recurrence has no time, just a date.
QDate startDate() const
Return the start date/time of the recurrence.
int durationTo(const QDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
void addMonthlyPos(short pos, const QBitArray &days)
Adds a position (e.g.
void addObserver(RecurrenceObserver *observer)
Installs an observer.
QDateTime endDateTime() const
Returns the date/time of the last recurrence.
void addYearlyDate(int date)
Adds date within a yearly recurrence.
void setEndDateTime(const QDateTime &endDateTime)
Sets the date and time of the last recurrence.
QList< QDateTime > timesInInterval(const QDateTime &start, const QDateTime &end) const
Returns a list of all the times at which the recurrence will occur between two specified times.
QBitArray days() const
Returns week day mask (bit 0 = Monday).
void removeExRule(RecurrenceRule *exrule)
Remove an exception rule from the recurrence.
void addYearlyPos(short pos, const QBitArray &days)
Adds position within month/year within a yearly recurrence.
void setAllDay(bool allDay)
Sets whether the dtstart is a all-day (i.e.
QDateTime startDateTime() const
Return the start date/time of the recurrence (Time for all-day recurrences will be 0:00).
void addRRule(RecurrenceRule *rrule)
Add a recurrence rule to the recurrence.
void setDaily(int freq)
Sets an event to recur daily.
void unsetRecurs()
Removes all recurrence rules.
void dump() const
Debug output.
void addExRule(RecurrenceRule *exrule)
Add an exception rule to the recurrence.
void setEndDate(const QDate &endDate)
Sets the date of the last recurrence.
int weekStart() const
Returns the first day of the week.
QList< int > yearMonths() const
Returns the months within a yearly recurrence.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
void setHourly(int freq)
Sets an event to recur hourly.
void clear()
Removes all recurrence and exception rules and dates.
bool recursOn(const QDate &date, const QTimeZone &timeZone) const
Returns true if the date specified is one on which the event will recur.
void addMonthlyDate(short day)
Adds a date (e.g.
QDateTime getPreviousDateTime(const QDateTime &afterDateTime) const
Returns the date and time of the last previous recurrence, before the specified date/time.
bool recursAt(const QDateTime &dt) const
Returns true if the date/time specified is one at which the event will recur.
QList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
bool operator==(const Recurrence &r) const
Comparison operator for equality.
void shiftTimes(const QTimeZone &oldZone, const QTimeZone &newZone)
Shift the times of the recurrence so that they appear at the same clock time as before but in a new t...
void addWeeklyDays(const QBitArray &days)
Adds days to the weekly day recurrence list.
QList< RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
QDate endDate() const
Returns the date of the last recurrence.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
TimeList recurTimesOn(const QDate &date, const QTimeZone &timeZone) const
Returns a list of the times on the specified date at which the recurrence will occur.
~Recurrence() override
Destructor.
Recurrence()
Constructs an empty recurrence.
Q_SCRIPTABLE Q_NOREPLY void start()
This file is part of the API for handling calendar data and defines the IncidenceBase class.
Namespace for all KCalendarCore types.
Definition alarm.h:37
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
Alarm deserializer.
Definition alarm.cpp:833
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
Alarm serializer.
Definition alarm.cpp:820
KCALENDARCORE_EXPORT bool identical(const QDateTime &dt1, const QDateTime &dt2)
Compare two QDateTimes for extended equality.
const QList< QKeySequence > & end()
bool operator==(const StyleDelim &l, const StyleDelim &r)
bool fill(bool value, qsizetype size)
void setBit(qsizetype i)
bool testBit(qsizetype i) const const
QDate date() const const
bool isValid() const const
void setDate(QDate date)
void setTime(QTime time)
QTime time() const const
QDateTime toTimeZone(const QTimeZone &timeZone) const const
iterator find(const Key &key)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
bool contains(const AT &value) const const
qsizetype count() const const
T & first()
bool isEmpty() const const
T & last()
void removeAt(qsizetype i)
qsizetype size() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:08:40 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.