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.
void addYearlyPos(short pos, const TQBitArray &days)
Adds position within month/year within a yearly recurrence.
void setFloats(bool floats)
Sets whether the dtstart is a floating time (i.e.
void setStartDate(const TQDate &start)
Set start of recurrence, as a date.
TQDateTime endDateTime() const
Returns the date/time of the last recurrence.
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
void setEndDateTime(const TQDateTime &endDateTime)
Sets the date and time of the last recurrence.
void addYearlyDay(int day)
Adds day number of year within a yearly recurrence.
void setYearly(int freq)
Sets an event to recur yearly.
void setWeekly(int freq, int weekStart=1)
Sets an event to recur weekly.
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.
TQValueList< RecurrenceRule::WDayPos > yearPositions() const
Returns the positions within a yearly recurrence.
TQBitArray days() const
Returns week day mask (bit 0 = Monday).
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.
void setMonthly(int freq)
Sets an event to recur monthly.
void addWeeklyDays(const TQBitArray &days)
Adds days to the weekly day recurrence list.
void setFrequency(int freq)
Sets the frequency of recurrence, in terms of the recurrence time period type.
TQValueList< int > monthDays() const
Returns list of day numbers of a month.
void addYearlyMonth(short _rNum)
Adds month in yearly recurrence.
void setStartDateTime(const TQDateTime &start)
Set start of recurrence, as a date and time.
void addMonthlyPos(short pos, const TQBitArray &days)
Adds a position (e.g.
void addYearlyDate(int date)
Adds date within a yearly recurrence.
TQDateTime getPreviousDateTime(const TQDateTime &afterDateTime) const
Returns the date and time of the last previous recurrence, before the specified date/time.
TQDateTime startDateTime() const
Return the start date/time of the recurrence (Time for floating incidences will be 0:00).
void addObserver(Observer *observer)
Installs an observer.
void setDaily(int freq)
Sets an event to recur daily.
void unsetRecurs()
Removes all recurrence rules.
int durationTo(const TQDateTime &) const
Returns the number of recurrences up to and including the date/time specified.
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.
bool doesRecur() const
Returns whether the event recurs at all.
int weekStart() const
Returns the first day of the week.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
void setHourly(int freq)
Sets an event to recur hourly.
TQDate endDate() const
Returns the date of the last recurrence.
void clear()
Removes all recurrence and exception rules and dates.
TQValueList< int > yearMonths() const
Returns the months within a yearly recurrence.
void addMonthlyDate(short day)
Adds a date (e.g.
bool recursAt(const TQDateTime &) const
Returns true if the date/time specified is one at which the event will recur.
TQValueList< RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
bool recursOn(const TQDate &qd) const
Returns true if the date specified is one on which the event will recur.
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.
TQValueList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
TQValueList< int > yearDates() const
Returns the dates within a yearly recurrence.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
TQDateTime getNextDateTime(const TQDateTime &preDateTime) const
Returns the date and time of the next recurrence, after the specified date/time.
void removeObserver(Observer *observer)
Removes an observer that was added with addObserver.
Namespace KCal is for global classes, objects and/or functions in libkcal.
Definition alarm.h:38