1 | /* |
2 | This file is part of kcalcore library. |
3 | |
4 | Copyright (c) 1998 Preston Brown <pbrown@kde.org> |
5 | Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> |
6 | Copyright (c) 2002,2006 David Jarvie <software@astrojar.org.uk> |
7 | Copyright (C) 2005 Reinhold Kainhofer <kainhofer@kde.org> |
8 | |
9 | This library is free software; you can redistribute it and/or |
10 | modify it under the terms of the GNU Library General Public |
11 | License as published by the Free Software Foundation; either |
12 | version 2 of the License, or (at your option) any later version. |
13 | |
14 | This library is distributed in the hope that it will be useful, |
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | Library General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Library General Public License |
20 | along with this library; see the file COPYING.LIB. If not, write to |
21 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
22 | Boston, MA 02110-1301, USA. |
23 | */ |
24 | #include "recurrence.h" |
25 | |
26 | #include <KDebug> |
27 | |
28 | #include <QtCore/QBitArray> |
29 | #include <QtCore/QTime> |
30 | |
31 | using namespace KCalCore; |
32 | |
33 | //@cond PRIVATE |
34 | class KCalCore::Recurrence::Private |
35 | { |
36 | public: |
37 | Private() |
38 | : mCachedType(rMax), |
39 | mAllDay(false), |
40 | mRecurReadOnly(false) |
41 | { |
42 | } |
43 | |
44 | Private(const Private &p) |
45 | : mRDateTimes(p.mRDateTimes), |
46 | mRDates(p.mRDates), |
47 | mExDateTimes(p.mExDateTimes), |
48 | mExDates(p.mExDates), |
49 | mStartDateTime(p.mStartDateTime), |
50 | mCachedType(p.mCachedType), |
51 | mAllDay(p.mAllDay), |
52 | mRecurReadOnly(p.mRecurReadOnly) |
53 | { |
54 | } |
55 | |
56 | bool operator==(const Private &p) const; |
57 | |
58 | RecurrenceRule::List mExRules; |
59 | RecurrenceRule::List mRRules; |
60 | DateTimeList mRDateTimes; |
61 | DateList mRDates; |
62 | DateTimeList mExDateTimes; |
63 | DateList mExDates; |
64 | KDateTime mStartDateTime; // date/time of first recurrence |
65 | QList<RecurrenceObserver*> mObservers; |
66 | |
67 | // Cache the type of the recurrence with the old system (e.g. MonthlyPos) |
68 | mutable ushort mCachedType; |
69 | |
70 | bool mAllDay; // the recurrence has no time, just a date |
71 | bool mRecurReadOnly; |
72 | }; |
73 | |
74 | bool Recurrence::Private::operator==(const Recurrence::Private &p) const |
75 | { |
76 | if ((mStartDateTime != p.mStartDateTime && |
77 | (mStartDateTime.isValid() || p.mStartDateTime.isValid())) || |
78 | mAllDay != p.mAllDay || |
79 | mRecurReadOnly != p.mRecurReadOnly || |
80 | mExDates != p.mExDates || |
81 | mExDateTimes != p.mExDateTimes || |
82 | mRDates != p.mRDates || |
83 | mRDateTimes != p.mRDateTimes) { |
84 | return false; |
85 | } |
86 | |
87 | // Compare the rrules, exrules! Assume they have the same order... This only |
88 | // matters if we have more than one rule (which shouldn't be the default anyway) |
89 | int i; |
90 | int end = mRRules.count(); |
91 | if (end != p.mRRules.count()) { |
92 | return false; |
93 | } |
94 | for (i = 0; i < end; ++i) { |
95 | if (*mRRules[i] != *p.mRRules[i]) { |
96 | return false; |
97 | } |
98 | } |
99 | end = mExRules.count(); |
100 | if (end != p.mExRules.count()) { |
101 | return false; |
102 | } |
103 | for (i = 0; i < end; ++i) { |
104 | if (*mExRules[i] != *p.mExRules[i]) { |
105 | return false; |
106 | } |
107 | } |
108 | return true; |
109 | } |
110 | //@endcond |
111 | |
112 | Recurrence::Recurrence() |
113 | : d(new KCalCore::Recurrence::Private()) |
114 | { |
115 | } |
116 | |
117 | Recurrence::Recurrence(const Recurrence &r) |
118 | : RecurrenceRule::RuleObserver(), |
119 | d(new KCalCore::Recurrence::Private(*r.d)) |
120 | { |
121 | int i, end; |
122 | for (i = 0, end = r.d->mRRules.count(); i < end; ++i) { |
123 | RecurrenceRule *rule = new RecurrenceRule(*r.d->mRRules[i]); |
124 | d->mRRules.append(rule); |
125 | rule->addObserver(this); |
126 | } |
127 | for (i = 0, end = r.d->mExRules.count(); i < end; ++i) { |
128 | RecurrenceRule *rule = new RecurrenceRule(*r.d->mExRules[i]); |
129 | d->mExRules.append(rule); |
130 | rule->addObserver(this); |
131 | } |
132 | } |
133 | |
134 | Recurrence::~Recurrence() |
135 | { |
136 | qDeleteAll(d->mExRules); |
137 | qDeleteAll(d->mRRules); |
138 | delete d; |
139 | } |
140 | |
141 | bool Recurrence::operator==(const Recurrence &recurrence) const |
142 | { |
143 | return *d == *recurrence.d; |
144 | } |
145 | |
146 | Recurrence &Recurrence::operator=(const Recurrence &recurrence) |
147 | { |
148 | // check for self assignment |
149 | if (&recurrence == this) { |
150 | return *this; |
151 | } |
152 | |
153 | *d = *recurrence.d; |
154 | return *this; |
155 | } |
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 | if (d->mObservers.contains(observer)) { |
167 | d->mObservers.removeAll(observer); |
168 | } |
169 | } |
170 | |
171 | KDateTime Recurrence::startDateTime() const |
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 0; |
202 | } |
203 | RecurrenceRule *rrule = new RecurrenceRule(); |
204 | rrule->setStartDt(startDateTime()); |
205 | const_cast<KCalCore::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() ? 0 : 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 | |
233 | ushort Recurrence::recurrenceType() const |
234 | { |
235 | if (d->mCachedType == rMax) { |
236 | d->mCachedType = recurrenceType(defaultRRuleConst()); |
237 | } |
238 | return d->mCachedType; |
239 | } |
240 | |
241 | ushort Recurrence::recurrenceType(const RecurrenceRule *rrule) |
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() || |
250 | !rrule->bySeconds().isEmpty() || |
251 | !rrule->byWeekNumbers().isEmpty()) { |
252 | return rOther; |
253 | } |
254 | |
255 | // It wasn't possible to set BYMINUTES, BYHOUR etc. by the old code. So if |
256 | // it's set, it's none of the old types |
257 | if (!rrule->byMinutes().isEmpty() || !rrule->byHours().isEmpty()) { |
258 | return rOther; |
259 | } |
260 | |
261 | // Possible combinations were: |
262 | // BYDAY: with WEEKLY, MONTHLY, YEARLY |
263 | // BYMONTHDAY: with MONTHLY, YEARLY |
264 | // BYMONTH: with YEARLY |
265 | // BYYEARDAY: with YEARLY |
266 | if ((!rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly) || |
267 | (!rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly)) { |
268 | return rOther; |
269 | } |
270 | if (!rrule->byDays().isEmpty()) { |
271 | if (type != RecurrenceRule::rYearly && |
272 | type != RecurrenceRule::rMonthly && |
273 | type != RecurrenceRule::rWeekly) { |
274 | return rOther; |
275 | } |
276 | } |
277 | |
278 | switch (type) { |
279 | case RecurrenceRule::rNone: |
280 | return rNone; |
281 | case RecurrenceRule::rMinutely: |
282 | return rMinutely; |
283 | case RecurrenceRule::rHourly: |
284 | return rHourly; |
285 | case RecurrenceRule::rDaily: |
286 | return rDaily; |
287 | case RecurrenceRule::rWeekly: |
288 | return rWeekly; |
289 | case RecurrenceRule::rMonthly: |
290 | { |
291 | if (rrule->byDays().isEmpty()) { |
292 | return rMonthlyDay; |
293 | } else if (rrule->byMonthDays().isEmpty()) { |
294 | return rMonthlyPos; |
295 | } else { |
296 | return rOther; // both position and date specified |
297 | } |
298 | } |
299 | case RecurrenceRule::rYearly: |
300 | { |
301 | // Possible combinations: |
302 | // rYearlyMonth: [BYMONTH &] BYMONTHDAY |
303 | // rYearlyDay: BYYEARDAY |
304 | // rYearlyPos: [BYMONTH &] BYDAY |
305 | if (!rrule->byDays().isEmpty()) { |
306 | // can only by rYearlyPos |
307 | if (rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty()) { |
308 | return rYearlyPos; |
309 | } else { |
310 | return rOther; |
311 | } |
312 | } else if (!rrule->byYearDays().isEmpty()) { |
313 | // Can only be rYearlyDay |
314 | if (rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty()) { |
315 | return rYearlyDay; |
316 | } else { |
317 | return rOther; |
318 | } |
319 | } else { |
320 | return rYearlyMonth; |
321 | } |
322 | break; |
323 | } |
324 | default: |
325 | return rOther; |
326 | } |
327 | return rOther; |
328 | } |
329 | |
330 | bool Recurrence::recursOn(const QDate &qd, const KDateTime::Spec &timeSpec) const |
331 | { |
332 | // Don't waste time if date is before the start of the recurrence |
333 | if (KDateTime(qd, QTime(23, 59, 59), timeSpec) < d->mStartDateTime) { |
334 | return false; |
335 | } |
336 | |
337 | // First handle dates. Exrules override |
338 | if (d->mExDates.containsSorted(qd)) { |
339 | return false; |
340 | } |
341 | |
342 | int i, end; |
343 | TimeList tms; |
344 | // For all-day events a matching exrule excludes the whole day |
345 | // since exclusions take precedence over inclusions, we know it can't occur on that day. |
346 | if (allDay()) { |
347 | for (i = 0, end = d->mExRules.count(); i < end; ++i) { |
348 | if (d->mExRules[i]->recursOn(qd, timeSpec)) { |
349 | return false; |
350 | } |
351 | } |
352 | } |
353 | |
354 | if (d->mRDates.containsSorted(qd)) { |
355 | return true; |
356 | } |
357 | |
358 | // Check if it might recur today at all. |
359 | bool recurs = (startDate() == qd); |
360 | for (i = 0, end = d->mRDateTimes.count(); i < end && !recurs; ++i) { |
361 | recurs = (d->mRDateTimes[i].toTimeSpec(timeSpec).date() == qd); |
362 | } |
363 | for (i = 0, end = d->mRRules.count(); i < end && !recurs; ++i) { |
364 | recurs = d->mRRules[i]->recursOn(qd, timeSpec); |
365 | } |
366 | // If the event wouldn't recur at all, simply return false, don't check ex* |
367 | if (!recurs) { |
368 | return false; |
369 | } |
370 | |
371 | // Check if there are any times for this day excluded, either by exdate or exrule: |
372 | bool exon = false; |
373 | for (i = 0, end = d->mExDateTimes.count(); i < end && !exon; ++i) { |
374 | exon = (d->mExDateTimes[i].toTimeSpec(timeSpec).date() == qd); |
375 | } |
376 | if (!allDay()) { // we have already checked all-day times above |
377 | for (i = 0, end = d->mExRules.count(); i < end && !exon; ++i) { |
378 | exon = d->mExRules[i]->recursOn(qd, timeSpec); |
379 | } |
380 | } |
381 | |
382 | if (!exon) { |
383 | // Simple case, nothing on that day excluded, return the value from before |
384 | return recurs; |
385 | } else { |
386 | // Harder part: I don't think there is any way other than to calculate the |
387 | // whole list of items for that day. |
388 | //TODO: consider whether it would be more efficient to call |
389 | // Rule::recurTimesOn() instead of Rule::recursOn() from the start |
390 | TimeList timesForDay(recurTimesOn(qd, timeSpec)); |
391 | return !timesForDay.isEmpty(); |
392 | } |
393 | } |
394 | |
395 | bool Recurrence::recursAt(const KDateTime &dt) const |
396 | { |
397 | // Convert to recurrence's time zone for date comparisons, and for more efficient time comparisons |
398 | KDateTime dtrecur = dt.toTimeSpec(d->mStartDateTime.timeSpec()); |
399 | |
400 | // if it's excluded anyway, don't bother to check if it recurs at all. |
401 | if (d->mExDateTimes.containsSorted(dtrecur) || |
402 | d->mExDates.containsSorted(dtrecur.date())) { |
403 | return false; |
404 | } |
405 | int i, end; |
406 | for (i = 0, end = d->mExRules.count(); i < end; ++i) { |
407 | if (d->mExRules[i]->recursAt(dtrecur)) { |
408 | return false; |
409 | } |
410 | } |
411 | |
412 | // Check explicit recurrences, then rrules. |
413 | if (startDateTime() == dtrecur || d->mRDateTimes.containsSorted(dtrecur)) { |
414 | return true; |
415 | } |
416 | for (i = 0, end = d->mRRules.count(); i < end; ++i) { |
417 | if (d->mRRules[i]->recursAt(dtrecur)) { |
418 | return true; |
419 | } |
420 | } |
421 | |
422 | return false; |
423 | } |
424 | |
425 | /** Calculates the cumulative end of the whole recurrence (rdates and rrules). |
426 | If any rrule is infinite, or the recurrence doesn't have any rrules or |
427 | rdates, an invalid date is returned. */ |
428 | KDateTime Recurrence::endDateTime() const |
429 | { |
430 | DateTimeList dts; |
431 | dts << startDateTime(); |
432 | if (!d->mRDates.isEmpty()) { |
433 | dts << KDateTime(d->mRDates.last(), QTime(0, 0, 0), d->mStartDateTime.timeSpec()); |
434 | } |
435 | if (!d->mRDateTimes.isEmpty()) { |
436 | dts << d->mRDateTimes.last(); |
437 | } |
438 | for (int i = 0, end = d->mRRules.count(); i < end; ++i) { |
439 | KDateTime rl(d->mRRules[i]->endDt()); |
440 | // if any of the rules is infinite, the whole recurrence is |
441 | if (!rl.isValid()) { |
442 | return KDateTime(); |
443 | } |
444 | dts << rl; |
445 | } |
446 | dts.sortUnique(); |
447 | return dts.isEmpty() ? KDateTime() : dts.last(); |
448 | } |
449 | |
450 | /** Calculates the cumulative end of the whole recurrence (rdates and rrules). |
451 | If any rrule is infinite, or the recurrence doesn't have any rrules or |
452 | rdates, an invalid date is returned. */ |
453 | QDate Recurrence::endDate() const |
454 | { |
455 | KDateTime end(endDateTime()); |
456 | return end.isValid() ? end.date() : QDate(); |
457 | } |
458 | |
459 | void Recurrence::setEndDate(const QDate &date) |
460 | { |
461 | KDateTime dt(date, d->mStartDateTime.time(), d->mStartDateTime.timeSpec()); |
462 | if (allDay()) { |
463 | dt.setTime(QTime(23, 59, 59)); |
464 | } |
465 | setEndDateTime(dt); |
466 | } |
467 | |
468 | void Recurrence::setEndDateTime(const KDateTime &dateTime) |
469 | { |
470 | if (d->mRecurReadOnly) { |
471 | return; |
472 | } |
473 | RecurrenceRule *rrule = defaultRRule(true); |
474 | if (!rrule) { |
475 | return; |
476 | } |
477 | rrule->setEndDt(dateTime); |
478 | updated(); |
479 | } |
480 | |
481 | int Recurrence::duration() const |
482 | { |
483 | RecurrenceRule *rrule = defaultRRuleConst(); |
484 | return rrule ? rrule->duration() : 0; |
485 | } |
486 | |
487 | int Recurrence::durationTo(const KDateTime &datetime) const |
488 | { |
489 | // Emulate old behavior: This is just an interface to the first rule! |
490 | RecurrenceRule *rrule = defaultRRuleConst(); |
491 | return rrule ? rrule->durationTo(datetime) : 0; |
492 | } |
493 | |
494 | int Recurrence::durationTo(const QDate &date) const |
495 | { |
496 | return durationTo(KDateTime(date, QTime(23, 59, 59), d->mStartDateTime.timeSpec())); |
497 | } |
498 | |
499 | void Recurrence::setDuration(int duration) |
500 | { |
501 | if (d->mRecurReadOnly) { |
502 | return; |
503 | } |
504 | |
505 | RecurrenceRule *rrule = defaultRRule(true); |
506 | if (!rrule) { |
507 | return; |
508 | } |
509 | rrule->setDuration(duration); |
510 | updated(); |
511 | } |
512 | |
513 | void Recurrence::shiftTimes(const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec) |
514 | { |
515 | if (d->mRecurReadOnly) { |
516 | return; |
517 | } |
518 | |
519 | d->mStartDateTime = d->mStartDateTime.toTimeSpec(oldSpec); |
520 | d->mStartDateTime.setTimeSpec(newSpec); |
521 | |
522 | int i, end; |
523 | for (i = 0, end = d->mRDateTimes.count(); i < end; ++i) { |
524 | d->mRDateTimes[i] = d->mRDateTimes[i].toTimeSpec(oldSpec); |
525 | d->mRDateTimes[i].setTimeSpec(newSpec); |
526 | } |
527 | for (i = 0, end = d->mExDateTimes.count(); i < end; ++i) { |
528 | d->mExDateTimes[i] = d->mExDateTimes[i].toTimeSpec(oldSpec); |
529 | d->mExDateTimes[i].setTimeSpec(newSpec); |
530 | } |
531 | for (i = 0, end = d->mRRules.count(); i < end; ++i) { |
532 | d->mRRules[i]->shiftTimes(oldSpec, newSpec); |
533 | } |
534 | for (i = 0, end = d->mExRules.count(); i < end; ++i) { |
535 | d->mExRules[i]->shiftTimes(oldSpec, newSpec); |
536 | } |
537 | } |
538 | |
539 | void Recurrence::unsetRecurs() |
540 | { |
541 | if (d->mRecurReadOnly) { |
542 | return; |
543 | } |
544 | qDeleteAll(d->mRRules); |
545 | d->mRRules.clear(); |
546 | updated(); |
547 | } |
548 | |
549 | void Recurrence::clear() |
550 | { |
551 | if (d->mRecurReadOnly) { |
552 | return; |
553 | } |
554 | qDeleteAll(d->mRRules); |
555 | d->mRRules.clear(); |
556 | qDeleteAll(d->mExRules); |
557 | d->mExRules.clear(); |
558 | d->mRDates.clear(); |
559 | d->mRDateTimes.clear(); |
560 | d->mExDates.clear(); |
561 | d->mExDateTimes.clear(); |
562 | d->mCachedType = rMax; |
563 | updated(); |
564 | } |
565 | |
566 | void Recurrence::setRecurReadOnly(bool readOnly) |
567 | { |
568 | d->mRecurReadOnly = readOnly; |
569 | } |
570 | |
571 | bool Recurrence::recurReadOnly() const |
572 | { |
573 | return d->mRecurReadOnly; |
574 | } |
575 | |
576 | QDate Recurrence::startDate() const |
577 | { |
578 | return d->mStartDateTime.date(); |
579 | } |
580 | |
581 | void Recurrence::setStartDateTime(const KDateTime &start) |
582 | { |
583 | if (d->mRecurReadOnly) { |
584 | return; |
585 | } |
586 | d->mStartDateTime = start; |
587 | setAllDay(start.isDateOnly()); // set all RRULEs and EXRULEs |
588 | |
589 | int i, end; |
590 | for (i = 0, end = d->mRRules.count(); i < end; ++i) { |
591 | d->mRRules[i]->setStartDt(start); |
592 | } |
593 | for (i = 0, end = d->mExRules.count(); i < end; ++i) { |
594 | d->mExRules[i]->setStartDt(start); |
595 | } |
596 | updated(); |
597 | } |
598 | |
599 | int Recurrence::frequency() const |
600 | { |
601 | RecurrenceRule *rrule = defaultRRuleConst(); |
602 | return rrule ? rrule->frequency() : 0; |
603 | } |
604 | |
605 | // Emulate the old behaviour. Make this methods just an interface to the |
606 | // first rrule |
607 | void Recurrence::setFrequency(int freq) |
608 | { |
609 | if (d->mRecurReadOnly || freq <= 0) { |
610 | return; |
611 | } |
612 | |
613 | RecurrenceRule *rrule = defaultRRule(true); |
614 | if (rrule) { |
615 | rrule->setFrequency(freq); |
616 | } |
617 | updated(); |
618 | } |
619 | |
620 | // WEEKLY |
621 | |
622 | int Recurrence::weekStart() const |
623 | { |
624 | RecurrenceRule *rrule = defaultRRuleConst(); |
625 | return rrule ? rrule->weekStart() : 1; |
626 | } |
627 | |
628 | // Emulate the old behavior |
629 | QBitArray Recurrence::days() const |
630 | { |
631 | QBitArray days(7); |
632 | days.fill(0); |
633 | RecurrenceRule *rrule = defaultRRuleConst(); |
634 | if (rrule) { |
635 | QList<RecurrenceRule::WDayPos> bydays = rrule->byDays(); |
636 | for (int i = 0; i < bydays.size(); ++i) { |
637 | if (bydays.at(i).pos() == 0) { |
638 | days.setBit(bydays.at(i).day() - 1); |
639 | } |
640 | } |
641 | } |
642 | return days; |
643 | } |
644 | |
645 | // MONTHLY |
646 | |
647 | // Emulate the old behavior |
648 | QList<int> Recurrence::monthDays() const |
649 | { |
650 | RecurrenceRule *rrule = defaultRRuleConst(); |
651 | if (rrule) { |
652 | return rrule->byMonthDays(); |
653 | } else { |
654 | return QList<int>(); |
655 | } |
656 | } |
657 | |
658 | // Emulate the old behavior |
659 | QList<RecurrenceRule::WDayPos> Recurrence::monthPositions() const |
660 | { |
661 | RecurrenceRule *rrule = defaultRRuleConst(); |
662 | return rrule ? rrule->byDays() : QList<RecurrenceRule::WDayPos>(); |
663 | } |
664 | |
665 | // YEARLY |
666 | |
667 | QList<int> Recurrence::yearDays() const |
668 | { |
669 | RecurrenceRule *rrule = defaultRRuleConst(); |
670 | return rrule ? rrule->byYearDays() : QList<int>(); |
671 | } |
672 | |
673 | QList<int> Recurrence::yearDates() const |
674 | { |
675 | return monthDays(); |
676 | } |
677 | |
678 | QList<int> Recurrence::yearMonths() const |
679 | { |
680 | RecurrenceRule *rrule = defaultRRuleConst(); |
681 | return rrule ? rrule->byMonths() : QList<int>(); |
682 | } |
683 | |
684 | QList<RecurrenceRule::WDayPos> Recurrence::yearPositions() const |
685 | { |
686 | return monthPositions(); |
687 | } |
688 | |
689 | RecurrenceRule *Recurrence::setNewRecurrenceType(RecurrenceRule::PeriodType type, int freq) |
690 | { |
691 | if (d->mRecurReadOnly || freq <= 0) { |
692 | return 0; |
693 | } |
694 | |
695 | qDeleteAll(d->mRRules); |
696 | d->mRRules.clear(); |
697 | updated(); |
698 | RecurrenceRule *rrule = defaultRRule(true); |
699 | if (!rrule) { |
700 | return 0; |
701 | } |
702 | rrule->setRecurrenceType(type); |
703 | rrule->setFrequency(freq); |
704 | rrule->setDuration(-1); |
705 | return rrule; |
706 | } |
707 | |
708 | void Recurrence::setMinutely(int _rFreq) |
709 | { |
710 | if (setNewRecurrenceType(RecurrenceRule::rMinutely, _rFreq)) { |
711 | updated(); |
712 | } |
713 | } |
714 | |
715 | void Recurrence::setHourly(int _rFreq) |
716 | { |
717 | if (setNewRecurrenceType(RecurrenceRule::rHourly, _rFreq)) { |
718 | updated(); |
719 | } |
720 | } |
721 | |
722 | void Recurrence::setDaily(int _rFreq) |
723 | { |
724 | if (setNewRecurrenceType(RecurrenceRule::rDaily, _rFreq)) { |
725 | updated(); |
726 | } |
727 | } |
728 | |
729 | void Recurrence::setWeekly(int freq, int weekStart) |
730 | { |
731 | RecurrenceRule *rrule = setNewRecurrenceType(RecurrenceRule::rWeekly, freq); |
732 | if (!rrule) { |
733 | return; |
734 | } |
735 | rrule->setWeekStart(weekStart); |
736 | updated(); |
737 | } |
738 | |
739 | void Recurrence::setWeekly(int freq, const QBitArray &days, int weekStart) |
740 | { |
741 | setWeekly(freq, weekStart); |
742 | addMonthlyPos(0, days); |
743 | } |
744 | |
745 | void Recurrence::addWeeklyDays(const QBitArray &days) |
746 | { |
747 | addMonthlyPos(0, days); |
748 | } |
749 | |
750 | void Recurrence::setMonthly(int freq) |
751 | { |
752 | if (setNewRecurrenceType(RecurrenceRule::rMonthly, freq)) { |
753 | updated(); |
754 | } |
755 | } |
756 | |
757 | void Recurrence::addMonthlyPos(short pos, const QBitArray &days) |
758 | { |
759 | // Allow 53 for yearly! |
760 | if (d->mRecurReadOnly || pos > 53 || pos < -53) { |
761 | return; |
762 | } |
763 | |
764 | RecurrenceRule *rrule = defaultRRule(false); |
765 | if (!rrule) { |
766 | return; |
767 | } |
768 | bool changed = false; |
769 | QList<RecurrenceRule::WDayPos> positions = rrule->byDays(); |
770 | |
771 | for (int i = 0; i < 7; ++i) { |
772 | if (days.testBit(i)) { |
773 | RecurrenceRule::WDayPos p(pos, i + 1); |
774 | if (!positions.contains(p)) { |
775 | changed = true; |
776 | positions.append(p); |
777 | } |
778 | } |
779 | } |
780 | if (changed) { |
781 | rrule->setByDays(positions); |
782 | updated(); |
783 | } |
784 | } |
785 | |
786 | void Recurrence::addMonthlyPos(short pos, ushort day) |
787 | { |
788 | // Allow 53 for yearly! |
789 | if (d->mRecurReadOnly || pos > 53 || pos < -53) { |
790 | return; |
791 | } |
792 | |
793 | RecurrenceRule *rrule = defaultRRule(false); |
794 | if (!rrule) { |
795 | return; |
796 | } |
797 | QList<RecurrenceRule::WDayPos> positions = rrule->byDays(); |
798 | |
799 | RecurrenceRule::WDayPos p(pos, day); |
800 | if (!positions.contains(p)) { |
801 | positions.append(p); |
802 | rrule->setByDays(positions); |
803 | updated(); |
804 | } |
805 | } |
806 | |
807 | void Recurrence::addMonthlyDate(short day) |
808 | { |
809 | if (d->mRecurReadOnly || day > 31 || day < -31) { |
810 | return; |
811 | } |
812 | |
813 | RecurrenceRule *rrule = defaultRRule(true); |
814 | if (!rrule) { |
815 | return; |
816 | } |
817 | |
818 | QList<int> monthDays = rrule->byMonthDays(); |
819 | if (!monthDays.contains(day)) { |
820 | monthDays.append(day); |
821 | rrule->setByMonthDays(monthDays); |
822 | updated(); |
823 | } |
824 | } |
825 | |
826 | void Recurrence::setYearly(int freq) |
827 | { |
828 | if (setNewRecurrenceType(RecurrenceRule::rYearly, freq)) { |
829 | updated(); |
830 | } |
831 | } |
832 | |
833 | // Daynumber within year |
834 | void Recurrence::addYearlyDay(int day) |
835 | { |
836 | RecurrenceRule *rrule = defaultRRule(false); // It must already exist! |
837 | if (!rrule) { |
838 | return; |
839 | } |
840 | |
841 | QList<int> days = rrule->byYearDays(); |
842 | if (!days.contains(day)) { |
843 | days << day; |
844 | rrule->setByYearDays(days); |
845 | updated(); |
846 | } |
847 | } |
848 | |
849 | // day part of date within year |
850 | void Recurrence::addYearlyDate(int day) |
851 | { |
852 | addMonthlyDate(day); |
853 | } |
854 | |
855 | // day part of date within year, given as position (n-th weekday) |
856 | void Recurrence::addYearlyPos(short pos, const QBitArray &days) |
857 | { |
858 | addMonthlyPos(pos, days); |
859 | } |
860 | |
861 | // month part of date within year |
862 | void Recurrence::addYearlyMonth(short month) |
863 | { |
864 | if (d->mRecurReadOnly || month < 1 || month > 12) { |
865 | return; |
866 | } |
867 | |
868 | RecurrenceRule *rrule = defaultRRule(false); |
869 | if (!rrule) { |
870 | return; |
871 | } |
872 | |
873 | QList<int> months = rrule->byMonths(); |
874 | if (!months.contains(month)) { |
875 | months << month; |
876 | rrule->setByMonths(months); |
877 | updated(); |
878 | } |
879 | } |
880 | |
881 | TimeList Recurrence::recurTimesOn(const QDate &date, const KDateTime::Spec &timeSpec) const |
882 | { |
883 | // kDebug() << "recurTimesOn(" << date << ")"; |
884 | int i, end; |
885 | TimeList times; |
886 | |
887 | // The whole day is excepted |
888 | if (d->mExDates.containsSorted(date)) { |
889 | return times; |
890 | } |
891 | |
892 | // EXRULE takes precedence over RDATE entries, so for all-day events, |
893 | // a matching excule also excludes the whole day automatically |
894 | if (allDay()) { |
895 | for (i = 0, end = d->mExRules.count(); i < end; ++i) { |
896 | if (d->mExRules[i]->recursOn(date, timeSpec)) { |
897 | return times; |
898 | } |
899 | } |
900 | } |
901 | |
902 | KDateTime dt = startDateTime().toTimeSpec(timeSpec); |
903 | if (dt.date() == date) { |
904 | times << dt.time(); |
905 | } |
906 | |
907 | bool foundDate = false; |
908 | for (i = 0, end = d->mRDateTimes.count(); i < end; ++i) { |
909 | dt = d->mRDateTimes[i].toTimeSpec(timeSpec); |
910 | if (dt.date() == date) { |
911 | times << dt.time(); |
912 | foundDate = true; |
913 | } else if (foundDate) { |
914 | break; // <= Assume that the rdatetime list is sorted |
915 | } |
916 | } |
917 | for (i = 0, end = d->mRRules.count(); i < end; ++i) { |
918 | times += d->mRRules[i]->recurTimesOn(date, timeSpec); |
919 | } |
920 | times.sortUnique(); |
921 | |
922 | foundDate = false; |
923 | TimeList extimes; |
924 | for (i = 0, end = d->mExDateTimes.count(); i < end; ++i) { |
925 | dt = d->mExDateTimes[i].toTimeSpec(timeSpec); |
926 | if (dt.date() == date) { |
927 | extimes << dt.time(); |
928 | foundDate = true; |
929 | } else if (foundDate) { |
930 | break; |
931 | } |
932 | } |
933 | if (!allDay()) { // we have already checked all-day times above |
934 | for (i = 0, end = d->mExRules.count(); i < end; ++i) { |
935 | extimes += d->mExRules[i]->recurTimesOn(date, timeSpec); |
936 | } |
937 | } |
938 | extimes.sortUnique(); |
939 | |
940 | int st = 0; |
941 | for (i = 0, end = extimes.count(); i < end; ++i) { |
942 | int j = times.removeSorted(extimes[i], st); |
943 | if (j >= 0) { |
944 | st = j; |
945 | } |
946 | } |
947 | return times; |
948 | } |
949 | |
950 | DateTimeList Recurrence::timesInInterval(const KDateTime &start, const KDateTime &end) const |
951 | { |
952 | int i, count; |
953 | DateTimeList times; |
954 | for (i = 0, count = d->mRRules.count(); i < count; ++i) { |
955 | times += d->mRRules[i]->timesInInterval(start, end); |
956 | } |
957 | |
958 | // add rdatetimes that fit in the interval |
959 | for (i = 0, count = d->mRDateTimes.count(); i < count; ++i) { |
960 | if (d->mRDateTimes[i] >= start && d->mRDateTimes[i] <= end) { |
961 | times += d->mRDateTimes[i]; |
962 | } |
963 | } |
964 | |
965 | // add rdates that fit in the interval |
966 | KDateTime kdt(d->mStartDateTime); |
967 | for (i = 0, count = d->mRDates.count(); i < count; ++i) { |
968 | kdt.setDate(d->mRDates[i]); |
969 | if (kdt >= start && kdt <= end) { |
970 | times += kdt; |
971 | } |
972 | } |
973 | |
974 | // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list |
975 | // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include |
976 | // mStartDateTime. |
977 | // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly |
978 | // add mStartDateTime to the list, otherwise we won't see the first occurrence. |
979 | if ((!d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty()) && |
980 | d->mRRules.isEmpty() && |
981 | start <= d->mStartDateTime && |
982 | end >= d->mStartDateTime) { |
983 | times += d->mStartDateTime; |
984 | } |
985 | |
986 | times.sortUnique(); |
987 | |
988 | // Remove excluded times |
989 | int idt = 0; |
990 | int enddt = times.count(); |
991 | for (i = 0, count = d->mExDates.count(); i < count && idt < enddt; ++i) { |
992 | while (idt < enddt && times[idt].date() < d->mExDates[i]) { |
993 | ++idt; |
994 | } |
995 | while (idt < enddt && times[idt].date() == d->mExDates[i]) { |
996 | times.removeAt(idt); |
997 | --enddt; |
998 | } |
999 | } |
1000 | DateTimeList extimes; |
1001 | for (i = 0, count = d->mExRules.count(); i < count; ++i) { |
1002 | extimes += d->mExRules[i]->timesInInterval(start, end); |
1003 | } |
1004 | extimes += d->mExDateTimes; |
1005 | extimes.sortUnique(); |
1006 | |
1007 | int st = 0; |
1008 | for (i = 0, count = extimes.count(); i < count; ++i) { |
1009 | int j = times.removeSorted(extimes[i], st); |
1010 | if (j >= 0) { |
1011 | st = j; |
1012 | } |
1013 | } |
1014 | |
1015 | return times; |
1016 | } |
1017 | |
1018 | KDateTime Recurrence::getNextDateTime(const KDateTime &preDateTime) const |
1019 | { |
1020 | KDateTime nextDT = preDateTime; |
1021 | // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g. |
1022 | // the exrule is identical to the rrule). If an occurrence is found, break |
1023 | // out of the loop by returning that KDateTime |
1024 | // TODO_Recurrence: Is a loop counter of 1000 really okay? I mean for secondly |
1025 | // recurrence, an exdate might exclude more than 1000 intervals! |
1026 | int loop = 0; |
1027 | while (loop < 1000) { |
1028 | // Outline of the algo: |
1029 | // 1) Find the next date/time after preDateTime when the event could recur |
1030 | // 1.0) Add the start date if it's after preDateTime |
1031 | // 1.1) Use the next occurrence from the explicit RDATE lists |
1032 | // 1.2) Add the next recurrence for each of the RRULEs |
1033 | // 2) Take the earliest recurrence of these = KDateTime nextDT |
1034 | // 3) If that date/time is not excluded, either explicitly by an EXDATE or |
1035 | // by an EXRULE, return nextDT as the next date/time of the recurrence |
1036 | // 4) If it's excluded, start all at 1), but starting at nextDT (instead |
1037 | // of preDateTime). Loop at most 1000 times. |
1038 | ++loop; |
1039 | // First, get the next recurrence from the RDate lists |
1040 | DateTimeList dates; |
1041 | if (nextDT < startDateTime()) { |
1042 | dates << startDateTime(); |
1043 | } |
1044 | |
1045 | int end; |
1046 | // Assume that the rdatetime list is sorted |
1047 | int i = d->mRDateTimes.findGT(nextDT); |
1048 | if (i >= 0) { |
1049 | dates << d->mRDateTimes[i]; |
1050 | } |
1051 | |
1052 | KDateTime kdt(startDateTime()); |
1053 | for (i = 0, end = d->mRDates.count(); i < end; ++i) { |
1054 | kdt.setDate(d->mRDates[i]); |
1055 | if (kdt > nextDT) { |
1056 | dates << kdt; |
1057 | break; |
1058 | } |
1059 | } |
1060 | |
1061 | // Add the next occurrences from all RRULEs. |
1062 | for (i = 0, end = d->mRRules.count(); i < end; ++i) { |
1063 | KDateTime dt = d->mRRules[i]->getNextDate(nextDT); |
1064 | if (dt.isValid()) { |
1065 | dates << dt; |
1066 | } |
1067 | } |
1068 | |
1069 | // Take the first of these (all others can't be used later on) |
1070 | dates.sortUnique(); |
1071 | if (dates.isEmpty()) { |
1072 | return KDateTime(); |
1073 | } |
1074 | nextDT = dates.first(); |
1075 | |
1076 | // Check if that date/time is excluded explicitly or by an exrule: |
1077 | if (!d->mExDates.containsSorted(nextDT.date()) && |
1078 | !d->mExDateTimes.containsSorted(nextDT)) { |
1079 | bool allowed = true; |
1080 | for (i = 0, end = d->mExRules.count(); i < end; ++i) { |
1081 | allowed = allowed && !(d->mExRules[i]->recursAt(nextDT)); |
1082 | } |
1083 | if (allowed) { |
1084 | return nextDT; |
1085 | } |
1086 | } |
1087 | } |
1088 | |
1089 | // Couldn't find a valid occurrences in 1000 loops, something is wrong! |
1090 | return KDateTime(); |
1091 | } |
1092 | |
1093 | KDateTime Recurrence::getPreviousDateTime(const KDateTime &afterDateTime) const |
1094 | { |
1095 | KDateTime prevDT = afterDateTime; |
1096 | // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g. |
1097 | // the exrule is identical to the rrule). If an occurrence is found, break |
1098 | // out of the loop by returning that KDateTime |
1099 | int loop = 0; |
1100 | while (loop < 1000) { |
1101 | // Outline of the algo: |
1102 | // 1) Find the next date/time after preDateTime when the event could recur |
1103 | // 1.1) Use the next occurrence from the explicit RDATE lists |
1104 | // 1.2) Add the next recurrence for each of the RRULEs |
1105 | // 2) Take the earliest recurrence of these = KDateTime nextDT |
1106 | // 3) If that date/time is not excluded, either explicitly by an EXDATE or |
1107 | // by an EXRULE, return nextDT as the next date/time of the recurrence |
1108 | // 4) If it's excluded, start all at 1), but starting at nextDT (instead |
1109 | // of preDateTime). Loop at most 1000 times. |
1110 | ++loop; |
1111 | // First, get the next recurrence from the RDate lists |
1112 | DateTimeList dates; |
1113 | if (prevDT > startDateTime()) { |
1114 | dates << startDateTime(); |
1115 | } |
1116 | |
1117 | int i = d->mRDateTimes.findLT(prevDT); |
1118 | if (i >= 0) { |
1119 | dates << d->mRDateTimes[i]; |
1120 | } |
1121 | |
1122 | KDateTime kdt(startDateTime()); |
1123 | for (i = d->mRDates.count(); --i >= 0;) { |
1124 | kdt.setDate(d->mRDates[i]); |
1125 | if (kdt < prevDT) { |
1126 | dates << kdt; |
1127 | break; |
1128 | } |
1129 | } |
1130 | |
1131 | // Add the previous occurrences from all RRULEs. |
1132 | int end; |
1133 | for (i = 0, end = d->mRRules.count(); i < end; ++i) { |
1134 | KDateTime dt = d->mRRules[i]->getPreviousDate(prevDT); |
1135 | if (dt.isValid()) { |
1136 | dates << dt; |
1137 | } |
1138 | } |
1139 | |
1140 | // Take the last of these (all others can't be used later on) |
1141 | dates.sortUnique(); |
1142 | if (dates.isEmpty()) { |
1143 | return KDateTime(); |
1144 | } |
1145 | prevDT = dates.last(); |
1146 | |
1147 | // Check if that date/time is excluded explicitly or by an exrule: |
1148 | if (!d->mExDates.containsSorted(prevDT.date()) && |
1149 | !d->mExDateTimes.containsSorted(prevDT)) { |
1150 | bool allowed = true; |
1151 | for (i = 0, end = d->mExRules.count(); i < end; ++i) { |
1152 | allowed = allowed && !(d->mExRules[i]->recursAt(prevDT)); |
1153 | } |
1154 | if (allowed) { |
1155 | return prevDT; |
1156 | } |
1157 | } |
1158 | } |
1159 | |
1160 | // Couldn't find a valid occurrences in 1000 loops, something is wrong! |
1161 | return KDateTime(); |
1162 | } |
1163 | |
1164 | /***************************** PROTECTED FUNCTIONS ***************************/ |
1165 | |
1166 | RecurrenceRule::List Recurrence::rRules() const |
1167 | { |
1168 | return d->mRRules; |
1169 | } |
1170 | |
1171 | void Recurrence::addRRule(RecurrenceRule *rrule) |
1172 | { |
1173 | if (d->mRecurReadOnly || !rrule) { |
1174 | return; |
1175 | } |
1176 | |
1177 | rrule->setAllDay(d->mAllDay); |
1178 | d->mRRules.append(rrule); |
1179 | rrule->addObserver(this); |
1180 | updated(); |
1181 | } |
1182 | |
1183 | void Recurrence::removeRRule(RecurrenceRule *rrule) |
1184 | { |
1185 | if (d->mRecurReadOnly) { |
1186 | return; |
1187 | } |
1188 | |
1189 | d->mRRules.removeAll(rrule); |
1190 | rrule->removeObserver(this); |
1191 | updated(); |
1192 | } |
1193 | |
1194 | void Recurrence::deleteRRule(RecurrenceRule *rrule) |
1195 | { |
1196 | if (d->mRecurReadOnly) { |
1197 | return; |
1198 | } |
1199 | |
1200 | d->mRRules.removeAll(rrule); |
1201 | delete rrule; |
1202 | updated(); |
1203 | } |
1204 | |
1205 | RecurrenceRule::List Recurrence::exRules() const |
1206 | { |
1207 | return d->mExRules; |
1208 | } |
1209 | |
1210 | void Recurrence::addExRule(RecurrenceRule *exrule) |
1211 | { |
1212 | if (d->mRecurReadOnly || !exrule) { |
1213 | return; |
1214 | } |
1215 | |
1216 | exrule->setAllDay(d->mAllDay); |
1217 | d->mExRules.append(exrule); |
1218 | exrule->addObserver(this); |
1219 | updated(); |
1220 | } |
1221 | |
1222 | void Recurrence::removeExRule(RecurrenceRule *exrule) |
1223 | { |
1224 | if (d->mRecurReadOnly) { |
1225 | return; |
1226 | } |
1227 | |
1228 | d->mExRules.removeAll(exrule); |
1229 | exrule->removeObserver(this); |
1230 | updated(); |
1231 | } |
1232 | |
1233 | void Recurrence::deleteExRule(RecurrenceRule *exrule) |
1234 | { |
1235 | if (d->mRecurReadOnly) { |
1236 | return; |
1237 | } |
1238 | |
1239 | d->mExRules.removeAll(exrule); |
1240 | delete exrule; |
1241 | updated(); |
1242 | } |
1243 | |
1244 | DateTimeList Recurrence::rDateTimes() const |
1245 | { |
1246 | return d->mRDateTimes; |
1247 | } |
1248 | |
1249 | void Recurrence::setRDateTimes(const DateTimeList &rdates) |
1250 | { |
1251 | if (d->mRecurReadOnly) { |
1252 | return; |
1253 | } |
1254 | |
1255 | d->mRDateTimes = rdates; |
1256 | d->mRDateTimes.sortUnique(); |
1257 | updated(); |
1258 | } |
1259 | |
1260 | void Recurrence::addRDateTime(const KDateTime &rdate) |
1261 | { |
1262 | if (d->mRecurReadOnly) { |
1263 | return; |
1264 | } |
1265 | |
1266 | d->mRDateTimes.insertSorted(rdate); |
1267 | updated(); |
1268 | } |
1269 | |
1270 | DateList Recurrence::rDates() const |
1271 | { |
1272 | return d->mRDates; |
1273 | } |
1274 | |
1275 | void Recurrence::setRDates(const DateList &rdates) |
1276 | { |
1277 | if (d->mRecurReadOnly) { |
1278 | return; |
1279 | } |
1280 | |
1281 | d->mRDates = rdates; |
1282 | d->mRDates.sortUnique(); |
1283 | updated(); |
1284 | } |
1285 | |
1286 | void Recurrence::addRDate(const QDate &rdate) |
1287 | { |
1288 | if (d->mRecurReadOnly) { |
1289 | return; |
1290 | } |
1291 | |
1292 | d->mRDates.insertSorted(rdate); |
1293 | updated(); |
1294 | } |
1295 | |
1296 | DateTimeList Recurrence::exDateTimes() const |
1297 | { |
1298 | return d->mExDateTimes; |
1299 | } |
1300 | |
1301 | void Recurrence::setExDateTimes(const DateTimeList &exdates) |
1302 | { |
1303 | if (d->mRecurReadOnly) { |
1304 | return; |
1305 | } |
1306 | |
1307 | d->mExDateTimes = exdates; |
1308 | d->mExDateTimes.sortUnique(); |
1309 | } |
1310 | |
1311 | void Recurrence::addExDateTime(const KDateTime &exdate) |
1312 | { |
1313 | if (d->mRecurReadOnly) { |
1314 | return; |
1315 | } |
1316 | |
1317 | d->mExDateTimes.insertSorted(exdate); |
1318 | updated(); |
1319 | } |
1320 | |
1321 | DateList Recurrence::exDates() const |
1322 | { |
1323 | return d->mExDates; |
1324 | } |
1325 | |
1326 | void Recurrence::setExDates(const DateList &exdates) |
1327 | { |
1328 | if (d->mRecurReadOnly) { |
1329 | return; |
1330 | } |
1331 | |
1332 | d->mExDates = exdates; |
1333 | d->mExDates.sortUnique(); |
1334 | updated(); |
1335 | } |
1336 | |
1337 | void Recurrence::addExDate(const QDate &exdate) |
1338 | { |
1339 | if (d->mRecurReadOnly) { |
1340 | return; |
1341 | } |
1342 | |
1343 | d->mExDates.insertSorted(exdate); |
1344 | updated(); |
1345 | } |
1346 | |
1347 | void Recurrence::recurrenceChanged(RecurrenceRule *) |
1348 | { |
1349 | updated(); |
1350 | } |
1351 | |
1352 | // %%%%%%%%%%%%%%%%%% end:Recurrencerule %%%%%%%%%%%%%%%%%% |
1353 | |
1354 | void Recurrence::dump() const |
1355 | { |
1356 | kDebug(); |
1357 | |
1358 | int i; |
1359 | int count = d->mRRules.count(); |
1360 | kDebug() << " -)" << count << "RRULEs:" ; |
1361 | for (i = 0; i < count; ++i) { |
1362 | kDebug() << " -) RecurrenceRule: " ; |
1363 | d->mRRules[i]->dump(); |
1364 | } |
1365 | count = d->mExRules.count(); |
1366 | kDebug() << " -)" << count << "EXRULEs:" ; |
1367 | for (i = 0; i < count; ++i) { |
1368 | kDebug() << " -) ExceptionRule :" ; |
1369 | d->mExRules[i]->dump(); |
1370 | } |
1371 | |
1372 | count = d->mRDates.count(); |
1373 | kDebug() << endl << " -)" << count << "Recurrence Dates:" ; |
1374 | for (i = 0; i < count; ++i) { |
1375 | kDebug() << " " << d->mRDates[i]; |
1376 | } |
1377 | count = d->mRDateTimes.count(); |
1378 | kDebug() << endl << " -)" << count << "Recurrence Date/Times:" ; |
1379 | for (i = 0; i < count; ++i) { |
1380 | kDebug() << " " << d->mRDateTimes[i].dateTime(); |
1381 | } |
1382 | count = d->mExDates.count(); |
1383 | kDebug() << endl << " -)" << count << "Exceptions Dates:" ; |
1384 | for (i = 0; i < count; ++i) { |
1385 | kDebug() << " " << d->mExDates[i]; |
1386 | } |
1387 | count = d->mExDateTimes.count(); |
1388 | kDebug() << endl << " -)" << count << "Exception Date/Times:" ; |
1389 | for (i = 0; i < count; ++i) { |
1390 | kDebug() << " " << d->mExDateTimes[i].dateTime(); |
1391 | } |
1392 | } |
1393 | |
1394 | Recurrence::RecurrenceObserver::~RecurrenceObserver() |
1395 | { |
1396 | } |
1397 | |
1398 | KCALCORE_EXPORT QDataStream& KCalCore::operator<<(QDataStream &out, KCalCore::Recurrence *r) |
1399 | { |
1400 | if (!r) |
1401 | return out; |
1402 | |
1403 | out << r->d->mRDateTimes << r->d->mExDateTimes |
1404 | << r->d->mRDates << r->d->mStartDateTime << r->d->mCachedType |
1405 | << r->d->mAllDay << r->d->mRecurReadOnly << r->d->mExDates |
1406 | << r->d->mExRules.count() << r->d->mRRules.count(); |
1407 | |
1408 | foreach(RecurrenceRule *rule, r->d->mExRules) { |
1409 | out << rule; |
1410 | } |
1411 | |
1412 | foreach(RecurrenceRule *rule, r->d->mRRules) { |
1413 | out << rule; |
1414 | } |
1415 | |
1416 | return out; |
1417 | } |
1418 | |
1419 | |
1420 | KCALCORE_EXPORT QDataStream& KCalCore::operator>>(QDataStream &in, KCalCore::Recurrence *r) |
1421 | { |
1422 | if (!r) |
1423 | return in; |
1424 | |
1425 | int rruleCount, exruleCount; |
1426 | |
1427 | in >> r->d->mRDateTimes >> r->d->mExDateTimes |
1428 | >> r->d->mRDates >> r->d->mStartDateTime >> r->d->mCachedType |
1429 | >> r->d->mAllDay >> r->d->mRecurReadOnly >> r->d->mExDates |
1430 | >> exruleCount >> rruleCount; |
1431 | |
1432 | r->d->mExRules.clear(); |
1433 | r->d->mRRules.clear(); |
1434 | |
1435 | for (int i=0; i<exruleCount; ++i) { |
1436 | RecurrenceRule *rule = new RecurrenceRule(); |
1437 | rule->addObserver(r); |
1438 | in >> rule; |
1439 | r->d->mExRules.append(rule); |
1440 | } |
1441 | |
1442 | for (int i=0; i<rruleCount; ++i) { |
1443 | RecurrenceRule *rule = new RecurrenceRule(); |
1444 | rule->addObserver(r); |
1445 | in >> rule; |
1446 | r->d->mRRules.append(rule); |
1447 | } |
1448 | |
1449 | return in; |
1450 | } |
1451 | |