KCalendarCore

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

KDE's Doxygen guidelines are available online.