1 | /* |
2 | This file is part of libkcal. |
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 | |
25 | #include "recurrence.h" |
26 | #include "recurrencerule.h" |
27 | |
28 | #include <kglobal.h> |
29 | #include <klocalizedstring.h> |
30 | #include <kdebug.h> |
31 | |
32 | #include <QtCore/QList> |
33 | #include <QtCore/QBitArray> |
34 | |
35 | #include <limits.h> |
36 | |
37 | using namespace KCal; |
38 | |
39 | //@cond PRIVATE |
40 | class KCal::Recurrence::Private |
41 | { |
42 | public: |
43 | Private() |
44 | : mCachedType( rMax ), |
45 | mAllDay( false ), |
46 | mRecurReadOnly( false ) |
47 | { |
48 | mExRules.setAutoDelete( true ); |
49 | mRRules.setAutoDelete( true ); |
50 | } |
51 | |
52 | Private( const Private &p ) |
53 | : mRDateTimes( p.mRDateTimes ), |
54 | mRDates( p.mRDates ), |
55 | mExDateTimes( p.mExDateTimes ), |
56 | mExDates( p.mExDates ), |
57 | mStartDateTime( p.mStartDateTime ), |
58 | mCachedType( p.mCachedType ), |
59 | mAllDay( p.mAllDay ), |
60 | mRecurReadOnly( p.mRecurReadOnly ) |
61 | { |
62 | mExRules.setAutoDelete( true ); |
63 | mRRules.setAutoDelete( true ); |
64 | } |
65 | |
66 | bool operator==( const Private &p ) const; |
67 | |
68 | RecurrenceRule::List mExRules; |
69 | RecurrenceRule::List mRRules; |
70 | DateTimeList mRDateTimes; |
71 | DateList mRDates; |
72 | DateTimeList mExDateTimes; |
73 | DateList mExDates; |
74 | KDateTime mStartDateTime; // date/time of first recurrence |
75 | QList<RecurrenceObserver*> mObservers; |
76 | |
77 | // Cache the type of the recurrence with the old system (e.g. MonthlyPos) |
78 | mutable ushort mCachedType; |
79 | |
80 | bool mAllDay; // the recurrence has no time, just a date |
81 | bool mRecurReadOnly; |
82 | }; |
83 | |
84 | bool Recurrence::Private::operator==( const Recurrence::Private &p ) const |
85 | { |
86 | if ( mStartDateTime != p.mStartDateTime || |
87 | mAllDay != p.mAllDay || |
88 | mRecurReadOnly != p.mRecurReadOnly || |
89 | mExDates != p.mExDates || |
90 | mExDateTimes != p.mExDateTimes || |
91 | mRDates != p.mRDates || |
92 | mRDateTimes != p.mRDateTimes ) { |
93 | return false; |
94 | } |
95 | |
96 | // Compare the rrules, exrules! Assume they have the same order... This only |
97 | // matters if we have more than one rule (which shouldn't be the default anyway) |
98 | int i; |
99 | int end = mRRules.count(); |
100 | if ( end != p.mRRules.count() ) { |
101 | return false; |
102 | } |
103 | for ( i = 0; i < end; ++i ) { |
104 | if ( *mRRules[i] != *p.mRRules[i] ) { |
105 | return false; |
106 | } |
107 | } |
108 | end = mExRules.count(); |
109 | if ( end != p.mExRules.count() ) { |
110 | return false; |
111 | } |
112 | for ( i = 0; i < end; ++i ) { |
113 | if ( *mExRules[i] != *p.mExRules[i] ) { |
114 | return false; |
115 | } |
116 | } |
117 | return true; |
118 | } |
119 | //@endcond |
120 | |
121 | Recurrence::Recurrence() |
122 | : d( new KCal::Recurrence::Private() ) |
123 | { |
124 | } |
125 | |
126 | Recurrence::Recurrence( const Recurrence &r ) |
127 | : RecurrenceRule::RuleObserver(), |
128 | d( new KCal::Recurrence::Private( *r.d ) ) |
129 | { |
130 | int i, end; |
131 | for ( i = 0, end = r.d->mRRules.count(); i < end; ++i ) { |
132 | RecurrenceRule *rule = new RecurrenceRule( *r.d->mRRules[i] ); |
133 | d->mRRules.append( rule ); |
134 | rule->addObserver( this ); |
135 | } |
136 | for ( i = 0, end = r.d->mExRules.count(); i < end; ++i ) { |
137 | RecurrenceRule *rule = new RecurrenceRule( *r.d->mExRules[i] ); |
138 | d->mExRules.append( rule ); |
139 | rule->addObserver( this ); |
140 | } |
141 | } |
142 | |
143 | Recurrence::~Recurrence() |
144 | { |
145 | delete d; |
146 | } |
147 | |
148 | bool Recurrence::operator==( const Recurrence &r2 ) const |
149 | { |
150 | return *d == *r2.d; |
151 | } |
152 | |
153 | Recurrence &Recurrence::operator=( const Recurrence &other ) |
154 | { |
155 | if ( &other == this ) // Check for self assignment |
156 | return *this; |
157 | |
158 | *d = *other.d; |
159 | return *this; |
160 | } |
161 | |
162 | void Recurrence::addObserver( RecurrenceObserver *observer ) |
163 | { |
164 | if ( !d->mObservers.contains( observer ) ) { |
165 | d->mObservers.append( observer ); |
166 | } |
167 | } |
168 | |
169 | void Recurrence::removeObserver( RecurrenceObserver *observer ) |
170 | { |
171 | if ( d->mObservers.contains( observer ) ) { |
172 | d->mObservers.removeAll( observer ); |
173 | } |
174 | } |
175 | |
176 | KDateTime Recurrence::startDateTime() const |
177 | { |
178 | return d->mStartDateTime; |
179 | } |
180 | |
181 | bool Recurrence::allDay() const |
182 | { |
183 | return d->mAllDay; |
184 | } |
185 | |
186 | void Recurrence::setAllDay( bool allDay ) |
187 | { |
188 | if ( d->mRecurReadOnly || allDay == d->mAllDay ) { |
189 | return; |
190 | } |
191 | |
192 | d->mAllDay = allDay; |
193 | for ( int i = 0, end = d->mRRules.count(); i < end; ++i ) { |
194 | d->mRRules[i]->setAllDay( allDay ); |
195 | } |
196 | for ( int i = 0, end = d->mExRules.count(); i < end; ++i ) { |
197 | d->mExRules[i]->setAllDay( allDay ); |
198 | } |
199 | updated(); |
200 | } |
201 | |
202 | RecurrenceRule *Recurrence::defaultRRule( bool create ) const |
203 | { |
204 | if ( d->mRRules.isEmpty() ) { |
205 | if ( !create || d->mRecurReadOnly ) { |
206 | return 0; |
207 | } |
208 | RecurrenceRule *rrule = new RecurrenceRule(); |
209 | rrule->setStartDt( startDateTime() ); |
210 | const_cast<KCal::Recurrence*>(this)->addRRule( rrule ); |
211 | return rrule; |
212 | } else { |
213 | return d->mRRules[0]; |
214 | } |
215 | } |
216 | |
217 | RecurrenceRule *Recurrence::defaultRRuleConst() const |
218 | { |
219 | return d->mRRules.isEmpty() ? 0 : d->mRRules[0]; |
220 | } |
221 | |
222 | void Recurrence::updated() |
223 | { |
224 | // recurrenceType() re-calculates the type if it's rMax |
225 | d->mCachedType = rMax; |
226 | for ( int i = 0, end = d->mObservers.count(); i < end; ++i ) { |
227 | if ( d->mObservers[i] ) { |
228 | d->mObservers[i]->recurrenceUpdated( this ); |
229 | } |
230 | } |
231 | } |
232 | |
233 | bool Recurrence::recurs() const |
234 | { |
235 | return !d->mRRules.isEmpty() || !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty(); |
236 | } |
237 | |
238 | ushort Recurrence::recurrenceType() const |
239 | { |
240 | if ( d->mCachedType == rMax ) { |
241 | d->mCachedType = recurrenceType( defaultRRuleConst() ); |
242 | } |
243 | return d->mCachedType; |
244 | } |
245 | |
246 | ushort Recurrence::recurrenceType( const RecurrenceRule *rrule ) |
247 | { |
248 | if ( !rrule ) { |
249 | return rNone; |
250 | } |
251 | RecurrenceRule::PeriodType type = rrule->recurrenceType(); |
252 | |
253 | // BYSETPOS, BYWEEKNUMBER and BYSECOND were not supported in old versions |
254 | if ( !rrule->bySetPos().isEmpty() || |
255 | !rrule->bySeconds().isEmpty() || |
256 | !rrule->byWeekNumbers().isEmpty() ) { |
257 | return rOther; |
258 | } |
259 | |
260 | // It wasn't possible to set BYMINUTES, BYHOUR etc. by the old code. So if |
261 | // it's set, it's none of the old types |
262 | if ( !rrule->byMinutes().isEmpty() || !rrule->byHours().isEmpty() ) { |
263 | return rOther; |
264 | } |
265 | |
266 | // Possible combinations were: |
267 | // BYDAY: with WEEKLY, MONTHLY, YEARLY |
268 | // BYMONTHDAY: with MONTHLY, YEARLY |
269 | // BYMONTH: with YEARLY |
270 | // BYYEARDAY: with YEARLY |
271 | if ( ( !rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly ) || |
272 | ( !rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly ) ) { |
273 | return rOther; |
274 | } |
275 | if ( !rrule->byDays().isEmpty() ) { |
276 | if ( type != RecurrenceRule::rYearly && |
277 | type != RecurrenceRule::rMonthly && |
278 | type != RecurrenceRule::rWeekly ) { |
279 | return rOther; |
280 | } |
281 | } |
282 | |
283 | switch ( type ) { |
284 | case RecurrenceRule::rNone: |
285 | return rNone; |
286 | case RecurrenceRule::rMinutely: |
287 | return rMinutely; |
288 | case RecurrenceRule::rHourly: |
289 | return rHourly; |
290 | case RecurrenceRule::rDaily: |
291 | return rDaily; |
292 | case RecurrenceRule::rWeekly: |
293 | return rWeekly; |
294 | case RecurrenceRule::rMonthly: |
295 | { |
296 | if ( rrule->byDays().isEmpty() ) { |
297 | return rMonthlyDay; |
298 | } else if ( rrule->byMonthDays().isEmpty() ) { |
299 | return rMonthlyPos; |
300 | } else { |
301 | return rOther; // both position and date specified |
302 | } |
303 | } |
304 | case RecurrenceRule::rYearly: |
305 | { |
306 | // Possible combinations: |
307 | // rYearlyMonth: [BYMONTH &] BYMONTHDAY |
308 | // rYearlyDay: BYYEARDAY |
309 | // rYearlyPos: [BYMONTH &] BYDAY |
310 | if ( !rrule->byDays().isEmpty() ) { |
311 | // can only by rYearlyPos |
312 | if ( rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty() ) { |
313 | return rYearlyPos; |
314 | } else { |
315 | return rOther; |
316 | } |
317 | } else if ( !rrule->byYearDays().isEmpty() ) { |
318 | // Can only be rYearlyDay |
319 | if ( rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty() ) { |
320 | return rYearlyDay; |
321 | } else { |
322 | return rOther; |
323 | } |
324 | } else { |
325 | return rYearlyMonth; |
326 | } |
327 | break; |
328 | } |
329 | default: return rOther; |
330 | } |
331 | return rOther; |
332 | } |
333 | |
334 | bool Recurrence::recursOn( const QDate &qd, const KDateTime::Spec &timeSpec ) const |
335 | { |
336 | // Don't waste time if date is before the start of the recurrence |
337 | if ( KDateTime( qd, QTime( 23, 59, 59 ), timeSpec ) < d->mStartDateTime ) { |
338 | return false; |
339 | } |
340 | |
341 | // First handle dates. Exrules override |
342 | if ( d->mExDates.containsSorted( qd ) ) { |
343 | return false; |
344 | } |
345 | |
346 | int i, end; |
347 | TimeList tms; |
348 | // For all-day events a matching exrule excludes the whole day |
349 | // since exclusions take precedence over inclusions, we know it can't occur on that day. |
350 | if ( allDay() ) { |
351 | for ( i = 0, end = d->mExRules.count(); i < end; ++i ) { |
352 | if ( d->mExRules[i]->recursOn( qd, timeSpec ) ) { |
353 | return false; |
354 | } |
355 | } |
356 | } |
357 | |
358 | if ( d->mRDates.containsSorted( qd ) ) { |
359 | return true; |
360 | } |
361 | |
362 | // Check if it might recur today at all. |
363 | bool recurs = ( startDate() == qd ); |
364 | for ( i = 0, end = d->mRDateTimes.count(); i < end && !recurs; ++i ) { |
365 | recurs = ( d->mRDateTimes[i].toTimeSpec( timeSpec ).date() == qd ); |
366 | } |
367 | for ( i = 0, end = d->mRRules.count(); i < end && !recurs; ++i ) { |
368 | recurs = d->mRRules[i]->recursOn( qd, timeSpec ); |
369 | } |
370 | // If the event wouldn't recur at all, simply return false, don't check ex* |
371 | if ( !recurs ) { |
372 | return false; |
373 | } |
374 | |
375 | // Check if there are any times for this day excluded, either by exdate or exrule: |
376 | bool exon = false; |
377 | for ( i = 0, end = d->mExDateTimes.count(); i < end && !exon; ++i ) { |
378 | exon = ( d->mExDateTimes[i].toTimeSpec( timeSpec ).date() == qd ); |
379 | } |
380 | if ( !allDay() ) { // we have already checked all-day times above |
381 | for ( i = 0, end = d->mExRules.count(); i < end && !exon; ++i ) { |
382 | exon = d->mExRules[i]->recursOn( qd, timeSpec ); |
383 | } |
384 | } |
385 | |
386 | if ( !exon ) { |
387 | // Simple case, nothing on that day excluded, return the value from before |
388 | return recurs; |
389 | } else { |
390 | // Harder part: I don't think there is any way other than to calculate the |
391 | // whole list of items for that day. |
392 | //TODO: consider whether it would be more efficient to call |
393 | // Rule::recurTimesOn() instead of Rule::recursOn() from the start |
394 | TimeList timesForDay( recurTimesOn( qd, timeSpec ) ); |
395 | return !timesForDay.isEmpty(); |
396 | } |
397 | } |
398 | |
399 | bool Recurrence::recursAt( const KDateTime &dt ) const |
400 | { |
401 | // Convert to recurrence's time zone for date comparisons, and for more efficient time comparisons |
402 | KDateTime dtrecur = dt.toTimeSpec( d->mStartDateTime.timeSpec() ); |
403 | |
404 | // if it's excluded anyway, don't bother to check if it recurs at all. |
405 | if ( d->mExDateTimes.containsSorted( dtrecur ) || |
406 | d->mExDates.containsSorted( dtrecur.date() ) ) { |
407 | return false; |
408 | } |
409 | int i, end; |
410 | for ( i = 0, end = d->mExRules.count(); i < end; ++i ) { |
411 | if ( d->mExRules[i]->recursAt( dtrecur ) ) { |
412 | return false; |
413 | } |
414 | } |
415 | |
416 | // Check explicit recurrences, then rrules. |
417 | if ( startDateTime() == dtrecur || d->mRDateTimes.containsSorted( dtrecur ) ) { |
418 | return true; |
419 | } |
420 | for ( i = 0, end = d->mRRules.count(); i < end; ++i ) { |
421 | if ( d->mRRules[i]->recursAt( dtrecur ) ) { |
422 | return true; |
423 | } |
424 | } |
425 | |
426 | return false; |
427 | } |
428 | |
429 | /** Calculates the cumulative end of the whole recurrence (rdates and rrules). |
430 | If any rrule is infinite, or the recurrence doesn't have any rrules or |
431 | rdates, an invalid date is returned. */ |
432 | KDateTime Recurrence::endDateTime() const |
433 | { |
434 | DateTimeList dts; |
435 | dts << startDateTime(); |
436 | if ( !d->mRDates.isEmpty() ) { |
437 | dts << KDateTime( d->mRDates.last(), QTime( 0, 0, 0 ), d->mStartDateTime.timeSpec() ); |
438 | } |
439 | if ( !d->mRDateTimes.isEmpty() ) { |
440 | dts << d->mRDateTimes.last(); |
441 | } |
442 | for ( int i = 0, end = d->mRRules.count(); i < end; ++i ) { |
443 | KDateTime rl( d->mRRules[i]->endDt() ); |
444 | // if any of the rules is infinite, the whole recurrence is |
445 | if ( !rl.isValid() ) { |
446 | return KDateTime(); |
447 | } |
448 | dts << rl; |
449 | } |
450 | dts.sortUnique(); |
451 | return dts.isEmpty() ? KDateTime() : dts.last(); |
452 | } |
453 | |
454 | /** Calculates the cumulative end of the whole recurrence (rdates and rrules). |
455 | If any rrule is infinite, or the recurrence doesn't have any rrules or |
456 | rdates, an invalid date is returned. */ |
457 | QDate Recurrence::endDate() const |
458 | { |
459 | KDateTime end( endDateTime() ); |
460 | return end.isValid() ? end.date() : QDate(); |
461 | } |
462 | |
463 | void Recurrence::setEndDate( const QDate &date ) |
464 | { |
465 | KDateTime dt( date, d->mStartDateTime.time(), d->mStartDateTime.timeSpec() ); |
466 | if ( allDay() ) { |
467 | dt.setTime( QTime( 23, 59, 59 ) ); |
468 | } |
469 | setEndDateTime( dt ); |
470 | } |
471 | |
472 | void Recurrence::setEndDateTime( const KDateTime &dateTime ) |
473 | { |
474 | if ( d->mRecurReadOnly ) { |
475 | return; |
476 | } |
477 | RecurrenceRule *rrule = defaultRRule( true ); |
478 | if ( !rrule ) { |
479 | return; |
480 | } |
481 | rrule->setEndDt( dateTime ); |
482 | updated(); |
483 | } |
484 | |
485 | int Recurrence::duration() const |
486 | { |
487 | RecurrenceRule *rrule = defaultRRuleConst(); |
488 | return rrule ? rrule->duration() : 0; |
489 | } |
490 | |
491 | int Recurrence::durationTo( const KDateTime &datetime ) const |
492 | { |
493 | // Emulate old behavior: This is just an interface to the first rule! |
494 | RecurrenceRule *rrule = defaultRRuleConst(); |
495 | return rrule ? rrule->durationTo( datetime ) : 0; |
496 | } |
497 | |
498 | int Recurrence::durationTo( const QDate &date ) const |
499 | { |
500 | return durationTo( KDateTime( date, QTime( 23, 59, 59 ), d->mStartDateTime.timeSpec() ) ); |
501 | } |
502 | |
503 | void Recurrence::setDuration( int duration ) |
504 | { |
505 | if ( d->mRecurReadOnly ) { |
506 | return; |
507 | } |
508 | |
509 | RecurrenceRule *rrule = defaultRRule( true ); |
510 | if ( !rrule ) { |
511 | return; |
512 | } |
513 | rrule->setDuration( duration ); |
514 | updated(); |
515 | } |
516 | |
517 | void Recurrence::shiftTimes( const KDateTime::Spec &oldSpec, const KDateTime::Spec &newSpec ) |
518 | { |
519 | if ( d->mRecurReadOnly ) { |
520 | return; |
521 | } |
522 | |
523 | d->mStartDateTime = d->mStartDateTime.toTimeSpec( oldSpec ); |
524 | d->mStartDateTime.setTimeSpec( newSpec ); |
525 | |
526 | int i, end; |
527 | for ( i = 0, end = d->mRDateTimes.count(); i < end; ++i ) { |
528 | d->mRDateTimes[i] = d->mRDateTimes[i].toTimeSpec( oldSpec ); |
529 | d->mRDateTimes[i].setTimeSpec( newSpec ); |
530 | } |
531 | for ( i = 0, end = d->mExDateTimes.count(); i < end; ++i ) { |
532 | d->mExDateTimes[i] = d->mExDateTimes[i].toTimeSpec( oldSpec ); |
533 | d->mExDateTimes[i].setTimeSpec( newSpec ); |
534 | } |
535 | for ( i = 0, end = d->mRRules.count(); i < end; ++i ) { |
536 | d->mRRules[i]->shiftTimes( oldSpec, newSpec ); |
537 | } |
538 | for ( i = 0, end = d->mExRules.count(); i < end; ++i ) { |
539 | d->mExRules[i]->shiftTimes( oldSpec, newSpec ); |
540 | } |
541 | } |
542 | |
543 | void Recurrence::unsetRecurs() |
544 | { |
545 | if ( d->mRecurReadOnly ) { |
546 | return; |
547 | } |
548 | d->mRRules.clear(); |
549 | updated(); |
550 | } |
551 | |
552 | void Recurrence::clear() |
553 | { |
554 | if ( d->mRecurReadOnly ) { |
555 | return; |
556 | } |
557 | d->mRRules.clearAll(); |
558 | d->mExRules.clearAll(); |
559 | d->mRDates.clear(); |
560 | d->mRDateTimes.clear(); |
561 | d->mExDates.clear(); |
562 | d->mExDateTimes.clear(); |
563 | d->mCachedType = rMax; |
564 | updated(); |
565 | } |
566 | |
567 | void Recurrence::setRecurReadOnly( bool readOnly ) |
568 | { |
569 | d->mRecurReadOnly = readOnly; |
570 | } |
571 | |
572 | bool Recurrence::recurReadOnly() const |
573 | { |
574 | return d->mRecurReadOnly; |
575 | } |
576 | |
577 | QDate Recurrence::startDate() const |
578 | { |
579 | return d->mStartDateTime.date(); |
580 | } |
581 | |
582 | void Recurrence::setStartDateTime( const KDateTime &start ) |
583 | { |
584 | if ( d->mRecurReadOnly ) { |
585 | return; |
586 | } |
587 | d->mStartDateTime = start; |
588 | setAllDay( start.isDateOnly() ); // set all RRULEs and EXRULEs |
589 | |
590 | int i, end; |
591 | for ( i = 0, end = d->mRRules.count(); i < end; ++i ) { |
592 | d->mRRules[i]->setStartDt( start ); |
593 | } |
594 | for ( i = 0, end = d->mExRules.count(); i < end; ++i ) { |
595 | d->mExRules[i]->setStartDt( start ); |
596 | } |
597 | updated(); |
598 | } |
599 | |
600 | int Recurrence::frequency() const |
601 | { |
602 | RecurrenceRule *rrule = defaultRRuleConst(); |
603 | return rrule ? rrule->frequency() : 0; |
604 | } |
605 | |
606 | // Emulate the old behaviour. Make this methods just an interface to the |
607 | // first rrule |
608 | void Recurrence::setFrequency( int freq ) |
609 | { |
610 | if ( d->mRecurReadOnly || freq <= 0 ) { |
611 | return; |
612 | } |
613 | |
614 | RecurrenceRule *rrule = defaultRRule( true ); |
615 | if ( rrule ) { |
616 | rrule->setFrequency( freq ); |
617 | } |
618 | updated(); |
619 | } |
620 | |
621 | // WEEKLY |
622 | |
623 | int Recurrence::weekStart() const |
624 | { |
625 | RecurrenceRule *rrule = defaultRRuleConst(); |
626 | return rrule ? rrule->weekStart() : 1; |
627 | } |
628 | |
629 | // Emulate the old behavior |
630 | QBitArray Recurrence::days() const |
631 | { |
632 | QBitArray days( 7 ); |
633 | days.fill( 0 ); |
634 | RecurrenceRule *rrule = defaultRRuleConst(); |
635 | if ( rrule ) { |
636 | QList<RecurrenceRule::WDayPos> bydays = rrule->byDays(); |
637 | for ( int i = 0; i < bydays.size(); ++i ) { |
638 | if ( bydays.at(i).pos() == 0 ) { |
639 | days.setBit( bydays.at( i ).day() - 1 ); |
640 | } |
641 | } |
642 | } |
643 | return days; |
644 | } |
645 | |
646 | // MONTHLY |
647 | |
648 | // Emulate the old behavior |
649 | QList<int> Recurrence::monthDays() const |
650 | { |
651 | RecurrenceRule *rrule = defaultRRuleConst(); |
652 | if ( rrule ) { |
653 | return rrule->byMonthDays(); |
654 | } else { |
655 | return QList<int>(); |
656 | } |
657 | } |
658 | |
659 | // Emulate the old behavior |
660 | QList<RecurrenceRule::WDayPos> Recurrence::monthPositions() const |
661 | { |
662 | RecurrenceRule *rrule = defaultRRuleConst(); |
663 | return rrule ? rrule->byDays() : QList<RecurrenceRule::WDayPos>(); |
664 | } |
665 | |
666 | // YEARLY |
667 | |
668 | QList<int> Recurrence::yearDays() const |
669 | { |
670 | RecurrenceRule *rrule = defaultRRuleConst(); |
671 | return rrule ? rrule->byYearDays() : QList<int>(); |
672 | } |
673 | |
674 | QList<int> Recurrence::yearDates() const |
675 | { |
676 | return monthDays(); |
677 | } |
678 | |
679 | QList<int> Recurrence::yearMonths() const |
680 | { |
681 | RecurrenceRule *rrule = defaultRRuleConst(); |
682 | return rrule ? rrule->byMonths() : QList<int>(); |
683 | } |
684 | |
685 | QList<RecurrenceRule::WDayPos> Recurrence::yearPositions() const |
686 | { |
687 | return monthPositions(); |
688 | } |
689 | |
690 | RecurrenceRule *Recurrence::setNewRecurrenceType( RecurrenceRule::PeriodType type, int freq ) |
691 | { |
692 | if ( d->mRecurReadOnly || freq <= 0 ) { |
693 | return 0; |
694 | } |
695 | |
696 | d->mRRules.clearAll(); |
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) break; // <= Assume that the rdatetime list is sorted |
914 | } |
915 | for ( i = 0, end = d->mRRules.count(); i < end; ++i ) { |
916 | times += d->mRRules[i]->recurTimesOn( date, timeSpec ); |
917 | } |
918 | times.sortUnique(); |
919 | |
920 | foundDate = false; |
921 | TimeList extimes; |
922 | for ( i = 0, end = d->mExDateTimes.count(); i < end; ++i ) { |
923 | dt = d->mExDateTimes[i].toTimeSpec( timeSpec ); |
924 | if ( dt.date() == date ) { |
925 | extimes << dt.time(); |
926 | foundDate = true; |
927 | } else if (foundDate) break; |
928 | } |
929 | if ( !allDay() ) { // we have already checked all-day times above |
930 | for ( i = 0, end = d->mExRules.count(); i < end; ++i ) { |
931 | extimes += d->mExRules[i]->recurTimesOn( date, timeSpec ); |
932 | } |
933 | } |
934 | extimes.sortUnique(); |
935 | |
936 | int st = 0; |
937 | for ( i = 0, end = extimes.count(); i < end; ++i ) { |
938 | int j = times.removeSorted( extimes[i], st ); |
939 | if ( j >= 0 ) { |
940 | st = j; |
941 | } |
942 | } |
943 | return times; |
944 | } |
945 | |
946 | DateTimeList Recurrence::timesInInterval( const KDateTime &start, const KDateTime &end ) const |
947 | { |
948 | int i, count; |
949 | DateTimeList times; |
950 | for ( i = 0, count = d->mRRules.count(); i < count; ++i ) { |
951 | times += d->mRRules[i]->timesInInterval( start, end ); |
952 | } |
953 | |
954 | // add rdatetimes that fit in the interval |
955 | for ( i = 0, count = d->mRDateTimes.count(); i < count; ++i ) { |
956 | if ( d->mRDateTimes[i] >= start && d->mRDateTimes[i] <= end ) { |
957 | times += d->mRDateTimes[i]; |
958 | } |
959 | } |
960 | |
961 | // add rdates that fit in the interval |
962 | KDateTime kdt( d->mStartDateTime ); |
963 | for ( i = 0, count = d->mRDates.count(); i < count; ++i ) { |
964 | kdt.setDate( d->mRDates[i] ); |
965 | if ( kdt >= start && kdt <= end ) { |
966 | times += kdt; |
967 | } |
968 | } |
969 | |
970 | // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list |
971 | // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include |
972 | // mStartDateTime. |
973 | // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly |
974 | // add mStartDateTime to the list, otherwise we won't see the first occurrence. |
975 | if ( ( !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty() ) && |
976 | d->mRRules.isEmpty() && |
977 | start <= d->mStartDateTime && |
978 | end >= d->mStartDateTime ) { |
979 | times += d->mStartDateTime; |
980 | } |
981 | |
982 | times.sortUnique(); |
983 | |
984 | // Remove excluded times |
985 | int idt = 0; |
986 | int enddt = times.count(); |
987 | for ( i = 0, count = d->mExDates.count(); i < count && idt < enddt; ++i ) { |
988 | while ( idt < enddt && times[idt].date() < d->mExDates[i] ) ++idt; |
989 | while ( idt < enddt && times[idt].date() == d->mExDates[i] ) { |
990 | times.removeAt(idt); |
991 | --enddt; |
992 | } |
993 | } |
994 | DateTimeList extimes; |
995 | for ( i = 0, count = d->mExRules.count(); i < count; ++i ) { |
996 | extimes += d->mExRules[i]->timesInInterval( start, end ); |
997 | } |
998 | extimes += d->mExDateTimes; |
999 | extimes.sortUnique(); |
1000 | |
1001 | int st = 0; |
1002 | for ( i = 0, count = extimes.count(); i < count; ++i ) { |
1003 | int j = times.removeSorted( extimes[i], st ); |
1004 | if ( j >= 0 ) { |
1005 | st = j; |
1006 | } |
1007 | } |
1008 | |
1009 | return times; |
1010 | } |
1011 | |
1012 | KDateTime Recurrence::getNextDateTime( const KDateTime &preDateTime ) const |
1013 | { |
1014 | KDateTime nextDT = preDateTime; |
1015 | // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g. |
1016 | // the exrule is identical to the rrule). If an occurrence is found, break |
1017 | // out of the loop by returning that KDateTime |
1018 | // TODO_Recurrence: Is a loop counter of 1000 really okay? I mean for secondly |
1019 | // recurrence, an exdate might exclude more than 1000 intervals! |
1020 | int loop = 0; |
1021 | while ( loop < 1000 ) { |
1022 | // Outline of the algo: |
1023 | // 1) Find the next date/time after preDateTime when the event could recur |
1024 | // 1.0) Add the start date if it's after preDateTime |
1025 | // 1.1) Use the next occurrence from the explicit RDATE lists |
1026 | // 1.2) Add the next recurrence for each of the RRULEs |
1027 | // 2) Take the earliest recurrence of these = KDateTime nextDT |
1028 | // 3) If that date/time is not excluded, either explicitly by an EXDATE or |
1029 | // by an EXRULE, return nextDT as the next date/time of the recurrence |
1030 | // 4) If it's excluded, start all at 1), but starting at nextDT (instead |
1031 | // of preDateTime). Loop at most 1000 times. |
1032 | ++loop; |
1033 | // First, get the next recurrence from the RDate lists |
1034 | DateTimeList dates; |
1035 | if ( nextDT < startDateTime() ) { |
1036 | dates << startDateTime(); |
1037 | } |
1038 | |
1039 | int end; |
1040 | // Assume that the rdatetime list is sorted |
1041 | int i = d->mRDateTimes.findGT( nextDT ); |
1042 | if ( i >= 0 ) { |
1043 | dates << d->mRDateTimes[i]; |
1044 | } |
1045 | |
1046 | KDateTime kdt( startDateTime() ); |
1047 | for ( i = 0, end = d->mRDates.count(); i < end; ++i ) { |
1048 | kdt.setDate( d->mRDates[i] ); |
1049 | if ( kdt > nextDT ) { |
1050 | dates << kdt; |
1051 | break; |
1052 | } |
1053 | } |
1054 | |
1055 | // Add the next occurrences from all RRULEs. |
1056 | for ( i = 0, end = d->mRRules.count(); i < end; ++i ) { |
1057 | KDateTime dt = d->mRRules[i]->getNextDate( nextDT ); |
1058 | if ( dt.isValid() ) { |
1059 | dates << dt; |
1060 | } |
1061 | } |
1062 | |
1063 | // Take the first of these (all others can't be used later on) |
1064 | dates.sortUnique(); |
1065 | if ( dates.isEmpty() ) { |
1066 | return KDateTime(); |
1067 | } |
1068 | nextDT = dates.first(); |
1069 | |
1070 | // Check if that date/time is excluded explicitly or by an exrule: |
1071 | if ( !d->mExDates.containsSorted( nextDT.date() ) && |
1072 | !d->mExDateTimes.containsSorted( nextDT ) ) { |
1073 | bool allowed = true; |
1074 | for ( i = 0, end = d->mExRules.count(); i < end; ++i ) { |
1075 | allowed = allowed && !( d->mExRules[i]->recursAt( nextDT ) ); |
1076 | } |
1077 | if ( allowed ) { |
1078 | return nextDT; |
1079 | } |
1080 | } |
1081 | } |
1082 | |
1083 | // Couldn't find a valid occurrences in 1000 loops, something is wrong! |
1084 | return KDateTime(); |
1085 | } |
1086 | |
1087 | KDateTime Recurrence::getPreviousDateTime( const KDateTime &afterDateTime ) const |
1088 | { |
1089 | KDateTime prevDT = afterDateTime; |
1090 | // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g. |
1091 | // the exrule is identical to the rrule). If an occurrence is found, break |
1092 | // out of the loop by returning that KDateTime |
1093 | int loop = 0; |
1094 | while ( loop < 1000 ) { |
1095 | // Outline of the algo: |
1096 | // 1) Find the next date/time after preDateTime when the event could recur |
1097 | // 1.1) Use the next occurrence from the explicit RDATE lists |
1098 | // 1.2) Add the next recurrence for each of the RRULEs |
1099 | // 2) Take the earliest recurrence of these = KDateTime nextDT |
1100 | // 3) If that date/time is not excluded, either explicitly by an EXDATE or |
1101 | // by an EXRULE, return nextDT as the next date/time of the recurrence |
1102 | // 4) If it's excluded, start all at 1), but starting at nextDT (instead |
1103 | // of preDateTime). Loop at most 1000 times. |
1104 | ++loop; |
1105 | // First, get the next recurrence from the RDate lists |
1106 | DateTimeList dates; |
1107 | if ( prevDT > startDateTime() ) { |
1108 | dates << startDateTime(); |
1109 | } |
1110 | |
1111 | int i = d->mRDateTimes.findLT( prevDT ); |
1112 | if ( i >= 0 ) { |
1113 | dates << d->mRDateTimes[i]; |
1114 | } |
1115 | |
1116 | KDateTime kdt( startDateTime() ); |
1117 | for ( i = d->mRDates.count(); --i >= 0; ) { |
1118 | kdt.setDate( d->mRDates[i] ); |
1119 | if ( kdt < prevDT ) { |
1120 | dates << kdt; |
1121 | break; |
1122 | } |
1123 | } |
1124 | |
1125 | // Add the previous occurrences from all RRULEs. |
1126 | int end; |
1127 | for ( i = 0, end = d->mRRules.count(); i < end; ++i ) { |
1128 | KDateTime dt = d->mRRules[i]->getPreviousDate( prevDT ); |
1129 | if ( dt.isValid() ) { |
1130 | dates << dt; |
1131 | } |
1132 | } |
1133 | |
1134 | // Take the last of these (all others can't be used later on) |
1135 | dates.sortUnique(); |
1136 | if ( dates.isEmpty() ) { |
1137 | return KDateTime(); |
1138 | } |
1139 | prevDT = dates.last(); |
1140 | |
1141 | // Check if that date/time is excluded explicitly or by an exrule: |
1142 | if ( !d->mExDates.containsSorted( prevDT.date() ) && |
1143 | !d->mExDateTimes.containsSorted( prevDT ) ) { |
1144 | bool allowed = true; |
1145 | for ( i = 0, end = d->mExRules.count(); i < end; ++i ) { |
1146 | allowed = allowed && !( d->mExRules[i]->recursAt( prevDT ) ); |
1147 | } |
1148 | if ( allowed ) { |
1149 | return prevDT; |
1150 | } |
1151 | } |
1152 | } |
1153 | |
1154 | // Couldn't find a valid occurrences in 1000 loops, something is wrong! |
1155 | return KDateTime(); |
1156 | } |
1157 | |
1158 | /***************************** PROTECTED FUNCTIONS ***************************/ |
1159 | |
1160 | RecurrenceRule::List Recurrence::rRules() const |
1161 | { |
1162 | return d->mRRules; |
1163 | } |
1164 | |
1165 | void Recurrence::addRRule( RecurrenceRule *rrule ) |
1166 | { |
1167 | if ( d->mRecurReadOnly || !rrule ) { |
1168 | return; |
1169 | } |
1170 | |
1171 | rrule->setAllDay( d->mAllDay ); |
1172 | d->mRRules.append( rrule ); |
1173 | rrule->addObserver( this ); |
1174 | updated(); |
1175 | } |
1176 | |
1177 | void Recurrence::removeRRule( RecurrenceRule *rrule ) |
1178 | { |
1179 | if (d->mRecurReadOnly) { |
1180 | return; |
1181 | } |
1182 | |
1183 | d->mRRules.removeAll( rrule ); |
1184 | rrule->removeObserver( this ); |
1185 | updated(); |
1186 | } |
1187 | |
1188 | void Recurrence::deleteRRule( RecurrenceRule *rrule ) |
1189 | { |
1190 | if (d->mRecurReadOnly) { |
1191 | return; |
1192 | } |
1193 | |
1194 | d->mRRules.removeAll( rrule ); |
1195 | delete rrule; |
1196 | updated(); |
1197 | } |
1198 | |
1199 | RecurrenceRule::List Recurrence::exRules() const |
1200 | { |
1201 | return d->mExRules; |
1202 | } |
1203 | |
1204 | void Recurrence::addExRule( RecurrenceRule *exrule ) |
1205 | { |
1206 | if ( d->mRecurReadOnly || !exrule ) { |
1207 | return; |
1208 | } |
1209 | |
1210 | exrule->setAllDay( d->mAllDay ); |
1211 | d->mExRules.append( exrule ); |
1212 | exrule->addObserver( this ); |
1213 | updated(); |
1214 | } |
1215 | |
1216 | void Recurrence::removeExRule( RecurrenceRule *exrule ) |
1217 | { |
1218 | if ( d->mRecurReadOnly ) { |
1219 | return; |
1220 | } |
1221 | |
1222 | d->mExRules.removeAll( exrule ); |
1223 | exrule->removeObserver( this ); |
1224 | updated(); |
1225 | } |
1226 | |
1227 | void Recurrence::deleteExRule( RecurrenceRule *exrule ) |
1228 | { |
1229 | if ( d->mRecurReadOnly ) { |
1230 | return; |
1231 | } |
1232 | |
1233 | d->mExRules.removeAll( exrule ); |
1234 | delete exrule; |
1235 | updated(); |
1236 | } |
1237 | |
1238 | DateTimeList Recurrence::rDateTimes() const |
1239 | { |
1240 | return d->mRDateTimes; |
1241 | } |
1242 | |
1243 | void Recurrence::setRDateTimes( const DateTimeList &rdates ) |
1244 | { |
1245 | if ( d->mRecurReadOnly ) { |
1246 | return; |
1247 | } |
1248 | |
1249 | d->mRDateTimes = rdates; |
1250 | d->mRDateTimes.sortUnique(); |
1251 | updated(); |
1252 | } |
1253 | |
1254 | void Recurrence::addRDateTime( const KDateTime &rdate ) |
1255 | { |
1256 | if ( d->mRecurReadOnly ) { |
1257 | return; |
1258 | } |
1259 | |
1260 | d->mRDateTimes.insertSorted( rdate ); |
1261 | updated(); |
1262 | } |
1263 | |
1264 | DateList Recurrence::rDates() const |
1265 | { |
1266 | return d->mRDates; |
1267 | } |
1268 | |
1269 | void Recurrence::setRDates( const DateList &rdates ) |
1270 | { |
1271 | if ( d->mRecurReadOnly ) { |
1272 | return; |
1273 | } |
1274 | |
1275 | d->mRDates = rdates; |
1276 | d->mRDates.sortUnique(); |
1277 | updated(); |
1278 | } |
1279 | |
1280 | void Recurrence::addRDate( const QDate &rdate ) |
1281 | { |
1282 | if ( d->mRecurReadOnly ) { |
1283 | return; |
1284 | } |
1285 | |
1286 | d->mRDates.insertSorted( rdate ); |
1287 | updated(); |
1288 | } |
1289 | |
1290 | DateTimeList Recurrence::exDateTimes() const |
1291 | { |
1292 | return d->mExDateTimes; |
1293 | } |
1294 | |
1295 | void Recurrence::setExDateTimes( const DateTimeList &exdates ) |
1296 | { |
1297 | if ( d->mRecurReadOnly ) { |
1298 | return; |
1299 | } |
1300 | |
1301 | d->mExDateTimes = exdates; |
1302 | d->mExDateTimes.sortUnique(); |
1303 | } |
1304 | |
1305 | void Recurrence::addExDateTime( const KDateTime &exdate ) |
1306 | { |
1307 | if ( d->mRecurReadOnly ) { |
1308 | return; |
1309 | } |
1310 | |
1311 | d->mExDateTimes.insertSorted( exdate ); |
1312 | updated(); |
1313 | } |
1314 | |
1315 | DateList Recurrence::exDates() const |
1316 | { |
1317 | return d->mExDates; |
1318 | } |
1319 | |
1320 | void Recurrence::setExDates( const DateList &exdates ) |
1321 | { |
1322 | if ( d->mRecurReadOnly ) { |
1323 | return; |
1324 | } |
1325 | |
1326 | d->mExDates = exdates; |
1327 | d->mExDates.sortUnique(); |
1328 | updated(); |
1329 | } |
1330 | |
1331 | void Recurrence::addExDate( const QDate &exdate ) |
1332 | { |
1333 | if ( d->mRecurReadOnly ) { |
1334 | return; |
1335 | } |
1336 | |
1337 | d->mExDates.insertSorted( exdate ); |
1338 | updated(); |
1339 | } |
1340 | |
1341 | void Recurrence::recurrenceChanged( RecurrenceRule * ) |
1342 | { |
1343 | updated(); |
1344 | } |
1345 | |
1346 | // %%%%%%%%%%%%%%%%%% end:Recurrencerule %%%%%%%%%%%%%%%%%% |
1347 | |
1348 | void Recurrence::dump() const |
1349 | { |
1350 | kDebug(); |
1351 | |
1352 | int i; |
1353 | int count = d->mRRules.count(); |
1354 | kDebug() << " -)" << count << "RRULEs:" ; |
1355 | for ( i = 0; i < count; ++i ) { |
1356 | kDebug() << " -) RecurrenceRule: " ; |
1357 | d->mRRules[i]->dump(); |
1358 | } |
1359 | count = d->mExRules.count(); |
1360 | kDebug() << " -)" << count << "EXRULEs:" ; |
1361 | for ( i = 0; i < count; ++i ) { |
1362 | kDebug() << " -) ExceptionRule :" ; |
1363 | d->mExRules[i]->dump(); |
1364 | } |
1365 | |
1366 | count = d->mRDates.count(); |
1367 | kDebug() << endl << " -)" << count << "Recurrence Dates:" ; |
1368 | for ( i = 0; i < count; ++i ) { |
1369 | kDebug() << " " << d->mRDates[i]; |
1370 | } |
1371 | count = d->mRDateTimes.count(); |
1372 | kDebug() << endl << " -)" << count << "Recurrence Date/Times:" ; |
1373 | for ( i = 0; i < count; ++i ) { |
1374 | kDebug() << " " << d->mRDateTimes[i].dateTime(); |
1375 | } |
1376 | count = d->mExDates.count(); |
1377 | kDebug() << endl << " -)" << count << "Exceptions Dates:" ; |
1378 | for ( i = 0; i < count; ++i ) { |
1379 | kDebug() << " " << d->mExDates[i]; |
1380 | } |
1381 | count = d->mExDateTimes.count(); |
1382 | kDebug() << endl << " -)" << count << "Exception Date/Times:" ; |
1383 | for ( i = 0; i < count; ++i ) { |
1384 | kDebug() << " " << d->mExDateTimes[i].dateTime(); |
1385 | } |
1386 | } |
1387 | |