libkcal

recurrence.cpp

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