libkcal

recurrence.cpp

00001 /*
00002     This file is part of libkcal.
00003 
00004     Copyright (c) 1998 Preston Brown <pbrown@kde.org>
00005     Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00006     Copyright (c) 2002 David Jarvie <software@astrojar.org.uk>
00007     Copyright (C) 2005 Reinhold Kainhofer <kainhofer@kde.org>
00008 
00009     This library is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU Library General Public
00011     License as published by the Free Software Foundation; either
00012     version 2 of the License, or (at your option) any later version.
00013 
00014     This library is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017     Library General Public License for more details.
00018 
00019     You should have received a copy of the GNU Library General Public License
00020     along with this library; see the file COPYING.LIB.  If not, write to
00021     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022     Boston, MA 02110-1301, USA.
00023 */
00024 
00025 #include <limits.h>
00026 
00027 #include <kdebug.h>
00028 #include <kglobal.h>
00029 #include <klocale.h>
00030 #include <tqbitarray.h>
00031 
00032 #include "recurrence.h"
00033 #include "recurrencerule.h"
00034 
00035 using namespace KCal;
00036 
00037 Recurrence::Recurrence()
00038 : mFloating( false ),
00039   mRecurReadOnly(false),
00040   mCachedType(rMax)
00041 {
00042   mExRules.setAutoDelete( true );
00043   mRRules.setAutoDelete( true );
00044 }
00045 
00046 Recurrence::Recurrence( const Recurrence &r )
00047 : RecurrenceRule::Observer(),
00048   mRDateTimes( r.mRDateTimes ), mRDates( r.mRDates ),
00049   mExDateTimes( r.mExDateTimes ), mExDates( r.mExDates ),
00050   mStartDateTime( r.mStartDateTime ),
00051   mFloating( r.mFloating ),
00052   mRecurReadOnly(r.mRecurReadOnly),
00053   mCachedType( r.mCachedType )
00054 {
00055   mExRules.setAutoDelete( true );
00056   mRRules.setAutoDelete( true );
00057   RecurrenceRule::List::ConstIterator rr;
00058   for ( rr = r.mRRules.begin(); rr != r.mRRules.end(); ++rr ) {
00059     RecurrenceRule *rule = new RecurrenceRule( *(*rr) );
00060     mRRules.append( rule );
00061     rule->addObserver( this );
00062   }
00063   for ( rr = r.mExRules.begin(); rr != r.mExRules.end(); ++rr ) {
00064     RecurrenceRule *rule = new RecurrenceRule( *(*rr) );
00065     mExRules.append( rule );
00066     rule->addObserver( this );
00067   }
00068 }
00069 
00070 Recurrence::~Recurrence()
00071 {
00072 }
00073 
00074 
00075 
00076 bool Recurrence::operator==( const Recurrence& r2 ) const
00077 {
00078   if ( mStartDateTime != r2.mStartDateTime
00079   ||   mFloating != r2.mFloating
00080   ||   mRecurReadOnly != r2.mRecurReadOnly )
00081     return false;
00082   if ( mExDates != r2.mExDates ) return false;
00083   if ( mExDateTimes != r2.mExDateTimes ) return false;
00084   if ( mRDates != r2.mRDates ) return false;
00085   if ( mRDateTimes != r2.mRDateTimes ) return false;
00086 
00087 // Compare the rrules, exrules! Assume they have the same order... This only
00088 // matters if we have more than one rule (which shouldn't be the default anyway)
00089   if ( mRRules.count() != r2.mRRules.count() ) return false;
00090   RecurrenceRule::List::ConstIterator rit1 = mRRules.begin();
00091   RecurrenceRule::List::ConstIterator rit2 = r2.mRRules.begin();
00092 
00093   while ( rit1 != mRRules.end() && rit2 != r2.mRRules.end() ) {
00094     // dereference the iterator to the RecurrenceRule*, and that once again
00095     // to RecurrenceRule...
00096     if ( *(*rit1) != *(*rit2) ) return false;
00097     ++rit1;
00098     ++rit2;
00099   }
00100   RecurrenceRule::List::ConstIterator exit1 = mExRules.begin();
00101   RecurrenceRule::List::ConstIterator exit2 = r2.mExRules.begin();
00102 
00103   while ( exit1 != mExRules.end() && exit2 != r2.mExRules.end() ) {
00104     // dereference the iterator to the RecurrenceRule*, and that once again
00105     // to RecurrenceRule...
00106     if ( *(*exit1) != *(*exit2) ) return false;
00107     ++exit1;
00108     ++exit2;
00109   }
00110   return true;
00111 }
00112 
00113 void Recurrence::addObserver( Observer *observer )
00114 {
00115   if ( !mObservers.contains( observer ) )
00116     mObservers.append( observer );
00117 }
00118 
00119 void Recurrence::removeObserver( Observer *observer )
00120 {
00121   if ( mObservers.contains( observer ) )
00122     mObservers.remove( observer );
00123 }
00124 
00125 
00126 TQDateTime Recurrence::startDateTime() const
00127 {
00128   if ( mFloating )
00129     return TQDateTime( mStartDateTime.date(), TQTime( 0, 0, 0 ) );
00130   else return mStartDateTime;
00131 }
00132 
00133 void Recurrence::setFloats( bool floats )
00134 {
00135   if ( mRecurReadOnly ) return;
00136   if ( floats == mFloating ) return;
00137   mFloating = floats;
00138 
00139 
00140   RecurrenceRule::List::ConstIterator it;
00141   for ( it = mRRules.begin(); it != mRRules.end(); ++it ) {
00142     (*it)->setFloats( floats );
00143   }
00144 
00145   RecurrenceRule::List::ConstIterator it1;
00146   for ( it1 = mExRules.begin(); it1 != mExRules.end(); ++it1 ) {
00147     (*it1)->setFloats( floats );
00148   }
00149   updated();
00150 }
00151 
00152 RecurrenceRule *Recurrence::defaultRRule( bool create ) const
00153 {
00154   if ( mRRules.isEmpty() ) {
00155     if ( !create || mRecurReadOnly ) return 0;
00156     RecurrenceRule *rrule = new RecurrenceRule();
00157     rrule->setStartDt( startDateTime() );
00158     const_cast<KCal::Recurrence*>(this)->addRRule( rrule );
00159     return rrule;
00160   } else {
00161     return mRRules.first();
00162   }
00163 }
00164 
00165 RecurrenceRule *Recurrence::defaultRRuleConst() const
00166 {
00167   if ( mRRules.isEmpty() ) {
00168     return 0;
00169   } else {
00170     return mRRules.first();
00171   }
00172 }
00173 
00174 void Recurrence::updated()
00175 {
00176   // recurrenceType() re-calculates the type if it's rMax
00177   mCachedType = rMax;
00178   for ( TQValueList<Observer*>::ConstIterator it = mObservers.begin();
00179         it != mObservers.end(); ++it ) {
00180     if ( (*it) ) (*it)->recurrenceUpdated( this );
00181   }
00182 }
00183 
00184 bool Recurrence::doesRecur() const
00185 {
00186   return !mRRules.isEmpty() || !mRDates.isEmpty() || !mRDateTimes.isEmpty();
00187 }
00188 
00189 ushort Recurrence::recurrenceType() const
00190 {
00191   if ( mCachedType == rMax ) {
00192     mCachedType = recurrenceType( defaultRRuleConst() );
00193   }
00194   return mCachedType;
00195 }
00196 
00197 ushort Recurrence::recurrenceType( const RecurrenceRule *rrule )
00198 {
00199   if ( !rrule ) return rNone;
00200   RecurrenceRule::PeriodType type = rrule->recurrenceType();
00201 
00202   // BYSETPOS, BYWEEKNUMBER and BYSECOND were not supported in old versions
00203   if ( !rrule->bySetPos().isEmpty() )
00204     return rOther;
00205   if ( !rrule->bySeconds().isEmpty() )
00206     return rOther;
00207   if ( !rrule->byWeekNumbers().isEmpty() )
00208     return rOther;
00209 
00210   // It wasn't possible to set BYMINUTES, BYHOUR etc. by the old code. So if
00211   // it's set, it's none of the old types
00212   if ( !rrule->byMinutes().isEmpty() )
00213     return rOther;
00214   if ( !rrule->byHours().isEmpty() )
00215     return rOther;
00216 
00217   // Possible combinations were:
00218   // BYDAY: with WEEKLY, MONTHLY, YEARLY
00219   // BYMONTHDAY: with MONTHLY, YEARLY
00220   // BYMONTH: with YEARLY
00221   // BYYEARDAY: with YEARLY
00222   if ( !rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly )
00223     return rOther;
00224   if ( !rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly )
00225     return rOther;
00226   if ( !rrule->byDays().isEmpty() ) {
00227     if ( type != RecurrenceRule::rYearly && type != RecurrenceRule::rMonthly &&
00228          type != RecurrenceRule::rWeekly )
00229       return rOther;
00230   }
00231 
00232   switch ( type ) {
00233     case RecurrenceRule::rNone:     return rNone;
00234     case RecurrenceRule::rMinutely: return rMinutely;
00235     case RecurrenceRule::rHourly:   return rHourly;
00236     case RecurrenceRule::rDaily:    return rDaily;
00237     case RecurrenceRule::rWeekly:   return rWeekly;
00238     case RecurrenceRule::rMonthly: {
00239         if ( rrule->byDays().isEmpty() ) return rMonthlyDay;
00240         else if ( rrule->byMonthDays().isEmpty() ) return rMonthlyPos;
00241         else return rOther; // both position and date specified
00242       }
00243     case RecurrenceRule::rYearly: {
00244         // Possible combinations:
00245         //   rYearlyMonth: [BYMONTH &] BYMONTHDAY
00246         //   rYearlyDay: BYYEARDAY
00247         //   rYearlyPos: [BYMONTH &] BYDAY
00248         if ( !rrule->byDays().isEmpty() ) {
00249           // can only by rYearlyPos
00250           if ( rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty() )
00251             return rYearlyPos;
00252           else return rOther;
00253         } else if ( !rrule->byYearDays().isEmpty() ) {
00254           // Can only be rYearlyDay
00255           if ( rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty() )
00256             return rYearlyDay;
00257           else return rOther;
00258         } else {
00259           return rYearlyMonth;
00260         }
00261         break;
00262       }
00263      default: return rOther;
00264   }
00265   return rOther;
00266 }
00267 
00268 bool Recurrence::recursOn(const TQDate &qd) const
00269 {
00270   TimeList tms;
00271   // First handle dates. Exrules override
00272   if ( mExDates.contains( qd ) ) return false;
00273   // For all-day events a matching exrule excludes the whole day
00274   // since exclusions take precedence over inclusions, we know it can't occur on that day.
00275   if ( doesFloat() ) {
00276     for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
00277       if ( (*rr)->recursOn( qd ) )
00278         return false;
00279     }
00280   }
00281 
00282   if ( mRDates.contains( qd ) ) return true;
00283 
00284   bool recurs = false;
00285 
00286   for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
00287     recurs = recurs || (*rr)->recursOn( qd );
00288   }
00289   // If we already know it recurs, no need to check the rdate list too.
00290   if ( !recurs ) {
00291     for ( DateTimeList::ConstIterator rit = mRDateTimes.begin();
00292           rit != mRDateTimes.end(); ++rit ) {
00293       if ( (*rit).date() == qd ) {
00294         recurs = true;
00295         break;
00296       }
00297     }
00298   }
00299   // If the event wouldn't recur at all, simply return false, don't check ex*
00300   if ( !recurs ) return false;
00301 
00302   // Check if there are any times for this day excluded, either by exdate or exrule:
00303   bool exon = false;
00304   for ( DateTimeList::ConstIterator exit = mExDateTimes.begin();
00305         exit != mExDateTimes.end(); ++exit ) {
00306     if ( (*exit).date() == qd ) {
00307       exon = true;
00308       break;
00309     }
00310   }
00311   if ( !doesFloat() ) {     // we have already checked floating times above
00312     for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
00313       exon = exon || (*rr)->recursOn( qd );
00314     }
00315   }
00316 
00317   if ( !exon ) {
00318     // Simple case, nothing on that day excluded, return the value from before
00319     return recurs;
00320   } else {
00321     // Harder part: I don't think there is any way other than to calculate the
00322     // whole list of items for that day.
00323     TimeList timesForDay( recurTimesOn( qd ) );
00324     return !timesForDay.isEmpty();
00325   }
00326 }
00327 
00328 bool Recurrence::recursAt( const TQDateTime &dt ) const
00329 {
00330   // if it's excluded anyway, don't bother to check if it recurs at all.
00331   if ( mExDateTimes.contains( dt )) return false;
00332   if ( mExDates.contains( dt.date() )) return false;
00333   for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
00334     if ( (*rr)->recursAt( dt ) ) return false;
00335   }
00336 
00337   // Check explicit recurrences, then rrules.
00338   bool occurs = ( startDateTime() == dt ) || mRDateTimes.contains( dt );
00339   if ( occurs )
00340     return true;
00341   for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
00342     if ( (*rr)->recursAt( dt ) ) return true;
00343   }
00344 
00345   return false;
00346 }
00347 
00351 TQDateTime Recurrence::endDateTime() const
00352 {
00353   DateTimeList dts;
00354   dts << startDateTime();
00355   if ( !mRDates.isEmpty() ) dts << TQDateTime( mRDates.last(), TQTime( 0, 0, 0 ) );
00356   if ( !mRDateTimes.isEmpty() ) dts << mRDateTimes.last();
00357   for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
00358     TQDateTime rl( (*rr)->endDt() );
00359     // if any of the rules is infinite, the whole recurrence is
00360     if ( !rl.isValid() ) return TQDateTime();
00361     dts << rl;
00362   }
00363   qSortUnique( dts );
00364   if ( dts.isEmpty() ) return TQDateTime();
00365   else return dts.last();
00366 }
00367 
00371 TQDate Recurrence::endDate() const
00372 {
00373   TQDateTime end( endDateTime() );
00374   if ( end.isValid() ) { return end.date(); }
00375   else return TQDate();
00376 }
00377 
00378 void Recurrence::setEndDate( const TQDate &date )
00379 {
00380   if ( doesFloat() )
00381     setEndDateTime( TQDateTime( date, TQTime( 23, 59, 59 ) ) );
00382   else
00383     setEndDateTime( TQDateTime( date, mStartDateTime.time() ) );
00384 }
00385 
00386 void Recurrence::setEndDateTime( const TQDateTime &dateTime )
00387 {
00388   if ( mRecurReadOnly ) return;
00389   RecurrenceRule *rrule = defaultRRule( true );
00390   if ( !rrule ) return;
00391   rrule->setEndDt( dateTime );
00392   updated();
00393 }
00394 
00395 int Recurrence::duration() const
00396 {
00397   RecurrenceRule *rrule = defaultRRuleConst();
00398   if ( rrule ) return rrule->duration();
00399   else return 0;
00400 }
00401 
00402 // int Recurrence::durationTo( const TQDate &/*date*/ ) const
00403 // {
00404 //   return 0;
00405 // }
00406 
00407 int Recurrence::durationTo( const TQDateTime &datetime ) const
00408 {
00409   // Emulate old behavior: This is just an interface to the first rule!
00410   RecurrenceRule *rrule = defaultRRuleConst();
00411   if ( !rrule ) return 0;
00412   else return rrule->durationTo( datetime );
00413 }
00414 
00415 void Recurrence::setDuration( int duration )
00416 {
00417   if ( mRecurReadOnly ) return;
00418   RecurrenceRule *rrule = defaultRRule( true );
00419   if ( !rrule ) return;
00420   rrule->setDuration( duration );
00421   updated();
00422 }
00423 
00424 void Recurrence::unsetRecurs()
00425 {
00426   if ( mRecurReadOnly ) return;
00427   mRRules.clearAll();
00428   updated();
00429 }
00430 
00431 void Recurrence::clear()
00432 {
00433   if ( mRecurReadOnly ) return;
00434   mRRules.clearAll();
00435   mExRules.clearAll();
00436   mRDates.clear();
00437   mRDateTimes.clear();
00438   mExDates.clear();
00439   mExDateTimes.clear();
00440   mCachedType = rMax;
00441   updated();
00442 }
00443 
00444 void Recurrence::setStartDateTime( const TQDateTime &start )
00445 {
00446   if ( mRecurReadOnly ) return;
00447   mStartDateTime = start;
00448   setFloats( false );   // set all RRULEs and EXRULEs
00449 
00450   for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
00451     (*rr)->setStartDt( start );
00452   }
00453   for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
00454     (*rr)->setStartDt( start );
00455   }
00456   updated();
00457 }
00458 
00459 void Recurrence::setStartDate( const TQDate &start )
00460 {
00461   setStartDateTime( TQDateTime( start, TQTime(0,0,0) ) );
00462   setFloats( true );
00463 }
00464 
00465 int Recurrence::frequency() const
00466 {
00467   RecurrenceRule *rrule = defaultRRuleConst();
00468   if ( rrule ) return rrule->frequency();
00469   else return 0;
00470 }
00471 
00472 // Emulate the old behaviour. Make this methods just an interface to the
00473 // first rrule
00474 void Recurrence::setFrequency( int freq )
00475 {
00476   if ( mRecurReadOnly || freq <= 0 ) return;
00477   RecurrenceRule *rrule = defaultRRule( true );
00478   if ( rrule )
00479     rrule->setFrequency( freq );
00480   updated();
00481 }
00482 
00483 
00484 // WEEKLY
00485 
00486 int Recurrence::weekStart() const
00487 {
00488   RecurrenceRule *rrule = defaultRRuleConst();
00489   if ( rrule ) return rrule->weekStart();
00490   else return 1;
00491 }
00492 
00493 // Emulate the old behavior
00494 TQBitArray Recurrence::days() const
00495 {
00496   TQBitArray days( 7 );
00497   days.fill( 0 );
00498   RecurrenceRule *rrule = defaultRRuleConst();
00499   if ( rrule ) {
00500     TQValueList<RecurrenceRule::WDayPos> bydays = rrule->byDays();
00501     for ( TQValueListConstIterator<RecurrenceRule::WDayPos> it = bydays.begin();
00502           it != bydays.end(); ++it ) {
00503       if ( (*it).pos() == 0 ) {
00504         days.setBit( (*it).day() - 1 );
00505       }
00506     }
00507   }
00508   return days;
00509 }
00510 
00511 
00512 // MONTHLY
00513 
00514 // Emulate the old behavior
00515 TQValueList<int> Recurrence::monthDays() const
00516 {
00517   RecurrenceRule *rrule = defaultRRuleConst();
00518   if ( rrule ) return rrule->byMonthDays();
00519   else return TQValueList<int>();
00520 }
00521 
00522 // Emulate the old behavior
00523 TQValueList<RecurrenceRule::WDayPos> Recurrence::monthPositions() const
00524 {
00525   RecurrenceRule *rrule = defaultRRuleConst();
00526   if ( rrule ) return rrule->byDays();
00527   else return TQValueList<RecurrenceRule::WDayPos>();
00528 }
00529 
00530 
00531 // YEARLY
00532 
00533 TQValueList<int> Recurrence::yearDays() const
00534 {
00535   RecurrenceRule *rrule = defaultRRuleConst();
00536   if ( rrule ) return rrule->byYearDays();
00537   else return TQValueList<int>();
00538 }
00539 
00540 TQValueList<int> Recurrence::yearDates() const
00541 {
00542   return monthDays();
00543 }
00544 
00545 TQValueList<int> Recurrence::yearMonths() const
00546 {
00547   RecurrenceRule *rrule = defaultRRuleConst();
00548   if ( rrule ) return rrule->byMonths();
00549   else return TQValueList<int>();
00550 }
00551 
00552 TQValueList<RecurrenceRule::WDayPos> Recurrence::yearPositions() const
00553 {
00554   return monthPositions();
00555 }
00556 
00557 
00558 
00559 RecurrenceRule *Recurrence::setNewRecurrenceType( RecurrenceRule::PeriodType type, int freq )
00560 {
00561   if ( mRecurReadOnly || freq <= 0 ) return 0;
00562   mRRules.clearAll();
00563   updated();
00564   RecurrenceRule *rrule = defaultRRule( true );
00565   if ( !rrule ) return 0;
00566   rrule->setRecurrenceType( type );
00567   rrule->setFrequency( freq );
00568   rrule->setDuration( -1 );
00569   return rrule;
00570 }
00571 
00572 void Recurrence::setMinutely( int _rFreq )
00573 {
00574   if ( setNewRecurrenceType( RecurrenceRule::rMinutely, _rFreq ) )
00575     updated();
00576 }
00577 
00578 void Recurrence::setHourly( int _rFreq )
00579 {
00580   if ( setNewRecurrenceType( RecurrenceRule::rHourly, _rFreq ) )
00581     updated();
00582 }
00583 
00584 void Recurrence::setDaily( int _rFreq )
00585 {
00586   if ( setNewRecurrenceType( RecurrenceRule::rDaily, _rFreq ) )
00587     updated();
00588 }
00589 
00590 void Recurrence::setWeekly( int freq, int weekStart )
00591 {
00592   RecurrenceRule *rrule = setNewRecurrenceType( RecurrenceRule::rWeekly, freq );
00593   if ( !rrule ) return;
00594   rrule->setWeekStart( weekStart );
00595   updated();
00596 }
00597 
00598 void Recurrence::setWeekly( int freq, const TQBitArray &days, int weekStart )
00599 {
00600   setWeekly( freq, weekStart );
00601   addMonthlyPos( 0, days );
00602 }
00603 
00604 void Recurrence::addWeeklyDays( const TQBitArray &days )
00605 {
00606   addMonthlyPos( 0, days );
00607 }
00608 
00609 void Recurrence::setMonthly( int freq )
00610 {
00611   if ( setNewRecurrenceType( RecurrenceRule::rMonthly, freq ) )
00612     updated();
00613 }
00614 
00615 void Recurrence::addMonthlyPos( short pos, const TQBitArray &days )
00616 {
00617   // Allow 53 for yearly!
00618   if ( mRecurReadOnly || pos > 53 || pos < -53 ) return;
00619   RecurrenceRule *rrule = defaultRRule( false );
00620   if ( !rrule ) return;
00621   bool changed = false;
00622   TQValueList<RecurrenceRule::WDayPos> positions = rrule->byDays();
00623 
00624   for ( int i = 0; i < 7; ++i ) {
00625     if ( days.testBit(i) ) {
00626       RecurrenceRule::WDayPos p( pos, i + 1 );
00627       if ( !positions.contains( p ) ) {
00628         changed = true;
00629         positions.append( p );
00630       }
00631     }
00632   }
00633   if ( changed ) {
00634     rrule->setByDays( positions );
00635     updated();
00636   }
00637 }
00638 
00639 
00640 void Recurrence::addMonthlyPos( short pos, ushort day )
00641 {
00642   // Allow 53 for yearly!
00643   if ( mRecurReadOnly || pos > 53 || pos < -53 ) return;
00644   RecurrenceRule *rrule = defaultRRule( false );
00645   if ( !rrule ) return;
00646   TQValueList<RecurrenceRule::WDayPos> positions = rrule->byDays();
00647 
00648   RecurrenceRule::WDayPos p( pos, day );
00649   if ( !positions.contains( p ) ) {
00650     positions.append( p );
00651     rrule->setByDays( positions );
00652     updated();
00653   }
00654 }
00655 
00656 
00657 void Recurrence::addMonthlyDate( short day )
00658 {
00659   if ( mRecurReadOnly || day > 31 || day < -31 ) return;
00660   RecurrenceRule *rrule = defaultRRule( true );
00661   if ( !rrule ) return;
00662 
00663   TQValueList<int> monthDays = rrule->byMonthDays();
00664   if ( !monthDays.contains( day ) ) {
00665     monthDays.append( day );
00666     rrule->setByMonthDays( monthDays );
00667     updated();
00668   }
00669 }
00670 
00671 void Recurrence::setYearly( int freq )
00672 {
00673   if ( setNewRecurrenceType( RecurrenceRule::rYearly, freq ) )
00674     updated();
00675 }
00676 
00677 
00678 // Daynumber within year
00679 void Recurrence::addYearlyDay( int day )
00680 {
00681   RecurrenceRule *rrule = defaultRRule( false ); // It must already exist!
00682   if ( !rrule ) return;
00683 
00684   TQValueList<int> days = rrule->byYearDays();
00685   if ( !days.contains( day ) ) {
00686     days << day;
00687     rrule->setByYearDays( days );
00688     updated();
00689   }
00690 }
00691 
00692 // day part of date within year
00693 void Recurrence::addYearlyDate( int day )
00694 {
00695   addMonthlyDate( day );
00696 }
00697 
00698 // day part of date within year, given as position (n-th weekday)
00699 void Recurrence::addYearlyPos( short pos, const TQBitArray &days )
00700 {
00701   addMonthlyPos( pos, days );
00702 }
00703 
00704 
00705 // month part of date within year
00706 void Recurrence::addYearlyMonth( short month )
00707 {
00708   if ( mRecurReadOnly || month < 1 || month > 12 ) return;
00709   RecurrenceRule *rrule = defaultRRule( false );
00710   if ( !rrule ) return;
00711 
00712   TQValueList<int> months = rrule->byMonths();
00713   if ( !months.contains(month) ) {
00714     months << month;
00715     rrule->setByMonths( months );
00716     updated();
00717   }
00718 }
00719 
00720 
00721 TimeList Recurrence::recurTimesOn( const TQDate &date ) const
00722 {
00723   TimeList times;
00724   // The whole day is excepted
00725   if ( mExDates.contains( date ) ) return times;
00726   // EXRULE takes precedence over RDATE entries, so for floating events,
00727   // a matching excule also excludes the whole day automatically
00728   if ( doesFloat() ) {
00729     for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
00730       if ( (*rr)->recursOn( date ) )
00731         return times;
00732     }
00733   }
00734 
00735   if ( startDate() == date ) times << startDateTime().time();
00736   bool foundDate = false;
00737   for ( DateTimeList::ConstIterator it = mRDateTimes.begin();
00738         it != mRDateTimes.end(); ++it ) {
00739     if ( (*it).date() == date ) {
00740       times << (*it).time();
00741       foundDate = true;
00742     } else if (foundDate) break; // <= Assume that the rdatetime list is sorted
00743   }
00744   for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
00745     times += (*rr)->recurTimesOn( date );
00746   }
00747   qSortUnique( times );
00748 
00749   foundDate = false;
00750   TimeList extimes;
00751   for ( DateTimeList::ConstIterator it = mExDateTimes.begin();
00752         it != mExDateTimes.end(); ++it ) {
00753     if ( (*it).date() == date ) {
00754       extimes << (*it).time();
00755       foundDate = true;
00756     } else if (foundDate) break;
00757   }
00758   if ( !doesFloat() ) {     // we have already checked floating times above
00759     for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
00760       extimes += (*rr)->recurTimesOn( date );
00761     }
00762   }
00763   qSortUnique( extimes );
00764 
00765   for ( TimeList::Iterator it = extimes.begin(); it != extimes.end(); ++it ) {
00766     times.remove( (*it) );
00767   }
00768   return times;
00769 }
00770 
00771 DateTimeList Recurrence::timesInInterval( const TQDateTime &start, const TQDateTime &end ) const
00772 {
00773   int i, count;
00774   DateTimeList times;
00775   for ( i = 0, count = mRRules.count();  i < count;  ++i ) {
00776     times += mRRules[i]->timesInInterval( start, end );
00777   }
00778 
00779   // add rdatetimes that fit in the interval
00780   for ( i = 0, count = mRDateTimes.count();  i < count;  ++i ) {
00781     if ( mRDateTimes[i] >= start && mRDateTimes[i] <= end ) {
00782       times += mRDateTimes[i];
00783     }
00784   }
00785 
00786   // add rdates that fit in the interval
00787   TQDateTime qdt( mStartDateTime );
00788   for ( i = 0, count = mRDates.count();  i < count;  ++i ) {
00789     qdt.setDate( mRDates[i] );
00790     if ( qdt >= start && qdt <= end ) {
00791       times += qdt;
00792     }
00793   }
00794 
00795   // Recurrence::timesInInterval(...) doesn't explicitly add mStartDateTime to the list
00796   // of times to be returned. It calls mRRules[i]->timesInInterval(...) which include
00797   // mStartDateTime.
00798   // So, If we have rdates/rdatetimes but don't have any rrule we must explicitly
00799   // add mStartDateTime to the list, otherwise we won't see the first occurrence.
00800   if ( ( !mRDates.isEmpty() || !mRDateTimes.isEmpty() ) &&
00801        mRRules.isEmpty() &&
00802        start <= mStartDateTime &&
00803        end >= mStartDateTime ) {
00804     times += mStartDateTime;
00805   }
00806 
00807   qSortUnique( times );
00808 
00809   // Remove excluded times
00810   int idt = 0;
00811   int enddt = times.count();
00812   for ( i = 0, count = mExDates.count();  i < count && idt < enddt;  ++i ) {
00813     while ( idt < enddt && times[idt].date() < mExDates[i] ) ++idt;
00814     while ( idt < enddt && times[idt].date() == mExDates[i] ) {
00815       times.remove( times.at( idt ) );
00816       --enddt;
00817     }
00818   }
00819   DateTimeList extimes;
00820   for ( i = 0, count = mExRules.count();  i < count;  ++i ) {
00821     extimes += mExRules[i]->timesInInterval( start, end );
00822   }
00823   extimes += mExDateTimes;
00824   qSortUnique( extimes );
00825 
00826   int st = 0;
00827   for ( i = 0, count = extimes.count();  i < count;  ++i ) {
00828     int j = removeSorted( times, extimes[i], st );
00829     if ( j >= 0 ) {
00830       st = j;
00831     }
00832   }
00833 
00834   return times;
00835 }
00836 
00837 TQDateTime Recurrence::getNextDateTime( const TQDateTime &preDateTime ) const
00838 {
00839   TQDateTime nextDT = preDateTime;
00840   // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
00841   // the exrule is identical to the rrule). If an occurrence is found, break
00842   // out of the loop by returning that QDateTime
00843 // TODO_Recurrence: Is a loop counter of 1000 really okay? I mean for secondly
00844 // recurrence, an exdate might exclude more than 1000 intervals!
00845   int loop = 0;
00846   while ( loop < 1000 ) {
00847     // Outline of the algo:
00848     //   1) Find the next date/time after preDateTime when the event could recur
00849     //     1.0) Add the start date if it's after preDateTime
00850     //     1.1) Use the next occurrence from the explicit RDATE lists
00851     //     1.2) Add the next recurrence for each of the RRULEs
00852     //   2) Take the earliest recurrence of these = TQDateTime nextDT
00853     //   3) If that date/time is not excluded, either explicitly by an EXDATE or
00854     //      by an EXRULE, return nextDT as the next date/time of the recurrence
00855     //   4) If it's excluded, start all at 1), but starting at nextDT (instead
00856     //      of preDateTime). Loop at most 1000 times.
00857     ++loop;
00858     // First, get the next recurrence from the RDate lists
00859     DateTimeList dates;
00860     if ( nextDT < startDateTime() ) {
00861       dates << startDateTime();
00862     }
00863 
00864     int end;
00865     // Assume that the rdatetime list is sorted
00866     int i = findGT( mRDateTimes, nextDT, 0 );
00867     if ( i >= 0 ) {
00868       dates << mRDateTimes[i];
00869     }
00870 
00871     TQDateTime qdt( startDateTime() );
00872     for ( i = 0, end = mRDates.count();  i < end;  ++i ) {
00873       qdt.setDate( mRDates[i] );
00874       if ( qdt > nextDT ) {
00875         dates << qdt;
00876         break;
00877       }
00878     }
00879 
00880     // Add the next occurrences from all RRULEs.
00881     for ( i = 0, end = mRRules.count();  i < end;  ++i ) {
00882       TQDateTime dt = mRRules[i]->getNextDate( nextDT );
00883       if ( dt.isValid() ) {
00884         dates << dt;
00885       }
00886     }
00887 
00888     // Take the first of these (all others can't be used later on)
00889     qSortUnique( dates );
00890     if ( dates.isEmpty() ) {
00891       return TQDateTime();
00892     }
00893     nextDT = dates.first();
00894 
00895     // Check if that date/time is excluded explicitly or by an exrule:
00896     if ( !containsSorted( mExDates, nextDT.date() ) &&
00897          !containsSorted( mExDateTimes, nextDT ) ) {
00898       bool allowed = true;
00899       for ( i = 0, end = mExRules.count();  i < end;  ++i ) {
00900         allowed = allowed && !( mExRules[i]->recursAt( nextDT ) );
00901       }
00902       if ( allowed ) {
00903         return nextDT;
00904       }
00905     }
00906   }
00907 
00908   // Couldn't find a valid occurrences in 1000 loops, something is wrong!
00909   return TQDateTime();
00910 }
00911 
00912 TQDateTime Recurrence::getPreviousDateTime( const TQDateTime &afterDateTime ) const
00913 {
00914   TQDateTime prevDT = afterDateTime;
00915   // prevent infinite loops, e.g. when an exrule extinguishes an rrule (e.g.
00916   // the exrule is identical to the rrule). If an occurrence is found, break
00917   // out of the loop by returning that QDateTime
00918   int loop = 0;
00919   while ( loop < 1000 ) {
00920     // Outline of the algo:
00921     //   1) Find the next date/time after preDateTime when the event could recur
00922     //     1.1) Use the next occurrence from the explicit RDATE lists
00923     //     1.2) Add the next recurrence for each of the RRULEs
00924     //   2) Take the earliest recurrence of these = TQDateTime nextDT
00925     //   3) If that date/time is not excluded, either explicitly by an EXDATE or
00926     //      by an EXRULE, return nextDT as the next date/time of the recurrence
00927     //   4) If it's excluded, start all at 1), but starting at nextDT (instead
00928     //      of preDateTime). Loop at most 1000 times.
00929     ++loop;
00930     // First, get the next recurrence from the RDate lists
00931     DateTimeList dates;
00932     if ( prevDT > startDateTime() ) {
00933       dates << startDateTime();
00934     }
00935 
00936     int i = findLT( mRDateTimes, prevDT, 0 );
00937     if ( i >= 0 ) {
00938       dates << mRDateTimes[i];
00939     }
00940 
00941     TQDateTime qdt( startDateTime() );
00942     for ( i = mRDates.count();  --i >= 0; ) {
00943       qdt.setDate( mRDates[i] );
00944       if ( qdt < prevDT ) {
00945         dates << qdt;
00946         break;
00947       }
00948     }
00949 
00950     // Add the previous occurrences from all RRULEs.
00951     int end;
00952     for ( i = 0, end = mRRules.count();  i < end;  ++i ) {
00953       TQDateTime dt = mRRules[i]->getPreviousDate( prevDT );
00954       if ( dt.isValid() ) {
00955         dates << dt;
00956       }
00957     }
00958 
00959     // Take the last of these (all others can't be used later on)
00960     qSortUnique( dates );
00961     if ( dates.isEmpty() ) {
00962       return TQDateTime();
00963     }
00964     prevDT = dates.last();
00965 
00966     // Check if that date/time is excluded explicitly or by an exrule:
00967     if ( !containsSorted( mExDates, prevDT.date() ) &&
00968          !containsSorted( mExDateTimes, prevDT ) ) {
00969       bool allowed = true;
00970       for ( i = 0, end = mExRules.count();  i < end;  ++i ) {
00971         allowed = allowed && !( mExRules[i]->recursAt( prevDT ) );
00972       }
00973       if ( allowed ) {
00974         return prevDT;
00975       }
00976     }
00977   }
00978 
00979   // Couldn't find a valid occurrences in 1000 loops, something is wrong!
00980   return TQDateTime();
00981 }
00982 
00983 
00984 /***************************** PROTECTED FUNCTIONS ***************************/
00985 
00986 
00987 RecurrenceRule::List Recurrence::rRules() const
00988 {
00989   return mRRules;
00990 }
00991 
00992 void Recurrence::addRRule( RecurrenceRule *rrule )
00993 {
00994   if ( mRecurReadOnly || !rrule ) return;
00995   rrule->setFloats( mFloating );
00996   mRRules.append( rrule );
00997   rrule->addObserver( this );
00998   updated();
00999 }
01000 
01001 void Recurrence::removeRRule( RecurrenceRule *rrule )
01002 {
01003   if (mRecurReadOnly) return;
01004   mRRules.remove( rrule );
01005   rrule->removeObserver( this );
01006   updated();
01007 }
01008 
01009 RecurrenceRule::List Recurrence::exRules() const
01010 {
01011   return mExRules;
01012 }
01013 
01014 void Recurrence::addExRule( RecurrenceRule *exrule )
01015 {
01016   if ( mRecurReadOnly || !exrule ) return;
01017   exrule->setFloats( mFloating );
01018   mExRules.append( exrule );
01019   exrule->addObserver( this );
01020   updated();
01021 }
01022 
01023 void Recurrence::removeExRule( RecurrenceRule *exrule )
01024 {
01025   if (mRecurReadOnly) return;
01026   mExRules.remove( exrule );
01027   exrule->removeObserver( this );
01028   updated();
01029 }
01030 
01031 
01032 DateTimeList Recurrence::rDateTimes() const
01033 {
01034   return mRDateTimes;
01035 }
01036 
01037 void Recurrence::setRDateTimes( const DateTimeList &rdates )
01038 {
01039   if ( mRecurReadOnly ) return;
01040   mRDateTimes = rdates;
01041   qSortUnique( mRDateTimes );
01042   updated();
01043 }
01044 
01045 void Recurrence::addRDateTime( const TQDateTime &rdate )
01046 {
01047   if ( mRecurReadOnly ) return;
01048   mRDateTimes.append( rdate );
01049   qSortUnique( mRDateTimes );
01050   updated();
01051 }
01052 
01053 
01054 DateList Recurrence::rDates() const
01055 {
01056   return mRDates;
01057 }
01058 
01059 void Recurrence::setRDates( const DateList &rdates )
01060 {
01061   if ( mRecurReadOnly ) return;
01062   mRDates = rdates;
01063   qSortUnique( mRDates );
01064   updated();
01065 }
01066 
01067 void Recurrence::addRDate( const TQDate &rdate )
01068 {
01069   if ( mRecurReadOnly ) return;
01070   mRDates.append( rdate );
01071   qSortUnique( mRDates );
01072   updated();
01073 }
01074 
01075 
01076 DateTimeList Recurrence::exDateTimes() const
01077 {
01078   return mExDateTimes;
01079 }
01080 
01081 void Recurrence::setExDateTimes( const DateTimeList &exdates )
01082 {
01083   if ( mRecurReadOnly ) return;
01084   mExDateTimes = exdates;
01085   qSortUnique( mExDateTimes );
01086 }
01087 
01088 void Recurrence::addExDateTime( const TQDateTime &exdate )
01089 {
01090   if ( mRecurReadOnly ) return;
01091   mExDateTimes.append( exdate );
01092   qSortUnique( mExDateTimes );
01093   updated();
01094 }
01095 
01096 
01097 DateList Recurrence::exDates() const
01098 {
01099   return mExDates;
01100 }
01101 
01102 void Recurrence::setExDates( const DateList &exdates )
01103 {
01104   if ( mRecurReadOnly ) return;
01105   mExDates = exdates;
01106   qSortUnique( mExDates );
01107   updated();
01108 }
01109 
01110 void Recurrence::addExDate( const TQDate &exdate )
01111 {
01112   if ( mRecurReadOnly ) return;
01113   mExDates.append( exdate );
01114   qSortUnique( mExDates );
01115   updated();
01116 }
01117 
01118 void Recurrence::recurrenceChanged( RecurrenceRule * )
01119 {
01120   updated();
01121 }
01122 
01123 
01124 // %%%%%%%%%%%%%%%%%% end:Recurrencerule %%%%%%%%%%%%%%%%%%
01125 
01126 void Recurrence::dump() const
01127 {
01128   kdDebug(5800) << "Recurrence::dump():" << endl;
01129 
01130   kdDebug(5800) << "  -) " << mRRules.count() << " RRULEs: " << endl;
01131   for ( RecurrenceRule::List::ConstIterator rr = mRRules.begin(); rr != mRRules.end(); ++rr ) {
01132     kdDebug(5800) << "    -) RecurrenceRule : " << endl;
01133     (*rr)->dump();
01134   }
01135   kdDebug(5800) << "  -) " << mExRules.count() << " EXRULEs: " << endl;
01136   for ( RecurrenceRule::List::ConstIterator rr = mExRules.begin(); rr != mExRules.end(); ++rr ) {
01137     kdDebug(5800) << "    -) ExceptionRule : " << endl;
01138     (*rr)->dump();
01139   }
01140 
01141 
01142   kdDebug(5800) << endl << "  -) " << mRDates.count() << " Recurrence Dates: " << endl;
01143   for ( DateList::ConstIterator it = mRDates.begin(); it != mRDates.end(); ++it ) {
01144     kdDebug(5800) << "     " << (*it) << endl;
01145   }
01146   kdDebug(5800) << endl << "  -) " << mRDateTimes.count() << " Recurrence Date/Times: " << endl;
01147   for ( DateTimeList::ConstIterator it = mRDateTimes.begin(); it != mRDateTimes.end(); ++it ) {
01148     kdDebug(5800) << "     " << (*it) << endl;
01149   }
01150   kdDebug(5800) << endl << "  -) " << mExDates.count() << " Exceptions Dates: " << endl;
01151   for ( DateList::ConstIterator it = mExDates.begin(); it != mExDates.end(); ++it ) {
01152     kdDebug(5800) << "     " << (*it) << endl;
01153   }
01154   kdDebug(5800) << endl << "  -) " << mExDateTimes.count() << " Exception Date/Times: " << endl;
01155   for ( DateTimeList::ConstIterator it = mExDateTimes.begin(); it != mExDateTimes.end(); ++it ) {
01156     kdDebug(5800) << "     " << (*it) << endl;
01157   }
01158 }