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 <tdeglobal.h> 00029 #include <tdelocale.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 TQDateTime 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 TQDateTime 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 }