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 }
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