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

KDE's Doxygen guidelines are available online.