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 
35 using namespace KCal;
36 
37 Recurrence::Recurrence()
38 : mFloating( false ),
39  mRecurReadOnly(false),
40  mCachedType(rMax)
41 {
42  mExRules.setAutoDelete( true );
43  mRRules.setAutoDelete( true );
44 }
45 
46 Recurrence::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 
70 Recurrence::~Recurrence()
71 {
72 }
73 
74 
75 
76 bool 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 
113 void Recurrence::addObserver( Observer *observer )
114 {
115  if ( !mObservers.contains( observer ) )
116  mObservers.append( observer );
117 }
118 
119 void Recurrence::removeObserver( Observer *observer )
120 {
121  if ( mObservers.contains( observer ) )
122  mObservers.remove( observer );
123 }
124 
125 
126 TQDateTime Recurrence::startDateTime() const
127 {
128  if ( mFloating )
129  return TQDateTime( mStartDateTime.date(), TQTime( 0, 0, 0 ) );
130  else return mStartDateTime;
131 }
132 
133 void 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 
152 RecurrenceRule *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 
165 RecurrenceRule *Recurrence::defaultRRuleConst() const
166 {
167  if ( mRRules.isEmpty() ) {
168  return 0;
169  } else {
170  return mRRules.first();
171  }
172 }
173 
174 void 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 
268 bool 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 
328 bool 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 
351 TQDateTime 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 
371 TQDate Recurrence::endDate() const
372 {
373  TQDateTime end( endDateTime() );
374  if ( end.isValid() ) { return end.date(); }
375  else return TQDate();
376 }
377 
378 void 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 
386 void 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 
407 int 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 
415 void 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 
444 void 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 
459 void 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
474 void Recurrence::setFrequency( int freq )
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
494 TQBitArray 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
515 TQValueList<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
523 TQValueList<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 
533 TQValueList<int> Recurrence::yearDays() const
534 {
535  RecurrenceRule *rrule = defaultRRuleConst();
536  if ( rrule ) return rrule->byYearDays();
537  else return TQValueList<int>();
538 }
539 
540 TQValueList<int> Recurrence::yearDates() const
541 {
542  return monthDays();
543 }
544 
545 TQValueList<int> Recurrence::yearMonths() const
546 {
547  RecurrenceRule *rrule = defaultRRuleConst();
548  if ( rrule ) return rrule->byMonths();
549  else return TQValueList<int>();
550 }
551 
552 TQValueList<RecurrenceRule::WDayPos> Recurrence::yearPositions() const
553 {
554  return monthPositions();
555 }
556 
557 
558 
559 RecurrenceRule *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 
572 void Recurrence::setMinutely( int _rFreq )
573 {
574  if ( setNewRecurrenceType( RecurrenceRule::rMinutely, _rFreq ) )
575  updated();
576 }
577 
578 void Recurrence::setHourly( int _rFreq )
579 {
580  if ( setNewRecurrenceType( RecurrenceRule::rHourly, _rFreq ) )
581  updated();
582 }
583 
584 void Recurrence::setDaily( int _rFreq )
585 {
586  if ( setNewRecurrenceType( RecurrenceRule::rDaily, _rFreq ) )
587  updated();
588 }
589 
590 void 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 
598 void Recurrence::setWeekly( int freq, const TQBitArray &days, int weekStart )
599 {
600  setWeekly( freq, weekStart );
601  addMonthlyPos( 0, days );
602 }
603 
604 void Recurrence::addWeeklyDays( const TQBitArray &days )
605 {
606  addMonthlyPos( 0, days );
607 }
608 
609 void Recurrence::setMonthly( int freq )
610 {
611  if ( setNewRecurrenceType( RecurrenceRule::rMonthly, freq ) )
612  updated();
613 }
614 
615 void 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 
640 void 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 
657 void Recurrence::addMonthlyDate( short day )
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 
671 void 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)
699 void Recurrence::addYearlyPos( short pos, const TQBitArray &days )
700 {
701  addMonthlyPos( pos, days );
702 }
703 
704 
705 // month part of date within year
706 void 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 
721 TimeList 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 
771 DateTimeList 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 
837 TQDateTime 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 
912 TQDateTime 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 
987 RecurrenceRule::List Recurrence::rRules() const
988 {
989  return mRRules;
990 }
991 
992 void 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 
1001 void Recurrence::removeRRule( RecurrenceRule *rrule )
1002 {
1003  if (mRecurReadOnly) return;
1004  mRRules.remove( rrule );
1005  rrule->removeObserver( this );
1006  updated();
1007 }
1008 
1009 RecurrenceRule::List Recurrence::exRules() const
1010 {
1011  return mExRules;
1012 }
1013 
1014 void 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 
1023 void Recurrence::removeExRule( RecurrenceRule *exrule )
1024 {
1025  if (mRecurReadOnly) return;
1026  mExRules.remove( exrule );
1027  exrule->removeObserver( this );
1028  updated();
1029 }
1030 
1031 
1032 DateTimeList Recurrence::rDateTimes() const
1033 {
1034  return mRDateTimes;
1035 }
1036 
1037 void Recurrence::setRDateTimes( const DateTimeList &rdates )
1038 {
1039  if ( mRecurReadOnly ) return;
1040  mRDateTimes = rdates;
1041  qSortUnique( mRDateTimes );
1042  updated();
1043 }
1044 
1045 void Recurrence::addRDateTime( const TQDateTime &rdate )
1046 {
1047  if ( mRecurReadOnly ) return;
1048  mRDateTimes.append( rdate );
1049  qSortUnique( mRDateTimes );
1050  updated();
1051 }
1052 
1053 
1054 DateList Recurrence::rDates() const
1055 {
1056  return mRDates;
1057 }
1058 
1059 void Recurrence::setRDates( const DateList &rdates )
1060 {
1061  if ( mRecurReadOnly ) return;
1062  mRDates = rdates;
1063  qSortUnique( mRDates );
1064  updated();
1065 }
1066 
1067 void Recurrence::addRDate( const TQDate &rdate )
1068 {
1069  if ( mRecurReadOnly ) return;
1070  mRDates.append( rdate );
1071  qSortUnique( mRDates );
1072  updated();
1073 }
1074 
1075 
1076 DateTimeList Recurrence::exDateTimes() const
1077 {
1078  return mExDateTimes;
1079 }
1080 
1081 void Recurrence::setExDateTimes( const DateTimeList &exdates )
1082 {
1083  if ( mRecurReadOnly ) return;
1084  mExDateTimes = exdates;
1085  qSortUnique( mExDateTimes );
1086 }
1087 
1088 void Recurrence::addExDateTime( const TQDateTime &exdate )
1089 {
1090  if ( mRecurReadOnly ) return;
1091  mExDateTimes.append( exdate );
1092  qSortUnique( mExDateTimes );
1093  updated();
1094 }
1095 
1096 
1097 DateList Recurrence::exDates() const
1098 {
1099  return mExDates;
1100 }
1101 
1102 void Recurrence::setExDates( const DateList &exdates )
1103 {
1104  if ( mRecurReadOnly ) return;
1105  mExDates = exdates;
1106  qSortUnique( mExDates );
1107  updated();
1108 }
1109 
1110 void Recurrence::addExDate( const TQDate &exdate )
1111 {
1112  if ( mRecurReadOnly ) return;
1113  mExDates.append( exdate );
1114  qSortUnique( mExDates );
1115  updated();
1116 }
1117 
1118 void Recurrence::recurrenceChanged( RecurrenceRule * )
1119 {
1120  updated();
1121 }
1122 
1123 
1124 // %%%%%%%%%%%%%%%%%% end:Recurrencerule %%%%%%%%%%%%%%%%%%
1125 
1126 void Recurrence::dump() const
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 }
void addObserver(Observer *observer)
Installs an observer.
Definition: recurrence.cpp:113
void addYearlyMonth(short _rNum)
Adds month in yearly recurrence.
Definition: recurrence.cpp:706
TQBitArray days() const
Returns week day mask (bit 0 = Monday).
Definition: recurrence.cpp:494
int frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
Definition: recurrence.cpp:465
TQValueList< RecurrenceRule::WDayPos > monthPositions() const
Returns list of day positions in months.
Definition: recurrence.cpp:523
void dump() const
Debug output.
void setFloats(bool floats)
Sets whether the dtstart is a floating time (i.e.
void setFloats(bool floats)
Sets whether the dtstart is a floating time (i.e.
Definition: recurrence.cpp:133
TQValueList< int > yearMonths() const
Returns the months within a yearly recurrence.
Definition: recurrence.cpp:545
void setEndDateTime(const TQDateTime &endDateTime)
Sets the date and time of the last recurrence.
Definition: recurrence.cpp:386
int durationTo(const TQDateTime &) const
Returns the number of recurrences up to and including the date/time specified.
void addMonthlyPos(short pos, const TQBitArray &days)
Adds a position (e.g.
Definition: recurrence.cpp:615
This class represents a recurrence rule for a calendar incidence.
Definition: recurrence.h:89
void setMonthly(int freq)
Sets an event to recur monthly.
Definition: recurrence.cpp:609
void addYearlyDay(int day)
Adds day number of year within a yearly recurrence.
Definition: recurrence.cpp:679
void addYearlyPos(short pos, const TQBitArray &days)
Adds position within month/year within a yearly recurrence.
Definition: recurrence.cpp:699
void setDaily(int freq)
Sets an event to recur daily.
Definition: recurrence.cpp:584
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
PeriodType
enum for describing the frequency how an event recurs, if at all.
bool doesFloat() const
Set whether the recurrence has no time, just a date.
Definition: recurrence.h:132
void setYearly(int freq)
Sets an event to recur yearly.
Definition: recurrence.cpp:671
TQDate startDate() const
Return the start date/time of the recurrence.
Definition: recurrence.h:116
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
int durationTo(const TQDateTime &) const
Returns the number of recurrences up to and including the date/time specified.
Definition: recurrence.cpp:407
Definition: alarm.h:38
void setHourly(int freq)
Sets an event to recur hourly.
Definition: recurrence.cpp:578
void setStartDate(const TQDate &start)
Set start of recurrence, as a date.
Definition: recurrence.cpp:459
void unsetRecurs()
Removes all recurrence rules.
Definition: recurrence.cpp:424
TQDateTime endDateTime() const
Returns the date/time of the last recurrence.
Definition: recurrence.cpp:351
TQValueList< int > monthDays() const
Returns list of day numbers of a month.
Definition: recurrence.cpp:515
void removeObserver(Observer *observer)
Removes an observer that was added with addObserver.
Definition: recurrence.cpp:119
TQValueList< int > yearDates() const
Returns the dates within a yearly recurrence.
Definition: recurrence.cpp:540
void setMinutely(int freq)
Sets an event to recur minutely.
Definition: recurrence.cpp:572
void setStartDt(const TQDateTime &start)
Set start of recurrence, as a date and time.
void addYearlyDate(int date)
Adds date within a yearly recurrence.
Definition: recurrence.cpp:693
TQDateTime startDateTime() const
Return the start date/time of the recurrence (Time for floating incidences will be 0:00).
Definition: recurrence.cpp:126
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
uint frequency() const
Returns frequency of recurrence, in terms of the recurrence time period type.
bool doesRecur() const
Returns whether the event recurs at all.
Definition: recurrence.cpp:184
TQDate endDate() const
Returns the date of the last recurrence.
Definition: recurrence.cpp:371
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
TQValueList< RecurrenceRule::WDayPos > yearPositions() const
Returns the positions within a yearly recurrence.
Definition: recurrence.cpp:552
void setFrequency(int freq)
Sets the frequency of recurrence, in terms of the recurrence time period type.
structure for describing the n-th weekday of the month/year.
TQValueList< int > yearDays() const
Returns the day numbers within a yearly recurrence.
Definition: recurrence.cpp:533
void setFrequency(int freq)
Sets the frequency of recurrence, in terms of the recurrence time period type.
Definition: recurrence.cpp:474
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
void setStartDateTime(const TQDateTime &start)
Set start of recurrence, as a date and time.
Definition: recurrence.cpp:444
void clear()
Removes all recurrence and exception rules and dates.
Definition: recurrence.cpp:431
void setEndDt(const TQDateTime &endDateTime)
Sets the date and time of the last recurrence.
bool recursAt(const TQDateTime &) const
Returns true if the date/time specified is one at which the event will recur.
Definition: recurrence.cpp:328
int weekStart() const
Returns the first day of the week.
Definition: recurrence.cpp:486
void addMonthlyDate(short day)
Adds a date (e.g.
Definition: recurrence.cpp:657
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
bool recursOn(const TQDate &qd) const
Returns true if the date specified is one on which the event will recur.
Definition: recurrence.cpp:268
void setEndDate(const TQDate &endDate)
Sets the date of the last recurrence.
Definition: recurrence.cpp:378
This class represents a recurrence rule for a calendar incidence.
void removeObserver(Observer *observer)
Removes an observer that was added with addObserver.
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
void addObserver(Observer *observer)
Installs an observer.
void addWeeklyDays(const TQBitArray &days)
Adds days to the weekly day recurrence list.
Definition: recurrence.cpp:604
void setWeekly(int freq, int weekStart=1)
Sets an event to recur weekly.
Definition: recurrence.cpp:590
ushort recurrenceType() const
Returns the event's recurrence status.
Definition: recurrence.cpp:189
TQDateTime getNextDateTime(const TQDateTime &preDateTime) const
Returns the date and time of the next recurrence, after the specified date/time.
Definition: recurrence.cpp:837