libkcal

recurrence.cpp
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 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 <limits.h>
26
27#include <kdebug.h>
28#include <tdeglobal.h>
29#include <tdelocale.h>
30#include <tqbitarray.h>
31
32#include "recurrence.h"
33#include "recurrencerule.h"
34
35using namespace KCal;
36
37Recurrence::Recurrence()
38: mFloating( false ),
39 mRecurReadOnly(false),
40 mCachedType(rMax)
41{
42 mExRules.setAutoDelete( true );
43 mRRules.setAutoDelete( true );
44}
45
46Recurrence::Recurrence( const Recurrence &r )
47: RecurrenceRule::Observer(),
48 mRDateTimes( r.mRDateTimes ), mRDates( r.mRDates ),
49 mExDateTimes( r.mExDateTimes ), mExDates( r.mExDates ),
50 mStartDateTime( r.mStartDateTime ),
51 mFloating( r.mFloating ),
52 mRecurReadOnly(r.mRecurReadOnly),
53 mCachedType( r.mCachedType )
54{
55 mExRules.setAutoDelete( true );
56 mRRules.setAutoDelete( true );
57 RecurrenceRule::List::ConstIterator rr;
58 for ( rr = r.mRRules.begin(); rr != r.mRRules.end(); ++rr ) {
59 RecurrenceRule *rule = new RecurrenceRule( *(*rr) );
60 mRRules.append( rule );
61 rule->addObserver( this );
62 }
63 for ( rr = r.mExRules.begin(); rr != r.mExRules.end(); ++rr ) {
64 RecurrenceRule *rule = new RecurrenceRule( *(*rr) );
65 mExRules.append( rule );
66 rule->addObserver( this );
67 }
68}
69
70Recurrence::~Recurrence()
71{
72}
73
74
75
76bool Recurrence::operator==( const Recurrence& r2 ) const
77{
78 if ( mStartDateTime != r2.mStartDateTime
79 || mFloating != r2.mFloating
80 || mRecurReadOnly != r2.mRecurReadOnly )
81 return false;
82 if ( mExDates != r2.mExDates ) return false;
83 if ( mExDateTimes != r2.mExDateTimes ) return false;
84 if ( mRDates != r2.mRDates ) return false;
85 if ( mRDateTimes != r2.mRDateTimes ) return false;
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 if ( mRRules.count() != r2.mRRules.count() ) return false;
90 RecurrenceRule::List::ConstIterator rit1 = mRRules.begin();
91 RecurrenceRule::List::ConstIterator rit2 = r2.mRRules.begin();
92
93 while ( rit1 != mRRules.end() && rit2 != r2.mRRules.end() ) {
94 // dereference the iterator to the RecurrenceRule*, and that once again
95 // to RecurrenceRule...
96 if ( *(*rit1) != *(*rit2) ) return false;
97 ++rit1;
98 ++rit2;
99 }
100 RecurrenceRule::List::ConstIterator exit1 = mExRules.begin();
101 RecurrenceRule::List::ConstIterator exit2 = r2.mExRules.begin();
102
103 while ( exit1 != mExRules.end() && exit2 != r2.mExRules.end() ) {
104 // dereference the iterator to the RecurrenceRule*, and that once again
105 // to RecurrenceRule...
106 if ( *(*exit1) != *(*exit2) ) return false;
107 ++exit1;
108 ++exit2;
109 }
110 return true;
111}
112
113void Recurrence::addObserver( Observer *observer )
114{
115 if ( !mObservers.contains( observer ) )
116 mObservers.append( observer );
117}
118
119void Recurrence::removeObserver( Observer *observer )
120{
121 if ( mObservers.contains( observer ) )
122 mObservers.remove( observer );
123}
124
125
127{
128 if ( mFloating )
129 return TQDateTime( mStartDateTime.date(), TQTime( 0, 0, 0 ) );
130 else return mStartDateTime;
131}
132
133void Recurrence::setFloats( bool floats )
134{
135 if ( mRecurReadOnly ) return;
136 if ( floats == mFloating ) return;
137 mFloating = floats;
138
139
140 RecurrenceRule::List::ConstIterator it;
141 for ( it = mRRules.begin(); it != mRRules.end(); ++it ) {
142 (*it)->setFloats( floats );
143 }
144
145 RecurrenceRule::List::ConstIterator it1;
146 for ( it1 = mExRules.begin(); it1 != mExRules.end(); ++it1 ) {
147 (*it1)->setFloats( floats );
148 }
149 updated();
150}
151
152RecurrenceRule *Recurrence::defaultRRule( bool create ) const
153{
154 if ( mRRules.isEmpty() ) {
155 if ( !create || mRecurReadOnly ) return 0;
156 RecurrenceRule *rrule = new RecurrenceRule();
157 rrule->setStartDt( startDateTime() );
158 const_cast<KCal::Recurrence*>(this)->addRRule( rrule );
159 return rrule;
160 } else {
161 return mRRules.first();
162 }
163}
164
165RecurrenceRule *Recurrence::defaultRRuleConst() const
166{
167 if ( mRRules.isEmpty() ) {
168 return 0;
169 } else {
170 return mRRules.first();
171 }
172}
173
174void Recurrence::updated()
175{
176 // recurrenceType() re-calculates the type if it's rMax
177 mCachedType = rMax;
178 for ( TQValueList<Observer*>::ConstIterator it = mObservers.begin();
179 it != mObservers.end(); ++it ) {
180 if ( (*it) ) (*it)->recurrenceUpdated( this );
181 }
182}
183
185{
186 return !mRRules.isEmpty() || !mRDates.isEmpty() || !mRDateTimes.isEmpty();
187}
188
190{
191 if ( mCachedType == rMax ) {
192 mCachedType = recurrenceType( defaultRRuleConst() );
193 }
194 return mCachedType;
195}
196
198{
199 if ( !rrule ) return rNone;
200 RecurrenceRule::PeriodType type = rrule->recurrenceType();
201
202 // BYSETPOS, BYWEEKNUMBER and BYSECOND were not supported in old versions
203 if ( !rrule->bySetPos().isEmpty() )
204 return rOther;
205 if ( !rrule->bySeconds().isEmpty() )
206 return rOther;
207 if ( !rrule->byWeekNumbers().isEmpty() )
208 return rOther;
209
210 // It wasn't possible to set BYMINUTES, BYHOUR etc. by the old code. So if
211 // it's set, it's none of the old types
212 if ( !rrule->byMinutes().isEmpty() )
213 return rOther;
214 if ( !rrule->byHours().isEmpty() )
215 return rOther;
216
217 // Possible combinations were:
218 // BYDAY: with WEEKLY, MONTHLY, YEARLY
219 // BYMONTHDAY: with MONTHLY, YEARLY
220 // BYMONTH: with YEARLY
221 // BYYEARDAY: with YEARLY
222 if ( !rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly )
223 return rOther;
224 if ( !rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly )
225 return rOther;
226 if ( !rrule->byDays().isEmpty() ) {
227 if ( type != RecurrenceRule::rYearly && type != RecurrenceRule::rMonthly &&
228 type != RecurrenceRule::rWeekly )
229 return rOther;
230 }
231
232 switch ( type ) {
233 case RecurrenceRule::rNone: return rNone;
234 case RecurrenceRule::rMinutely: return rMinutely;
235 case RecurrenceRule::rHourly: return rHourly;
236 case RecurrenceRule::rDaily: return rDaily;
237 case RecurrenceRule::rWeekly: return rWeekly;
238 case RecurrenceRule::rMonthly: {
239 if ( rrule->byDays().isEmpty() ) return rMonthlyDay;
240 else if ( rrule->byMonthDays().isEmpty() ) return rMonthlyPos;
241 else return rOther; // both position and date specified
242 }
243 case RecurrenceRule::rYearly: {
244 // Possible combinations:
245 // rYearlyMonth: [BYMONTH &] BYMONTHDAY
246 // rYearlyDay: BYYEARDAY
247 // rYearlyPos: [BYMONTH &] BYDAY
248 if ( !rrule->byDays().isEmpty() ) {
249 // can only by rYearlyPos
250 if ( rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty() )
251 return rYearlyPos;
252 else return rOther;
253 } else if ( !rrule->byYearDays().isEmpty() ) {
254 // Can only be rYearlyDay
255 if ( rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty() )
256 return rYearlyDay;
257 else return rOther;
258 } else {
259 return rYearlyMonth;
260 }
261 break;
262 }
263 default: return rOther;
264 }
265 return rOther;
266}
267
268bool Recurrence::recursOn(const TQDate &qd) const
269{
270 TimeList tms;
271 // First handle dates. Exrules override
272 if ( mExDates.contains( qd ) ) return false;
273 // For all-day events a matching exrule excludes the whole day
274 // since exclusions take precedence over inclusions, we know it can't occur on that day.
275 if ( doesFloat() ) {
276 for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
277 if ( (*rr)->recursOn( qd ) )
278 return false;
279 }
280 }
281
282 if ( mRDates.contains( qd ) ) return true;
283
284 bool recurs = false;
285
286 for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
287 recurs = recurs || (*rr)->recursOn( qd );
288 }
289 // If we already know it recurs, no need to check the rdate list too.
290 if ( !recurs ) {
291 for ( DateTimeList::ConstIterator rit = mRDateTimes.begin();
292 rit != mRDateTimes.end(); ++rit ) {
293 if ( (*rit).date() == qd ) {
294 recurs = true;
295 break;
296 }
297 }
298 }
299 // If the event wouldn't recur at all, simply return false, don't check ex*
300 if ( !recurs ) return false;
301
302 // Check if there are any times for this day excluded, either by exdate or exrule:
303 bool exon = false;
304 for ( DateTimeList::ConstIterator exit = mExDateTimes.begin();
305 exit != mExDateTimes.end(); ++exit ) {
306 if ( (*exit).date() == qd ) {
307 exon = true;
308 break;
309 }
310 }
311 if ( !doesFloat() ) { // we have already checked floating times above
312 for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
313 exon = exon || (*rr)->recursOn( qd );
314 }
315 }
316
317 if ( !exon ) {
318 // Simple case, nothing on that day excluded, return the value from before
319 return recurs;
320 } else {
321 // Harder part: I don't think there is any way other than to calculate the
322 // whole list of items for that day.
323 TimeList timesForDay( recurTimesOn( qd ) );
324 return !timesForDay.isEmpty();
325 }
326}
327
328bool Recurrence::recursAt( const TQDateTime &dt ) const
329{
330 // if it's excluded anyway, don't bother to check if it recurs at all.
331 if ( mExDateTimes.contains( dt )) return false;
332 if ( mExDates.contains( dt.date() )) return false;
333 for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
334 if ( (*rr)->recursAt( dt ) ) return false;
335 }
336
337 // Check explicit recurrences, then rrules.
338 bool occurs = ( startDateTime() == dt ) || mRDateTimes.contains( dt );
339 if ( occurs )
340 return true;
341 for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
342 if ( (*rr)->recursAt( dt ) ) return true;
343 }
344
345 return false;
346}
347
351TQDateTime Recurrence::endDateTime() const
352{
353 DateTimeList dts;
354 dts << startDateTime();
355 if ( !mRDates.isEmpty() ) dts << TQDateTime( mRDates.last(), TQTime( 0, 0, 0 ) );
356 if ( !mRDateTimes.isEmpty() ) dts << mRDateTimes.last();
357 for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
358 TQDateTime rl( (*rr)->endDt() );
359 // if any of the rules is infinite, the whole recurrence is
360 if ( !rl.isValid() ) return TQDateTime();
361 dts << rl;
362 }
363 qSortUnique( dts );
364 if ( dts.isEmpty() ) return TQDateTime();
365 else return dts.last();
366}
367
372{
373 TQDateTime end( endDateTime() );
374 if ( end.isValid() ) { return end.date(); }
375 else return TQDate();
376}
377
378void Recurrence::setEndDate( const TQDate &date )
379{
380 if ( doesFloat() )
381 setEndDateTime( TQDateTime( date, TQTime( 23, 59, 59 ) ) );
382 else
383 setEndDateTime( TQDateTime( date, mStartDateTime.time() ) );
384}
385
386void Recurrence::setEndDateTime( const TQDateTime &dateTime )
387{
388 if ( mRecurReadOnly ) return;
389 RecurrenceRule *rrule = defaultRRule( true );
390 if ( !rrule ) return;
391 rrule->setEndDt( dateTime );
392 updated();
393}
394
396{
397 RecurrenceRule *rrule = defaultRRuleConst();
398 if ( rrule ) return rrule->duration();
399 else return 0;
400}
401
402// int Recurrence::durationTo( const TQDate &/*date*/ ) const
403// {
404// return 0;
405// }
406
407int Recurrence::durationTo( const TQDateTime &datetime ) const
408{
409 // Emulate old behavior: This is just an interface to the first rule!
410 RecurrenceRule *rrule = defaultRRuleConst();
411 if ( !rrule ) return 0;
412 else return rrule->durationTo( datetime );
413}
414
415void Recurrence::setDuration( int duration )
416{
417 if ( mRecurReadOnly ) return;
418 RecurrenceRule *rrule = defaultRRule( true );
419 if ( !rrule ) return;
420 rrule->setDuration( duration );
421 updated();
422}
423
425{
426 if ( mRecurReadOnly ) return;
427 mRRules.clearAll();
428 updated();
429}
430
432{
433 if ( mRecurReadOnly ) return;
434 mRRules.clearAll();
435 mExRules.clearAll();
436 mRDates.clear();
437 mRDateTimes.clear();
438 mExDates.clear();
439 mExDateTimes.clear();
440 mCachedType = rMax;
441 updated();
442}
443
444void Recurrence::setStartDateTime( const TQDateTime &start )
445{
446 if ( mRecurReadOnly ) return;
447 mStartDateTime = start;
448 setFloats( false ); // set all RRULEs and EXRULEs
449
450 for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
451 (*rr)->setStartDt( start );
452 }
453 for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
454 (*rr)->setStartDt( start );
455 }
456 updated();
457}
458
459void Recurrence::setStartDate( const TQDate &start )
460{
461 setStartDateTime( TQDateTime( start, TQTime(0,0,0) ) );
462 setFloats( true );
463}
464
466{
467 RecurrenceRule *rrule = defaultRRuleConst();
468 if ( rrule ) return rrule->frequency();
469 else return 0;
470}
471
472// Emulate the old behaviour. Make this methods just an interface to the
473// first rrule
475{
476 if ( mRecurReadOnly || freq <= 0 ) return;
477 RecurrenceRule *rrule = defaultRRule( true );
478 if ( rrule )
479 rrule->setFrequency( freq );
480 updated();
481}
482
483
484// WEEKLY
485
487{
488 RecurrenceRule *rrule = defaultRRuleConst();
489 if ( rrule ) return rrule->weekStart();
490 else return 1;
491}
492
493// Emulate the old behavior
494TQBitArray Recurrence::days() const
495{
496 TQBitArray days( 7 );
497 days.fill( 0 );
498 RecurrenceRule *rrule = defaultRRuleConst();
499 if ( rrule ) {
500 TQValueList<RecurrenceRule::WDayPos> bydays = rrule->byDays();
501 for ( TQValueListConstIterator<RecurrenceRule::WDayPos> it = bydays.begin();
502 it != bydays.end(); ++it ) {
503 if ( (*it).pos() == 0 ) {
504 days.setBit( (*it).day() - 1 );
505 }
506 }
507 }
508 return days;
509}
510
511
512// MONTHLY
513
514// Emulate the old behavior
515TQValueList<int> Recurrence::monthDays() const
516{
517 RecurrenceRule *rrule = defaultRRuleConst();
518 if ( rrule ) return rrule->byMonthDays();
519 else return TQValueList<int>();
520}
521
522// Emulate the old behavior
523TQValueList<RecurrenceRule::WDayPos> Recurrence::monthPositions() const
524{
525 RecurrenceRule *rrule = defaultRRuleConst();
526 if ( rrule ) return rrule->byDays();
527 else return TQValueList<RecurrenceRule::WDayPos>();
528}
529
530
531// YEARLY
532
533TQValueList<int> Recurrence::yearDays() const
534{
535 RecurrenceRule *rrule = defaultRRuleConst();
536 if ( rrule ) return rrule->byYearDays();
537 else return TQValueList<int>();
538}
539
540TQValueList<int> Recurrence::yearDates() const
541{
542 return monthDays();
543}
544
545TQValueList<int> Recurrence::yearMonths() const
546{
547 RecurrenceRule *rrule = defaultRRuleConst();
548 if ( rrule ) return rrule->byMonths();
549 else return TQValueList<int>();
550}
551
552TQValueList<RecurrenceRule::WDayPos> Recurrence::yearPositions() const
553{
554 return monthPositions();
555}
556
557
558
559RecurrenceRule *Recurrence::setNewRecurrenceType( RecurrenceRule::PeriodType type, int freq )
560{
561 if ( mRecurReadOnly || freq <= 0 ) return 0;
562 mRRules.clearAll();
563 updated();
564 RecurrenceRule *rrule = defaultRRule( true );
565 if ( !rrule ) return 0;
566 rrule->setRecurrenceType( type );
567 rrule->setFrequency( freq );
568 rrule->setDuration( -1 );
569 return rrule;
570}
571
572void Recurrence::setMinutely( int _rFreq )
573{
574 if ( setNewRecurrenceType( RecurrenceRule::rMinutely, _rFreq ) )
575 updated();
576}
577
578void Recurrence::setHourly( int _rFreq )
579{
580 if ( setNewRecurrenceType( RecurrenceRule::rHourly, _rFreq ) )
581 updated();
582}
583
584void Recurrence::setDaily( int _rFreq )
585{
586 if ( setNewRecurrenceType( RecurrenceRule::rDaily, _rFreq ) )
587 updated();
588}
589
590void Recurrence::setWeekly( int freq, int weekStart )
591{
592 RecurrenceRule *rrule = setNewRecurrenceType( RecurrenceRule::rWeekly, freq );
593 if ( !rrule ) return;
594 rrule->setWeekStart( weekStart );
595 updated();
596}
597
598void Recurrence::setWeekly( int freq, const TQBitArray &days, int weekStart )
599{
600 setWeekly( freq, weekStart );
601 addMonthlyPos( 0, days );
602}
603
604void Recurrence::addWeeklyDays( const TQBitArray &days )
605{
606 addMonthlyPos( 0, days );
607}
608
610{
611 if ( setNewRecurrenceType( RecurrenceRule::rMonthly, freq ) )
612 updated();
613}
614
615void Recurrence::addMonthlyPos( short pos, const TQBitArray &days )
616{
617 // Allow 53 for yearly!
618 if ( mRecurReadOnly || pos > 53 || pos < -53 ) return;
619 RecurrenceRule *rrule = defaultRRule( false );
620 if ( !rrule ) return;
621 bool changed = false;
622 TQValueList<RecurrenceRule::WDayPos> positions = rrule->byDays();
623
624 for ( int i = 0; i < 7; ++i ) {
625 if ( days.testBit(i) ) {
626 RecurrenceRule::WDayPos p( pos, i + 1 );
627 if ( !positions.contains( p ) ) {
628 changed = true;
629 positions.append( p );
630 }
631 }
632 }
633 if ( changed ) {
634 rrule->setByDays( positions );
635 updated();
636 }
637}
638
639
640void Recurrence::addMonthlyPos( short pos, ushort day )
641{
642 // Allow 53 for yearly!
643 if ( mRecurReadOnly || pos > 53 || pos < -53 ) return;
644 RecurrenceRule *rrule = defaultRRule( false );
645 if ( !rrule ) return;
646 TQValueList<RecurrenceRule::WDayPos> positions = rrule->byDays();
647
648 RecurrenceRule::WDayPos p( pos, day );
649 if ( !positions.contains( p ) ) {
650 positions.append( p );
651 rrule->setByDays( positions );
652 updated();
653 }
654}
655
656
658{
659 if ( mRecurReadOnly || day > 31 || day < -31 ) return;
660 RecurrenceRule *rrule = defaultRRule( true );
661 if ( !rrule ) return;
662
663 TQValueList<int> monthDays = rrule->byMonthDays();
664 if ( !monthDays.contains( day ) ) {
665 monthDays.append( day );
666 rrule->setByMonthDays( monthDays );
667 updated();
668 }
669}
670
671void Recurrence::setYearly( int freq )
672{
673 if ( setNewRecurrenceType( RecurrenceRule::rYearly, freq ) )
674 updated();
675}
676
677
678// Daynumber within year
680{
681 RecurrenceRule *rrule = defaultRRule( false ); // It must already exist!
682 if ( !rrule ) return;
683
684 TQValueList<int> days = rrule->byYearDays();
685 if ( !days.contains( day ) ) {
686 days << day;
687 rrule->setByYearDays( days );
688 updated();
689 }
690}
691
692// day part of date within year
694{
695 addMonthlyDate( day );
696}
697
698// day part of date within year, given as position (n-th weekday)
699void Recurrence::addYearlyPos( short pos, const TQBitArray &days )
700{
701 addMonthlyPos( pos, days );
702}
703
704
705// month part of date within year
706void Recurrence::addYearlyMonth( short month )
707{
708 if ( mRecurReadOnly || month < 1 || month > 12 ) return;
709 RecurrenceRule *rrule = defaultRRule( false );
710 if ( !rrule ) return;
711
712 TQValueList<int> months = rrule->byMonths();
713 if ( !months.contains(month) ) {
714 months << month;
715 rrule->setByMonths( months );
716 updated();
717 }
718}
719
720
721TimeList Recurrence::recurTimesOn( const TQDate &date ) const
722{
723 TimeList times;
724 // The whole day is excepted
725 if ( mExDates.contains( date ) ) return times;
726 // EXRULE takes precedence over RDATE entries, so for floating events,
727 // a matching excule also excludes the whole day automatically
728 if ( doesFloat() ) {
729 for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
730 if ( (*rr)->recursOn( date ) )
731 return times;
732 }
733 }
734
735 if ( startDate() == date ) times << startDateTime().time();
736 bool foundDate = false;
737 for ( DateTimeList::ConstIterator it = mRDateTimes.begin();
738 it != mRDateTimes.end(); ++it ) {
739 if ( (*it).date() == date ) {
740 times << (*it).time();
741 foundDate = true;
742 } else if (foundDate) break; // <= Assume that the rdatetime list is sorted
743 }
744 for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
745 times += (*rr)->recurTimesOn( date );
746 }
747 qSortUnique( times );
748
749 foundDate = false;
750 TimeList extimes;
751 for ( DateTimeList::ConstIterator it = mExDateTimes.begin();
752 it != mExDateTimes.end(); ++it ) {
753 if ( (*it).date() == date ) {
754 extimes << (*it).time();
755 foundDate = true;
756 } else if (foundDate) break;
757 }
758 if ( !doesFloat() ) { // we have already checked floating times above
759 for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
760 extimes += (*rr)->recurTimesOn( date );
761 }
762 }
763 qSortUnique( extimes );
764
765 for ( TimeList::Iterator it = extimes.begin(); it != extimes.end(); ++it ) {
766 times.remove( (*it) );
767 }
768 return times;
769}
770
771DateTimeList Recurrence::timesInInterval( const TQDateTime &start, const TQDateTime &end ) const
772{
773 int i, count;
774 DateTimeList times;
775 for ( i = 0, count = mRRules.count(); i < count; ++i ) {
776 times += mRRules[i]->timesInInterval( start, end );
777 }
778
779 // add rdatetimes that fit in the interval
780 for ( i = 0, count = mRDateTimes.count(); i < count; ++i ) {
781 if ( mRDateTimes[i] >= start && mRDateTimes[i] <= end ) {
782 times += mRDateTimes[i];
783 }
784 }
785
786 // add rdates that fit in the interval
787 TQDateTime qdt( mStartDateTime );
788 for ( i = 0, count = mRDates.count(); i < count; ++i ) {
789 qdt.setDate( mRDates[i] );
790 if ( qdt >= start && qdt <= end ) {
791 times += qdt;
792 }
793 }
794
795 // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list
796 // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include
797 // mStartDateTime.
798 // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly
799 // add mStartDateTime to the list, otherwise we won't see the first occurrence.
800 if ( ( !mRDates.isEmpty() || !mRDateTimes.isEmpty() ) &&
801 mRRules.isEmpty() &&
802 start <= mStartDateTime &&
803 end >= mStartDateTime ) {
804 times += mStartDateTime;
805 }
806
807 qSortUnique( times );
808
809 // Remove excluded times
810 int idt = 0;
811 int enddt = times.count();
812 for ( i = 0, count = mExDates.count(); i < count && idt < enddt; ++i ) {
813 while ( idt < enddt && times[idt].date() < mExDates[i] ) ++idt;
814 while ( idt < enddt && times[idt].date() == mExDates[i] ) {
815 times.remove( times.at( idt ) );
816 --enddt;
817 }
818 }
819 DateTimeList extimes;
820 for ( i = 0, count = mExRules.count(); i < count; ++i ) {
821 extimes += mExRules[i]->timesInInterval( start, end );
822 }
823 extimes += mExDateTimes;
824 qSortUnique( extimes );
825
826 int st = 0;
827 for ( i = 0, count = extimes.count(); i < count; ++i ) {
828 int j = removeSorted( times, extimes[i], st );
829 if ( j >= 0 ) {
830 st = j;
831 }
832 }
833
834 return times;
835}
836
837TQDateTime Recurrence::getNextDateTime( const TQDateTime &preDateTime ) const
838{
839 TQDateTime nextDT = preDateTime;
840 // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
841 // the exrule is identical to the rrule). If an occurrence is found, break
842 // out of the loop by returning that TQDateTime
843// TODO_Recurrence: Is a loop counter of 1000 really okay? I mean for secondly
844// recurrence, an exdate might exclude more than 1000 intervals!
845 int loop = 0;
846 while ( loop < 1000 ) {
847 // Outline of the algo:
848 // 1) Find the next date/time after preDateTime when the event could recur
849 // 1.0) Add the start date if it's after preDateTime
850 // 1.1) Use the next occurrence from the explicit RDATE lists
851 // 1.2) Add the next recurrence for each of the RRULEs
852 // 2) Take the earliest recurrence of these = TQDateTime nextDT
853 // 3) If that date/time is not excluded, either explicitly by an EXDATE or
854 // by an EXRULE, return nextDT as the next date/time of the recurrence
855 // 4) If it's excluded, start all at 1), but starting at nextDT (instead
856 // of preDateTime). Loop at most 1000 times.
857 ++loop;
858 // First, get the next recurrence from the RDate lists
859 DateTimeList dates;
860 if ( nextDT < startDateTime() ) {
861 dates << startDateTime();
862 }
863
864 int end;
865 // Assume that the rdatetime list is sorted
866 int i = findGT( mRDateTimes, nextDT, 0 );
867 if ( i >= 0 ) {
868 dates << mRDateTimes[i];
869 }
870
871 TQDateTime qdt( startDateTime() );
872 for ( i = 0, end = mRDates.count(); i < end; ++i ) {
873 qdt.setDate( mRDates[i] );
874 if ( qdt > nextDT ) {
875 dates << qdt;
876 break;
877 }
878 }
879
880 // Add the next occurrences from all RRULEs.
881 for ( i = 0, end = mRRules.count(); i < end; ++i ) {
882 TQDateTime dt = mRRules[i]->getNextDate( nextDT );
883 if ( dt.isValid() ) {
884 dates << dt;
885 }
886 }
887
888 // Take the first of these (all others can't be used later on)
889 qSortUnique( dates );
890 if ( dates.isEmpty() ) {
891 return TQDateTime();
892 }
893 nextDT = dates.first();
894
895 // Check if that date/time is excluded explicitly or by an exrule:
896 if ( !containsSorted( mExDates, nextDT.date() ) &&
897 !containsSorted( mExDateTimes, nextDT ) ) {
898 bool allowed = true;
899 for ( i = 0, end = mExRules.count(); i < end; ++i ) {
900 allowed = allowed && !( mExRules[i]->recursAt( nextDT ) );
901 }
902 if ( allowed ) {
903 return nextDT;
904 }
905 }
906 }
907
908 // Couldn't find a valid occurrences in 1000 loops, something is wrong!
909 return TQDateTime();
910}
911
912TQDateTime Recurrence::getPreviousDateTime( const TQDateTime &afterDateTime ) const
913{
914 TQDateTime prevDT = afterDateTime;
915 // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
916 // the exrule is identical to the rrule). If an occurrence is found, break
917 // out of the loop by returning that TQDateTime
918 int loop = 0;
919 while ( loop < 1000 ) {
920 // Outline of the algo:
921 // 1) Find the next date/time after preDateTime when the event could recur
922 // 1.1) Use the next occurrence from the explicit RDATE lists
923 // 1.2) Add the next recurrence for each of the RRULEs
924 // 2) Take the earliest recurrence of these = TQDateTime nextDT
925 // 3) If that date/time is not excluded, either explicitly by an EXDATE or
926 // by an EXRULE, return nextDT as the next date/time of the recurrence
927 // 4) If it's excluded, start all at 1), but starting at nextDT (instead
928 // of preDateTime). Loop at most 1000 times.
929 ++loop;
930 // First, get the next recurrence from the RDate lists
931 DateTimeList dates;
932 if ( prevDT > startDateTime() ) {
933 dates << startDateTime();
934 }
935
936 int i = findLT( mRDateTimes, prevDT, 0 );
937 if ( i >= 0 ) {
938 dates << mRDateTimes[i];
939 }
940
941 TQDateTime qdt( startDateTime() );
942 for ( i = mRDates.count(); --i >= 0; ) {
943 qdt.setDate( mRDates[i] );
944 if ( qdt < prevDT ) {
945 dates << qdt;
946 break;
947 }
948 }
949
950 // Add the previous occurrences from all RRULEs.
951 int end;
952 for ( i = 0, end = mRRules.count(); i < end; ++i ) {
953 TQDateTime dt = mRRules[i]->getPreviousDate( prevDT );
954 if ( dt.isValid() ) {
955 dates << dt;
956 }
957 }
958
959 // Take the last of these (all others can't be used later on)
960 qSortUnique( dates );
961 if ( dates.isEmpty() ) {
962 return TQDateTime();
963 }
964 prevDT = dates.last();
965
966 // Check if that date/time is excluded explicitly or by an exrule:
967 if ( !containsSorted( mExDates, prevDT.date() ) &&
968 !containsSorted( mExDateTimes, prevDT ) ) {
969 bool allowed = true;
970 for ( i = 0, end = mExRules.count(); i < end; ++i ) {
971 allowed = allowed && !( mExRules[i]->recursAt( prevDT ) );
972 }
973 if ( allowed ) {
974 return prevDT;
975 }
976 }
977 }
978
979 // Couldn't find a valid occurrences in 1000 loops, something is wrong!
980 return TQDateTime();
981}
982
983
984/***************************** PROTECTED FUNCTIONS ***************************/
985
986
987RecurrenceRule::List Recurrence::rRules() const
988{
989 return mRRules;
990}
991
992void Recurrence::addRRule( RecurrenceRule *rrule )
993{
994 if ( mRecurReadOnly || !rrule ) return;
995 rrule->setFloats( mFloating );
996 mRRules.append( rrule );
997 rrule->addObserver( this );
998 updated();
999}
1000
1001void Recurrence::removeRRule( RecurrenceRule *rrule )
1002{
1003 if (mRecurReadOnly) return;
1004 mRRules.remove( rrule );
1005 rrule->removeObserver( this );
1006 updated();
1007}
1008
1009RecurrenceRule::List Recurrence::exRules() const
1010{
1011 return mExRules;
1012}
1013
1014void Recurrence::addExRule( RecurrenceRule *exrule )
1015{
1016 if ( mRecurReadOnly || !exrule ) return;
1017 exrule->setFloats( mFloating );
1018 mExRules.append( exrule );
1019 exrule->addObserver( this );
1020 updated();
1021}
1022
1023void Recurrence::removeExRule( RecurrenceRule *exrule )
1024{
1025 if (mRecurReadOnly) return;
1026 mExRules.remove( exrule );
1027 exrule->removeObserver( this );
1028 updated();
1029}
1030
1031
1032DateTimeList Recurrence::rDateTimes() const
1033{
1034 return mRDateTimes;
1035}
1036
1037void Recurrence::setRDateTimes( const DateTimeList &rdates )
1038{
1039 if ( mRecurReadOnly ) return;
1040 mRDateTimes = rdates;
1041 qSortUnique( mRDateTimes );
1042 updated();
1043}
1044
1045void Recurrence::addRDateTime( const TQDateTime &rdate )
1046{
1047 if ( mRecurReadOnly ) return;
1048 mRDateTimes.append( rdate );
1049 qSortUnique( mRDateTimes );
1050 updated();
1051}
1052
1053
1054DateList Recurrence::rDates() const
1055{
1056 return mRDates;
1057}
1058
1059void Recurrence::setRDates( const DateList &rdates )
1060{
1061 if ( mRecurReadOnly ) return;
1062 mRDates = rdates;
1063 qSortUnique( mRDates );
1064 updated();
1065}
1066
1067void Recurrence::addRDate( const TQDate &rdate )
1068{
1069 if ( mRecurReadOnly ) return;
1070 mRDates.append( rdate );
1071 qSortUnique( mRDates );
1072 updated();
1073}
1074
1075
1076DateTimeList Recurrence::exDateTimes() const
1077{
1078 return mExDateTimes;
1079}
1080
1081void Recurrence::setExDateTimes( const DateTimeList &exdates )
1082{
1083 if ( mRecurReadOnly ) return;
1084 mExDateTimes = exdates;
1085 qSortUnique( mExDateTimes );
1086}
1087
1088void Recurrence::addExDateTime( const TQDateTime &exdate )
1089{
1090 if ( mRecurReadOnly ) return;
1091 mExDateTimes.append( exdate );
1092 qSortUnique( mExDateTimes );
1093 updated();
1094}
1095
1096
1097DateList Recurrence::exDates() const
1098{
1099 return mExDates;
1100}
1101
1102void Recurrence::setExDates( const DateList &exdates )
1103{
1104 if ( mRecurReadOnly ) return;
1105 mExDates = exdates;
1106 qSortUnique( mExDates );
1107 updated();
1108}
1109
1110void Recurrence::addExDate( const TQDate &exdate )
1111{
1112 if ( mRecurReadOnly ) return;
1113 mExDates.append( exdate );
1114 qSortUnique( mExDates );
1115 updated();
1116}
1117
1118void Recurrence::recurrenceChanged( RecurrenceRule * )
1119{
1120 updated();
1121}
1122
1123
1124// %%%%%%%%%%%%%%%%%% end:Recurrencerule %%%%%%%%%%%%%%%%%%
1125
1127{
1128 kdDebug(5800) << "Recurrence::dump():" << endl;
1129
1130 kdDebug(5800) << " -) " << mRRules.count() << " RRULEs: " << endl;
1131 for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
1132 kdDebug(5800) << " -) RecurrenceRule : " << endl;
1133 (*rr)->dump();
1134 }
1135 kdDebug(5800) << " -) " << mExRules.count() << " EXRULEs: " << endl;
1136 for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
1137 kdDebug(5800) << " -) ExceptionRule : " << endl;
1138 (*rr)->dump();
1139 }
1140
1141
1142 kdDebug(5800) << endl << " -) " << mRDates.count() << " Recurrence Dates: " << endl;
1143 for ( DateList::ConstIterator it = mRDates.begin(); it != mRDates.end(); ++it ) {
1144 kdDebug(5800) << " " << (*it) << endl;
1145 }
1146 kdDebug(5800) << endl << " -) " << mRDateTimes.count() << " Recurrence Date/Times: " << endl;
1147 for ( DateTimeList::ConstIterator it = mRDateTimes.begin(); it != mRDateTimes.end(); ++it ) {
1148 kdDebug(5800) << " " << (*it) << endl;
1149 }
1150 kdDebug(5800) << endl << " -) " << mExDates.count() << " Exceptions Dates: " << endl;
1151 for ( DateList::ConstIterator it = mExDates.begin(); it != mExDates.end(); ++it ) {
1152 kdDebug(5800) << " " << (*it) << endl;
1153 }
1154 kdDebug(5800) << endl << " -) " << mExDateTimes.count() << " Exception Date/Times: " << endl;
1155 for ( DateTimeList::ConstIterator it = mExDateTimes.begin(); it != mExDateTimes.end(); ++it ) {
1156 kdDebug(5800) << " " << (*it) << endl;
1157 }
1158}
structure for describing the n-th weekday of the month/year.
This class represents a recurrence rule for a calendar incidence.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
int durationTo(const TQDateTime &) const
Returns the number of recurrences up to and including the date/time specified.
void setFrequency(int freq)
Sets the frequency of recurrence, in terms of the recurrence time period type.
void removeObserver(Observer *observer)
Removes an observer that was added with addObserver.
void setFloats(bool floats)
Sets whether the dtstart is a floating time (i.e.
void setEndDt(const TQDateTime &endDateTime)
Sets the date and time of the last recurrence.
uint frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
void addObserver(Observer *observer)
Installs an observer.
PeriodType
enum for describing the frequency how an event recurs, if at all.
void setStartDt(const TQDateTime &start)
Set start of recurrence, as a date and time.
This class represents a recurrence rule for a calendar incidence.
Definition: recurrence.h:90
ushort recurrenceType() const
Returns the event's recurrence status.
Definition: recurrence.cpp:189
void addYearlyPos(short pos, const TQBitArray &days)
Adds position within month/year within a yearly recurrence.
Definition: recurrence.cpp:699
void setFloats(bool floats)
Sets whether the dtstart is a floating time (i.e.
Definition: recurrence.cpp:133
void setStartDate(const TQDate &start)
Set start of recurrence, as a date.
Definition: recurrence.cpp:459
TQDateTime endDateTime() const
Returns the date/time of the last recurrence.
Definition: recurrence.cpp:351
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
Definition: recurrence.cpp:465
void setEndDateTime(const TQDateTime &endDateTime)
Sets the date and time of the last recurrence.
Definition: recurrence.cpp:386
void addYearlyDay(int day)
Adds day number of year within a yearly recurrence.
Definition: recurrence.cpp:679
void setYearly(int freq)
Sets an event to recur yearly.
Definition: recurrence.cpp:671
void setWeekly(int freq, int weekStart=1)
Sets an event to recur weekly.
Definition: recurrence.cpp:590
DateTimeList timesInInterval(const TQDateTime &start, const TQDateTime &end) const
Returns a list of all the times at which the recurrence will occur between two specified times.
Definition: recurrence.cpp:771
TQValueList< RecurrenceRule::WDayPos > yearPositions() const
Returns the positions within a yearly recurrence.
Definition: recurrence.cpp:552
TQBitArray days() const
Returns week day mask (bit 0 = Monday).
Definition: recurrence.cpp:494
TQDate startDate() const
Return the start date/time of the recurrence.
Definition: recurrence.h:116
void setMinutely(int freq)
Sets an event to recur minutely.
Definition: recurrence.cpp:572
void setMonthly(int freq)
Sets an event to recur monthly.
Definition: recurrence.cpp:609
void addWeeklyDays(const TQBitArray &days)
Adds days to the weekly day recurrence list.
Definition: recurrence.cpp:604
void setFrequency(int freq)
Sets the frequency of recurrence, in terms of the recurrence time period type.
Definition: recurrence.cpp:474
TQValueList< int > monthDays() const
Returns list of day numbers of a month.
Definition: recurrence.cpp:515
void addYearlyMonth(short _rNum)
Adds month in yearly recurrence.
Definition: recurrence.cpp:706
void setStartDateTime(const TQDateTime &start)
Set start of recurrence, as a date and time.
Definition: recurrence.cpp:444
void addMonthlyPos(short pos, const TQBitArray &days)
Adds a position (e.g.
Definition: recurrence.cpp:615
void addYearlyDate(int date)
Adds date within a yearly recurrence.
Definition: recurrence.cpp:693
TQDateTime getPreviousDateTime(const TQDateTime &afterDateTime) const
Returns the date and time of the last previous recurrence, before the specified date/time.
Definition: recurrence.cpp:912
TQDateTime startDateTime() const
Return the start date/time of the recurrence (Time for floating incidences will be 0:00).
Definition: recurrence.cpp:126
void addObserver(Observer *observer)
Installs an observer.
Definition: recurrence.cpp:113
void setDaily(int freq)
Sets an event to recur daily.
Definition: recurrence.cpp:584
void unsetRecurs()
Removes all recurrence rules.
Definition: recurrence.cpp:424
int durationTo(const TQDateTime &) const
Returns the number of recurrences up to and including the date/time specified.
Definition: recurrence.cpp:407
void dump() const
Debug output.
TQValueList< TQTime > recurTimesOn(const TQDate &date) const
Returns a list of the times on the specified date at which the recurrence will occur.
Definition: recurrence.cpp:721
bool doesRecur() const
Returns whether the event recurs at all.
Definition: recurrence.cpp:184
int weekStart() const
Returns the first day of the week.
Definition: recurrence.cpp:486
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
Definition: recurrence.cpp:395
void setHourly(int freq)
Sets an event to recur hourly.
Definition: recurrence.cpp:578
TQDate endDate() const
Returns the date of the last recurrence.
Definition: recurrence.cpp:371
void clear()
Removes all recurrence and exception rules and dates.
Definition: recurrence.cpp:431
TQValueList< int > yearMonths() const
Returns the months within a yearly recurrence.
Definition: recurrence.cpp:545
void addMonthlyDate(short day)
Adds a date (e.g.
Definition: recurrence.cpp:657
bool recursAt(const TQDateTime &) const
Returns true if the date/time specified is one at which the event will recur.
Definition: recurrence.cpp:328
TQValueList< RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
Definition: recurrence.cpp:523
bool recursOn(const TQDate &qd) const
Returns true if the date specified is one on which the event will recur.
Definition: recurrence.cpp:268
bool doesFloat() const
Set whether the recurrence has no time, just a date.
Definition: recurrence.h:132
void setEndDate(const TQDate &endDate)
Sets the date of the last recurrence.
Definition: recurrence.cpp:378
TQValueList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
Definition: recurrence.cpp:533
TQValueList< int > yearDates() const
Returns the dates within a yearly recurrence.
Definition: recurrence.cpp:540
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
Definition: recurrence.cpp:415
TQDateTime getNextDateTime(const TQDateTime &preDateTime) const
Returns the date and time of the next recurrence, after the specified date/time.
Definition: recurrence.cpp:837
void removeObserver(Observer *observer)
Removes an observer that was added with addObserver.
Definition: recurrence.cpp:119
Namespace KCal is for global classes, objects and/or functions in libkcal.
Definition: alarm.h:38