libkcal Library API Documentation

recurrence.cpp

00001 /*
00002     This file is part of libkcal.
00003     Copyright (c) 1998 Preston Brown
00004     Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (c) 2002 David Jarvie <software@astrojar.org.uk>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to
00019     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00020     Boston, MA 02111-1307, USA.
00021 */
00022 
00023 #include <limits.h>
00024 
00025 #include <kdebug.h>
00026 #include <kglobal.h>
00027 #include <klocale.h>
00028 
00029 #include "incidence.h"
00030 
00031 #include "recurrence.h"
00032 
00033 using namespace KCal;
00034 
00035 Recurrence::Feb29Type Recurrence::mFeb29YearlyDefaultType = Recurrence::rMar1;
00036 
00037 
00038 Recurrence::Recurrence(Incidence *parent, int compatVersion)
00039 : recurs(rNone),   // by default, it's not a recurring event
00040   rWeekStart(1),   // default is Monday
00041   rDays(7),
00042   mUseCachedEndDT(false),
00043   mFloats(parent ? parent->doesFloat() : false),
00044   mRecurReadOnly(false),
00045   mFeb29YearlyType(mFeb29YearlyDefaultType),
00046   mCompatVersion(compatVersion ? compatVersion : INT_MAX),
00047   mCompatRecurs(rNone),
00048   mCompatDuration(0),
00049   mParent(parent)
00050 {
00051   rMonthDays.setAutoDelete( true );
00052   rMonthPositions.setAutoDelete( true );
00053   rYearNums.setAutoDelete( true );
00054 }
00055 
00056 Recurrence::Recurrence(const Recurrence &r, Incidence *parent)
00057 : recurs(r.recurs),
00058   rWeekStart(r.rWeekStart),
00059   rDays(r.rDays.copy()),
00060   rFreq(r.rFreq),
00061   rDuration(r.rDuration),
00062   rEndDateTime(r.rEndDateTime),
00063   mCachedEndDT(r.mCachedEndDT),
00064   mUseCachedEndDT(r.mUseCachedEndDT),
00065   mRecurStart(r.mRecurStart),
00066   mFloats(r.mFloats),
00067   mRecurReadOnly(r.mRecurReadOnly),
00068   mFeb29YearlyType(r.mFeb29YearlyType),
00069   mCompatVersion(r.mCompatVersion),
00070   mCompatRecurs(r.mCompatRecurs),
00071   mCompatDuration(r.mCompatDuration),
00072   mParent(parent)
00073 {
00074   for (QPtrListIterator<rMonthPos> mp(r.rMonthPositions);  mp.current();  ++mp) {
00075     rMonthPos *tmp = new rMonthPos;
00076     tmp->rPos     = mp.current()->rPos;
00077     tmp->negative = mp.current()->negative;
00078     tmp->rDays    = mp.current()->rDays.copy();
00079     rMonthPositions.append(tmp);
00080   }
00081   for (QPtrListIterator<int> md(r.rMonthDays);  md.current();  ++md) {
00082     int *tmp = new int;
00083     *tmp = *md.current();
00084     rMonthDays.append(tmp);
00085   }
00086   for (QPtrListIterator<int> yn(r.rYearNums);  yn.current();  ++yn) {
00087     int *tmp = new int;
00088     *tmp = *yn.current();
00089     rYearNums.append(tmp);
00090   }
00091   rMonthDays.setAutoDelete( true );
00092   rMonthPositions.setAutoDelete( true );
00093   rYearNums.setAutoDelete( true );
00094 }
00095 
00096 Recurrence::~Recurrence()
00097 {
00098 }
00099 
00100 
00101 bool Recurrence::operator==( const Recurrence& r2 ) const
00102 {
00103   if ( recurs == rNone  &&  r2.recurs == rNone )
00104     return true;
00105   if ( recurs != r2.recurs
00106   ||   rFreq != r2.rFreq
00107   ||   rDuration != r2.rDuration
00108   ||   !rDuration && rEndDateTime != r2.rEndDateTime
00109   ||   mRecurStart != r2.mRecurStart
00110   ||   mFloats != r2.mFloats
00111   ||   mRecurReadOnly != r2.mRecurReadOnly )
00112     return false;
00113   // no need to compare mCompat* and mParent
00114   // OK to compare the pointers
00115   switch ( recurs )
00116   {
00117     case rWeekly:
00118       return rDays == r2.rDays
00119       &&     rWeekStart == r2.rWeekStart;
00120     case rMonthlyPos:
00121       return rMonthPositions == r2.rMonthPositions;
00122     case rMonthlyDay:
00123       return rMonthDays == r2.rMonthDays;
00124     case rYearlyPos:
00125       return rYearNums == r2.rYearNums
00126       &&     rMonthPositions == r2.rMonthPositions;
00127     case rYearlyMonth:
00128       return rYearNums == r2.rYearNums
00129       &&     rMonthDays == r2.rMonthDays
00130       &&     mFeb29YearlyType == r2.mFeb29YearlyType;
00131     case rYearlyDay:
00132       return rYearNums == r2.rYearNums;
00133     case rNone:
00134     case rMinutely:
00135     case rHourly:
00136     case rDaily:
00137     default:
00138       return true;
00139   }
00140 }
00141 
00142 
00143 void Recurrence::setCompatVersion(int version)
00144 {
00145   mCompatVersion = version ? version : INT_MAX;
00146   mUseCachedEndDT = false;
00147 }
00148 
00149 ushort Recurrence::doesRecur() const
00150 {
00151   return recurs;
00152 }
00153 
00154 bool Recurrence::recursOnPure(const QDate &qd) const
00155 {
00156   switch(recurs) {
00157     case rMinutely:
00158       return recursSecondly(qd, rFreq*60);
00159     case rHourly:
00160       return recursSecondly(qd, rFreq*3600);
00161     case rDaily:
00162       return recursDaily(qd);
00163     case rWeekly:
00164       return recursWeekly(qd);
00165     case rMonthlyPos:
00166     case rMonthlyDay:
00167       return recursMonthly(qd);
00168     case rYearlyMonth:
00169       return recursYearlyByMonth(qd);
00170     case rYearlyDay:
00171       return recursYearlyByDay(qd);
00172     case rYearlyPos:
00173       return recursYearlyByPos(qd);
00174     default:
00175       // catch-all.  Should never get here.
00176       kdError(5800) << "Control should never reach here in recursOnPure()!" << endl;
00177     case rNone:
00178       return false;
00179   } // case
00180 }
00181 
00182 bool Recurrence::recursAtPure(const QDateTime &dt) const
00183 {
00184   switch(recurs) {
00185   case rMinutely:
00186     return recursMinutelyAt(dt, rFreq);
00187   case rHourly:
00188     return recursMinutelyAt(dt, rFreq*60);
00189   default:
00190     if (dt.time() != mRecurStart.time())
00191       return false;
00192     switch(recurs) {
00193       case rDaily:
00194         return recursDaily(dt.date());
00195       case rWeekly:
00196         return recursWeekly(dt.date());
00197       case rMonthlyPos:
00198       case rMonthlyDay:
00199         return recursMonthly(dt.date());
00200       case rYearlyMonth:
00201         return recursYearlyByMonth(dt.date());
00202       case rYearlyDay:
00203         return recursYearlyByDay(dt.date());
00204       case rYearlyPos:
00205         return recursYearlyByPos(dt.date());
00206       default:
00207         // catch-all.  Should never get here.
00208         kdError(5800) << "Control should never reach here in recursAtPure()!" << endl;
00209       case rNone:
00210         return false;
00211     }
00212   } // case
00213 }
00214 
00215 QDate Recurrence::endDate(bool *result) const
00216 {
00217   return endDateTime(result).date();
00218 }
00219 
00220 QDateTime Recurrence::endDateTime(bool *result) const
00221 {
00222   int count = 0;
00223   if (result)
00224     *result = true;
00225   QDate end;
00226   if (recurs != rNone) {
00227     if (rDuration < 0)
00228       return QDateTime();    // infinite recurrence
00229     if (rDuration == 0)
00230       return rEndDateTime;
00231 
00232     // The end date is determined by the recurrence count
00233     if (mUseCachedEndDT) {
00234       if (result && !mCachedEndDT.isValid())
00235         *result = false;     // error - there is no recurrence
00236       return mCachedEndDT;   // avoid potentially long calculation
00237     }
00238 
00239     mUseCachedEndDT = true;
00240     switch (recurs)
00241     {
00242     case rMinutely:
00243       mCachedEndDT = mRecurStart.addSecs((rDuration-1)*rFreq*60);
00244       return mCachedEndDT;
00245     case rHourly:
00246       mCachedEndDT = mRecurStart.addSecs((rDuration-1)*rFreq*3600);
00247       return mCachedEndDT;
00248     case rDaily:
00249       mCachedEndDT = mRecurStart.addDays((rDuration-1)*rFreq);
00250       return mCachedEndDT;
00251 
00252     case rWeekly:
00253       count = weeklyCalc(END_DATE_AND_COUNT, end);
00254       break;
00255     case rMonthlyPos:
00256     case rMonthlyDay:
00257       count = monthlyCalc(END_DATE_AND_COUNT, end);
00258       break;
00259     case rYearlyMonth:
00260       count = yearlyMonthCalc(END_DATE_AND_COUNT, end);
00261       break;
00262     case rYearlyDay:
00263       count = yearlyDayCalc(END_DATE_AND_COUNT, end);
00264       break;
00265     case rYearlyPos:
00266       count = yearlyPosCalc(END_DATE_AND_COUNT, end);
00267       break;
00268     default:
00269       // catch-all.  Should never get here.
00270       kdError(5800) << "Control should never reach here in endDate()!" << endl;
00271       mUseCachedEndDT = false;
00272       break;
00273     }
00274   }
00275   if (!count) {
00276     if (result)
00277       *result = false;
00278     mCachedEndDT = QDateTime();   // error - there is no recurrence
00279   }
00280   else
00281     mCachedEndDT = QDateTime(end, mRecurStart.time());
00282   return mCachedEndDT;
00283 }
00284 
00285 int Recurrence::durationTo(const QDate &date) const
00286 {
00287   QDate d = date;
00288   return recurCalc(COUNT_TO_DATE, d);
00289 }
00290 
00291 int Recurrence::durationTo(const QDateTime &datetime) const
00292 {
00293   QDateTime dt = datetime;
00294   return recurCalc(COUNT_TO_DATE, dt);
00295 }
00296 
00297 void Recurrence::unsetRecurs()
00298 {
00299   if (mRecurReadOnly) return;
00300   recurs = rNone;
00301   rMonthPositions.clear();
00302   rMonthDays.clear();
00303   rYearNums.clear();
00304   mUseCachedEndDT = false;
00305 }
00306 
00307 void Recurrence::setRecurStart(const QDateTime &start)
00308 {
00309   mRecurStart = start;
00310   mFloats = false;
00311   switch (recurs)
00312   {
00313     case rMinutely:
00314     case rHourly:
00315       break;
00316     case rDaily:
00317     case rWeekly:
00318     case rMonthlyPos:
00319     case rMonthlyDay:
00320     case rYearlyMonth:
00321     case rYearlyDay:
00322     case rYearlyPos:
00323     default:
00324       rEndDateTime.setTime(start.time());
00325       break;
00326   }
00327   mUseCachedEndDT = false;
00328 }
00329 
00330 void Recurrence::setRecurStart(const QDate &start)
00331 {
00332   mRecurStart.setDate(start);
00333   mRecurStart.setTime(QTime(0,0,0));
00334   switch (recurs)
00335   {
00336     case rMinutely:
00337     case rHourly:
00338       break;
00339     case rDaily:
00340     case rWeekly:
00341     case rMonthlyPos:
00342     case rMonthlyDay:
00343     case rYearlyMonth:
00344     case rYearlyDay:
00345     case rYearlyPos:
00346     default:
00347       mFloats = true;
00348       break;
00349   }
00350   mUseCachedEndDT = false;
00351 }
00352 
00353 void Recurrence::setFloats(bool f)
00354 {
00355   if (f && mFloats  ||  !f && !mFloats)
00356     return;    // no change
00357 
00358   switch (recurs)
00359   {
00360     case rDaily:
00361     case rWeekly:
00362     case rMonthlyPos:
00363     case rMonthlyDay:
00364     case rYearlyMonth:
00365     case rYearlyDay:
00366     case rYearlyPos:
00367       break;
00368     case rMinutely:
00369     case rHourly:
00370     default:
00371       return;     // can't set sub-daily to floating
00372   }
00373   mFloats = f;
00374   if (f) {
00375     mRecurStart.setTime(QTime(0,0,0));
00376     rEndDateTime.setTime(QTime(0,0,0));
00377   }
00378   mUseCachedEndDT = false;
00379 }
00380 
00381 int Recurrence::frequency() const
00382 {
00383   return rFreq;
00384 }
00385 
00386 int Recurrence::duration() const
00387 {
00388   return rDuration;
00389 }
00390 
00391 void Recurrence::setDuration(int _rDuration)
00392 {
00393   if (mRecurReadOnly) return;
00394   if (_rDuration > 0) {
00395     rDuration = _rDuration;
00396     // Compatibility mode is only needed when reading the calendar in ICalFormatImpl,
00397     // so explicitly setting the duration means no backwards compatibility is needed.
00398     mCompatDuration = 0;
00399   }
00400   mUseCachedEndDT = false;
00401 }
00402 
00403 QString Recurrence::endDateStr(bool shortfmt) const
00404 {
00405   return KGlobal::locale()->formatDate(rEndDateTime.date(),shortfmt);
00406 }
00407 
00408 const QBitArray &Recurrence::days() const
00409 {
00410   return rDays;
00411 }
00412 
00413 const QPtrList<Recurrence::rMonthPos> &Recurrence::monthPositions() const
00414 {
00415   return rMonthPositions;
00416 }
00417 
00418 const QPtrList<Recurrence::rMonthPos> &Recurrence::yearMonthPositions() const
00419 {
00420   return rMonthPositions;
00421 }
00422 
00423 const QPtrList<int> &Recurrence::monthDays() const
00424 {
00425   return rMonthDays;
00426 }
00427 
00428 void Recurrence::setMinutely(int _rFreq, int _rDuration)
00429 {
00430   if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
00431     return;
00432   setDailySub(rMinutely, _rFreq, _rDuration);
00433 }
00434 
00435 void Recurrence::setMinutely(int _rFreq, const QDateTime &_rEndDateTime)
00436 {
00437   if (mRecurReadOnly) return;
00438   rEndDateTime = _rEndDateTime;
00439   setDailySub(rMinutely, _rFreq, 0);
00440 }
00441 
00442 void Recurrence::setHourly(int _rFreq, int _rDuration)
00443 {
00444   if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
00445     return;
00446   setDailySub(rHourly, _rFreq, _rDuration);
00447 }
00448 
00449 void Recurrence::setHourly(int _rFreq, const QDateTime &_rEndDateTime)
00450 {
00451   if (mRecurReadOnly) return;
00452   rEndDateTime = _rEndDateTime;
00453   setDailySub(rHourly, _rFreq, 0);
00454 }
00455 
00456 void Recurrence::setDaily(int _rFreq, int _rDuration)
00457 {
00458   if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
00459     return;
00460   setDailySub(rDaily, _rFreq, _rDuration);
00461 }
00462 
00463 void Recurrence::setDaily(int _rFreq, const QDate &_rEndDate)
00464 {
00465   if (mRecurReadOnly) return;
00466   rEndDateTime.setDate(_rEndDate);
00467   rEndDateTime.setTime(mRecurStart.time());
00468   setDailySub(rDaily, _rFreq, 0);
00469 }
00470 
00471 void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
00472                                int _rDuration, int _rWeekStart)
00473 {
00474   if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
00475     return;
00476   mUseCachedEndDT = false;
00477 
00478   recurs = rWeekly;
00479   rFreq = _rFreq;
00480   rDays = _rDays;
00481   rWeekStart = _rWeekStart;
00482   rDuration = _rDuration;
00483   if (mCompatVersion < 310 && _rDuration > 0) {
00484     // Backwards compatibility for KDE < 3.1.
00485     // rDuration was set to the number of time periods to recur,
00486     // with week start always on a Monday.
00487     // Convert this to the number of occurrences.
00488     mCompatDuration = _rDuration;
00489     int weeks = ((mCompatDuration-1)*7) + (7 - mRecurStart.date().dayOfWeek());
00490     QDate end(mRecurStart.date().addDays(weeks * rFreq));
00491     rDuration = INT_MAX;    // ensure that weeklyCalc() does its job correctly
00492     rDuration = weeklyCalc(COUNT_TO_DATE, end);
00493   } else {
00494     mCompatDuration = 0;
00495   }
00496   rMonthPositions.clear();
00497   rMonthDays.clear();
00498   if (mParent) mParent->updated();
00499 }
00500 
00501 void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
00502                                const QDate &_rEndDate, int _rWeekStart)
00503 {
00504   if (mRecurReadOnly) return;
00505   mUseCachedEndDT = false;
00506 
00507   recurs = rWeekly;
00508   rFreq = _rFreq;
00509   rDays = _rDays;
00510   rWeekStart = _rWeekStart;
00511   rEndDateTime.setDate(_rEndDate);
00512   rEndDateTime.setTime(mRecurStart.time());
00513   rDuration = 0; // set to 0 because there is an end date
00514   mCompatDuration = 0;
00515   rMonthPositions.clear();
00516   rMonthDays.clear();
00517   rYearNums.clear();
00518   if (mParent) mParent->updated();
00519 }
00520 
00521 void Recurrence::setMonthly(short type, int _rFreq, int _rDuration)
00522 {
00523   if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
00524     return;
00525   mUseCachedEndDT = false;
00526 
00527   recurs = type;
00528   rFreq = _rFreq;
00529   rDuration = _rDuration;
00530   if (mCompatVersion < 310)
00531     mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
00532   rYearNums.clear();
00533   if (mParent) mParent->updated();
00534 }
00535 
00536 void Recurrence::setMonthly(short type, int _rFreq,
00537                             const QDate &_rEndDate)
00538 {
00539   if (mRecurReadOnly) return;
00540   mUseCachedEndDT = false;
00541 
00542   recurs = type;
00543   rFreq = _rFreq;
00544   rEndDateTime.setDate(_rEndDate);
00545   rEndDateTime.setTime(mRecurStart.time());
00546   rDuration = 0; // set to 0 because there is an end date
00547   mCompatDuration = 0;
00548   rYearNums.clear();
00549   if (mParent) mParent->updated();
00550 }
00551 
00552 void Recurrence::addMonthlyPos(short _rPos, const QBitArray &_rDays)
00553 {
00554   if (recurs == rMonthlyPos)
00555     addMonthlyPos_(_rPos, _rDays);
00556 }
00557 
00558 void Recurrence::addMonthlyPos_(short _rPos, const QBitArray &_rDays)
00559 {
00560   if (mRecurReadOnly
00561   ||  _rPos == 0 || _rPos > 5 || _rPos < -5)    // invalid week number
00562     return;
00563 
00564   mUseCachedEndDT = false;
00565   for (rMonthPos* it = rMonthPositions.first();  it;  it = rMonthPositions.next()) {
00566     int itPos = it->negative ? -it->rPos : it->rPos;
00567     if (_rPos == itPos) {
00568       // This week is already in the list.
00569       // Combine the specified days with those in the list.
00570       it->rDays |= _rDays;
00571       if (mParent) mParent->updated();
00572       return;
00573     }
00574   }
00575   // Add the new position to the list
00576   rMonthPos *tmpPos = new rMonthPos;
00577   if (_rPos > 0) {
00578     tmpPos->rPos = _rPos;
00579     tmpPos->negative = false;
00580   } else {
00581     tmpPos->rPos = -_rPos; // take abs()
00582     tmpPos->negative = true;
00583   }
00584   tmpPos->rDays = _rDays;
00585   tmpPos->rDays.detach();
00586   rMonthPositions.append(tmpPos);
00587 
00588   if (mCompatVersion < 310 && mCompatDuration > 0) {
00589     // Backwards compatibility for KDE < 3.1.
00590     // rDuration was set to the number of time periods to recur.
00591     // Convert this to the number of occurrences.
00592     int monthsAhead = (mCompatDuration-1) * rFreq;
00593     int month = mRecurStart.date().month() - 1 + monthsAhead;
00594     QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
00595     rDuration = INT_MAX;    // ensure that recurCalc() does its job correctly
00596     rDuration = recurCalc(COUNT_TO_DATE, end);
00597   }
00598 
00599   if (mParent) mParent->updated();
00600 }
00601 
00602 void Recurrence::addMonthlyDay(short _rDay)
00603 {
00604   if (mRecurReadOnly || (recurs != rMonthlyDay && recurs != rYearlyMonth)
00605   ||  _rDay == 0 || _rDay > 31 || _rDay < -31)   // invalid day number
00606     return;
00607   for (int* it = rMonthDays.first();  it;  it = rMonthDays.next()) {
00608     if (_rDay == *it)
00609       return;        // this day is already in the list - avoid duplication
00610   }
00611   mUseCachedEndDT = false;
00612 
00613   int *tmpDay = new int;
00614   *tmpDay = _rDay;
00615   rMonthDays.append(tmpDay);
00616 
00617   if (mCompatVersion < 310 && mCompatDuration > 0) {
00618     // Backwards compatibility for KDE < 3.1.
00619     // rDuration was set to the number of time periods to recur.
00620     // Convert this to the number of occurrences.
00621     int monthsAhead = (mCompatDuration-1) * rFreq;
00622     int month = mRecurStart.date().month() - 1 + monthsAhead;
00623     QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
00624     rDuration = INT_MAX;    // ensure that recurCalc() does its job correctly
00625     rDuration = recurCalc(COUNT_TO_DATE, end);
00626   }
00627 
00628   if (mParent) mParent->updated();
00629 }
00630 
00631 void Recurrence::setYearly(int type, int _rFreq, int _rDuration)
00632 {
00633   if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
00634     return;
00635   if (mCompatVersion < 310)
00636     mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
00637   setYearly_(type, mFeb29YearlyDefaultType, _rFreq, _rDuration);
00638 }
00639 
00640 void Recurrence::setYearly(int type, int _rFreq, const QDate &_rEndDate)
00641 {
00642   if (mRecurReadOnly) return;
00643   rEndDateTime.setDate(_rEndDate);
00644   rEndDateTime.setTime(mRecurStart.time());
00645   mCompatDuration = 0;
00646   setYearly_(type, mFeb29YearlyDefaultType, _rFreq, 0);
00647 }
00648 
00649 void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, int _rDuration)
00650 {
00651   setYearlyByDate(0, type, _rFreq, _rDuration);
00652 }
00653 
00654 void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, const QDate &_rEndDate)
00655 {
00656   setYearlyByDate(0, type, _rFreq, _rEndDate);
00657 }
00658 
00659 void Recurrence::setYearlyByDate(int day, Feb29Type type, int _rFreq, int _rDuration)
00660 {
00661   if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
00662     return;
00663   if (mCompatVersion < 310)
00664     mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
00665   setYearly_(rYearlyMonth, type, _rFreq, _rDuration);
00666   if (day)
00667     addMonthlyDay(day);
00668 }
00669 
00670 void Recurrence::setYearlyByDate(int day, Feb29Type type, int _rFreq, const QDate &_rEndDate)
00671 {
00672   if (mRecurReadOnly) return;
00673   rEndDateTime.setDate(_rEndDate);
00674   rEndDateTime.setTime(mRecurStart.time());
00675   mCompatDuration = 0;
00676   setYearly_(rYearlyMonth, type, _rFreq, 0);
00677   if (day)
00678     addMonthlyDay(day);
00679 }
00680 
00681 void Recurrence::addYearlyMonthPos(short _rPos, const QBitArray &_rDays)
00682 {
00683   if (recurs == rYearlyPos)
00684     addMonthlyPos_(_rPos, _rDays);
00685 }
00686 
00687 const QPtrList<int> &Recurrence::yearNums() const
00688 {
00689   return rYearNums;
00690 }
00691 
00692 void Recurrence::addYearlyNum(short _rNum)
00693 {
00694   if (mRecurReadOnly
00695   ||  (recurs != rYearlyMonth && recurs != rYearlyDay && recurs != rYearlyPos)
00696   ||  _rNum <= 0)    // invalid day/month number
00697     return;
00698 
00699   if (mCompatVersion < 310 && mCompatRecurs == rYearlyDay) {
00700     // Backwards compatibility for KDE < 3.1.
00701     // Dates were stored as day numbers, with a fiddle to take account of leap years.
00702     // Convert the day number to a month.
00703     if (_rNum <= 0 || _rNum > 366 || (_rNum == 366 && mRecurStart.date().daysInYear() < 366))
00704       return;     // invalid day number
00705     _rNum = QDate(mRecurStart.date().year(), 1, 1).addDays(_rNum - 1).month();
00706   } else
00707   if ((recurs == rYearlyMonth || recurs == rYearlyPos) && _rNum > 12
00708   ||  recurs == rYearlyDay && _rNum > 366)
00709     return;     // invalid day number
00710 
00711   uint i = 0;
00712   for (int* it = rYearNums.first();  it && _rNum >= *it;  it = rYearNums.next()) {
00713     if (_rNum == *it)
00714       return;        // this day/month is already in the list - avoid duplication
00715     ++i;
00716   }
00717   mUseCachedEndDT = false;
00718 
00719   int *tmpNum = new int;
00720   *tmpNum = _rNum;
00721   rYearNums.insert(i, tmpNum);   // insert the day/month in a sorted position
00722 
00723   if (mCompatVersion < 310 && mCompatDuration > 0) {
00724     // Backwards compatibility for KDE < 3.1.
00725     // rDuration was set to the number of time periods to recur.
00726     // Convert this to the number of occurrences.
00727     QDate end(mRecurStart.date().year() + (mCompatDuration-1)*rFreq, 12, 31);
00728     rDuration = INT_MAX;    // ensure that recurCalc() does its job correctly
00729     rDuration = recurCalc(COUNT_TO_DATE, end);
00730   }
00731 
00732   if (mParent) mParent->updated();
00733 }
00734 
00735 
00736 QValueList<QTime> Recurrence::recurTimesOn(const QDate &date) const
00737 {
00738   QValueList<QTime> times;
00739   switch (recurs)
00740   {
00741     case rMinutely:
00742     case rHourly:
00743       if ((date >= mRecurStart.date()) &&
00744           ((rDuration > 0) && (date <= endDate()) ||
00745            ((rDuration == 0) && (date <= rEndDateTime.date())) ||
00746            (rDuration == -1))) {
00747         // The date queried falls within the range of the event.
00748         int secondFreq = rFreq * (recurs == rMinutely ? 60 : 3600);
00749         int after = mRecurStart.secsTo(QDateTime(date)) - 1;
00750         int count = (after + 24*3600) / secondFreq - after / secondFreq;
00751         if (count) {
00752           // It recurs at least once on the given date
00753           QTime t = mRecurStart.addSecs((after / secondFreq) * secondFreq).time();
00754           while (--count >= 0) {
00755             t = t.addSecs(secondFreq);
00756             times.append(t);
00757           }
00758         }
00759       }
00760       break;
00761     case rDaily:
00762     case rWeekly:
00763     case rMonthlyPos:
00764     case rMonthlyDay:
00765     case rYearlyMonth:
00766     case rYearlyDay:
00767     case rYearlyPos:
00768       if (recursOnPure(date))
00769         times.append(mRecurStart.time());
00770       break;
00771     default:
00772       break;
00773   }
00774   return times;
00775 }
00776 
00777 QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime, bool *last) const
00778 {
00779   int freq;
00780   switch (recurs)
00781   {
00782     case rMinutely:
00783       freq = rFreq * 60;
00784       break;
00785     case rHourly:
00786       freq = rFreq * 3600;
00787       break;
00788     case rDaily:
00789     case rWeekly:
00790     case rMonthlyPos:
00791     case rMonthlyDay:
00792     case rYearlyMonth:
00793     case rYearlyDay:
00794     case rYearlyPos: {
00795       QDate preDate = preDateTime.date();
00796       if (!mFloats && mRecurStart.time() > preDateTime.time())
00797         preDate = preDate.addDays(-1);
00798       return QDateTime(getNextDateNoTime(preDate, last), mRecurStart.time());
00799     }
00800     default:
00801       return QDateTime();
00802   }
00803 
00804   // It's a sub-daily recurrence
00805   if (last)
00806     *last = false;
00807   if (preDateTime < mRecurStart)
00808     return mRecurStart;
00809   int count = mRecurStart.secsTo(preDateTime) / freq + 2;
00810   if (rDuration > 0) {
00811     if (count > rDuration)
00812       return QDateTime();
00813     if (last && count == rDuration)
00814       *last = true;
00815   }
00816   QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
00817   if (rDuration == 0) {
00818     if (endtime > rEndDateTime)
00819       return QDateTime();
00820     if (last && endtime == rEndDateTime)
00821       *last = true;
00822   }
00823   return endtime;
00824 }
00825 
00826 QDate Recurrence::getNextDate(const QDate &preDate, bool *last) const
00827 {
00828   switch (recurs)
00829   {
00830     case rMinutely:
00831     case rHourly:
00832       return getNextDateTime(QDateTime(preDate, QTime(23,59,59)), last).date();
00833     case rDaily:
00834     case rWeekly:
00835     case rMonthlyPos:
00836     case rMonthlyDay:
00837     case rYearlyMonth:
00838     case rYearlyDay:
00839     case rYearlyPos:
00840       return getNextDateNoTime(preDate, last);
00841     default:
00842       return QDate();
00843   }
00844 }
00845 
00846 
00847 QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime, bool *last) const
00848 {
00849   int freq;
00850   switch (recurs)
00851   {
00852     case rMinutely:
00853       freq = rFreq * 60;
00854       break;
00855     case rHourly:
00856       freq = rFreq * 3600;
00857       break;
00858     case rDaily:
00859     case rWeekly:
00860     case rMonthlyPos:
00861     case rMonthlyDay:
00862     case rYearlyMonth:
00863     case rYearlyDay:
00864     case rYearlyPos: {
00865       QDate afterDate = afterDateTime.date();
00866       if (!mFloats && mRecurStart.time() < afterDateTime.time())
00867         afterDate = afterDate.addDays(1);
00868       return QDateTime(getPreviousDateNoTime(afterDate, last), mRecurStart.time());
00869     }
00870     default:
00871       return QDateTime();
00872   }
00873 
00874   // It's a sub-daily recurrence
00875   if (last)
00876     *last = false;
00877   if (afterDateTime <= mRecurStart)
00878     return QDateTime();
00879   int count = (mRecurStart.secsTo(afterDateTime) - 1) / freq + 1;
00880   if (rDuration > 0) {
00881     if (count > rDuration)
00882       count = rDuration;
00883     if (last && count == rDuration)
00884       *last = true;
00885   }
00886   QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
00887   if (rDuration == 0) {
00888     if (endtime > rEndDateTime)
00889       endtime = rEndDateTime;
00890     if (last && endtime == rEndDateTime)
00891       *last = true;
00892   }
00893   return endtime;
00894 }
00895 
00896 QDate Recurrence::getPreviousDate(const QDate &afterDate, bool *last) const
00897 {
00898   switch (recurs)
00899   {
00900     case rMinutely:
00901     case rHourly:
00902       return getPreviousDateTime(QDateTime(afterDate, QTime(0,0,0)), last).date();
00903     case rDaily:
00904     case rWeekly:
00905     case rMonthlyPos:
00906     case rMonthlyDay:
00907     case rYearlyMonth:
00908     case rYearlyDay:
00909     case rYearlyPos:
00910       return getPreviousDateNoTime(afterDate, last);
00911     default:
00912       return QDate();
00913   }
00914 }
00915 
00916 
00917 /***************************** PROTECTED FUNCTIONS ***************************/
00918 
00919 bool Recurrence::recursSecondly(const QDate &qd, int secondFreq) const
00920 {
00921   if ((qd >= mRecurStart.date()) &&
00922       ((rDuration > 0) && (qd <= endDate()) ||
00923        ((rDuration == 0) && (qd <= rEndDateTime.date())) ||
00924        (rDuration == -1))) {
00925     // The date queried falls within the range of the event.
00926     if (secondFreq < 24*3600)
00927       return true;      // the event recurs at least once each day
00928     int after = mRecurStart.secsTo(QDateTime(qd)) - 1;
00929     if (after / secondFreq != (after + 24*3600) / secondFreq)
00930       return true;
00931   }
00932   return false;
00933 }
00934 
00935 bool Recurrence::recursMinutelyAt(const QDateTime &dt, int minuteFreq) const
00936 {
00937   if ((dt >= mRecurStart) &&
00938       ((rDuration > 0) && (dt <= endDateTime()) ||
00939        ((rDuration == 0) && (dt <= rEndDateTime)) ||
00940        (rDuration == -1))) {
00941     // The time queried falls within the range of the event.
00942     if (((mRecurStart.secsTo(dt) / 60) % minuteFreq) == 0)
00943       return true;
00944   }
00945   return false;
00946 }
00947 
00948 bool Recurrence::recursDaily(const QDate &qd) const
00949 {
00950   QDate dStart = mRecurStart.date();
00951   if ((dStart.daysTo(qd) % rFreq) == 0) {
00952     // The date is a day which recurs
00953     if (qd >= dStart
00954     &&  ((rDuration > 0 && qd <= endDate()) ||
00955          (rDuration == 0 && qd <= rEndDateTime.date()) ||
00956          rDuration == -1)) {
00957       // The date queried falls within the range of the event.
00958       return true;
00959     }
00960   }
00961   return false;
00962 }
00963 
00964 bool Recurrence::recursWeekly(const QDate &qd) const
00965 {
00966   QDate dStart = mRecurStart.date();
00967   if ((dStart.daysTo(qd)/7) % rFreq == 0) {
00968     // The date is in a week which recurs
00969     if (qd >= dStart
00970     && ((rDuration > 0 && qd <= endDate()) ||
00971         (rDuration == 0 && qd <= rEndDateTime.date()) ||
00972         rDuration == -1)) {
00973       // The date queried falls within the range of the event.
00974       // check if the bits set match today.
00975       int i = qd.dayOfWeek()-1;
00976       if (rDays.testBit((uint) i))
00977         return true;
00978     }
00979   }
00980   return false;
00981 }
00982 
00983 bool Recurrence::recursMonthly(const QDate &qd) const
00984 {
00985   QDate dStart = mRecurStart.date();
00986   int year  = qd.year();
00987   int month = qd.month();
00988   int day   = qd.day();
00989   // calculate how many months ahead this date is from the original
00990   // event's date
00991   int monthsAhead = (year - dStart.year()) * 12 + (month - dStart.month());
00992   if ((monthsAhead % rFreq) == 0) {
00993     // The date is in a month which recurs
00994     if (qd >= dStart
00995     &&  ((rDuration > 0 && qd <= endDate()) ||
00996          (rDuration == 0 && qd <= rEndDateTime.date()) ||
00997          rDuration == -1)) {
00998       // The date queried falls within the range of the event.
00999       QValueList<int> days;
01000       int daysInMonth = qd.daysInMonth();
01001       if (recurs == rMonthlyDay)
01002         getMonthlyDayDays(days, daysInMonth);
01003       else if (recurs == rMonthlyPos)
01004         getMonthlyPosDays(days, daysInMonth, QDate(year, month, 1).dayOfWeek());
01005       for (QValueList<int>::Iterator it = days.begin();  it != days.end();  ++it) {
01006         if (*it == day)
01007           return true;
01008       }
01009       // no dates matched
01010     }
01011   }
01012   return false;
01013 }
01014 
01015 bool Recurrence::recursYearlyByMonth(const QDate &qd) const
01016 {
01017   QDate dStart = mRecurStart.date();
01018   int startDay = dStart.day();
01019   if (rMonthDays.count())
01020     startDay = *rMonthDays.getFirst();
01021   int qday     = qd.day();
01022   int qmonth   = qd.month();
01023   int qyear    = qd.year();
01024   bool match = (qday == startDay);
01025   if (startDay < 0)
01026     match = (qday == qd.daysInMonth() + startDay + 1);
01027   if (!match && startDay == 29 && dStart.month() == 2) {
01028     // It's a recurrence on February 29th
01029     switch (mFeb29YearlyType) {
01030       case rFeb28:
01031         if (qday == 28 && qmonth == 2 && !QDate::leapYear(qyear))
01032           match = true;
01033         break;
01034       case rMar1:
01035         if (qday == 1 && qmonth == 3 && !QDate::leapYear(qyear)) {
01036           qmonth = 2;
01037           match = true;
01038         }
01039         break;
01040       case rFeb29:
01041         break;
01042     }
01043   }
01044 
01045   if (match) {
01046     // The day of the month matches. Calculate how many years ahead
01047     // this date is from the original event's date.
01048     int yearsAhead = (qyear - dStart.year());
01049     if (yearsAhead % rFreq == 0) {
01050       // The date is in a year which recurs
01051       if (qd >= dStart
01052       &&  ((rDuration > 0 && qd <= endDate()) ||
01053            (rDuration == 0 && qd <= rEndDateTime.date()) ||
01054            rDuration == -1)) {
01055         // The date queried falls within the range of the event.
01056         int i = qmonth;
01057         for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
01058           if (i == *qlin.current())
01059             return true;
01060         }
01061       }
01062     }
01063   }
01064   return false;
01065 }
01066 
01067 bool Recurrence::recursYearlyByPos(const QDate &qd) const
01068 {
01069   QDate dStart = mRecurStart.date();
01070   int year  = qd.year();
01071   int month = qd.month();
01072   int day   = qd.day();
01073   // calculate how many years ahead this date is from the original
01074   // event's date
01075   int yearsAhead = (year - dStart.year());
01076   if (yearsAhead % rFreq == 0) {
01077     // The date is in a year which recurs
01078     if (qd >= dStart
01079     &&  ((rDuration > 0 && qd <= endDate()) ||
01080          (rDuration == 0 && qd <= rEndDateTime.date()) ||
01081          rDuration == -1)) {
01082       // The date queried falls within the range of the event.
01083       for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
01084         if (month == *qlin.current()) {
01085           // The month recurs
01086           QValueList<int> days;
01087           getMonthlyPosDays(days, qd.daysInMonth(), QDate(year, month, 1).dayOfWeek());
01088           for (QValueList<int>::Iterator it = days.begin();  it != days.end();  ++it) {
01089             if (*it == day)
01090               return true;
01091           }
01092         }
01093       }
01094     }
01095   }
01096   return false;
01097 }
01098 
01099 bool Recurrence::recursYearlyByDay(const QDate &qd) const
01100 {
01101   QDate dStart = mRecurStart.date();
01102   // calculate how many years ahead this date is from the original
01103   // event's date
01104   int yearsAhead = (qd.year() - dStart.year());
01105   if (yearsAhead % rFreq == 0) {
01106     // The date is in a year which recurs
01107     if (qd >= dStart
01108     &&  ((rDuration > 0 && qd <= endDate()) ||
01109          (rDuration == 0 && qd <= rEndDateTime.date()) ||
01110          rDuration == -1)) {
01111       // The date queried falls within the range of the event.
01112       int i = qd.dayOfYear();
01113       for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
01114         if (i == *qlin.current())
01115           return true;
01116       }
01117     }
01118   }
01119   return false;
01120 }
01121 
01122 /* Get the date of the next recurrence, after the specified date.
01123  * If 'last' is non-null, '*last' is set to true if the next recurrence is the
01124  * last recurrence, else false.
01125  * Reply = date of next recurrence, or invalid date if none.
01126  */
01127 QDate Recurrence::getNextDateNoTime(const QDate &preDate, bool *last) const
01128 {
01129   if (last)
01130     *last = false;
01131   QDate dStart = mRecurStart.date();
01132   if (preDate < dStart)
01133     return dStart;
01134   QDate earliestDate = preDate.addDays(1);
01135   QDate nextDate;
01136 
01137   switch (recurs) {
01138     case rDaily:
01139       nextDate = dStart.addDays((dStart.daysTo(preDate)/rFreq + 1) * rFreq);
01140       break;
01141 
01142     case rWeekly: {
01143       QDate start = dStart.addDays(1 - dStart.dayOfWeek());   // start of week for dStart
01144       int earliestDayOfWeek = earliestDate.dayOfWeek();
01145       int weeksAhead = start.daysTo(earliestDate) / 7;
01146       int notThisWeek = weeksAhead % rFreq;    // zero if this week is a recurring week
01147       weeksAhead -= notThisWeek;               // latest week which recurred
01148       int weekday = 0;
01149       // First check for any remaining day this week, if this week is a recurring week
01150       if (!notThisWeek)
01151         weekday = getFirstDayInWeek(earliestDayOfWeek);
01152       // Check for a day in the next scheduled week
01153       if (!weekday  &&  earliestDayOfWeek > 1)
01154         weekday = getFirstDayInWeek(rWeekStart) + rFreq*7;
01155       if (weekday)
01156         nextDate = start.addDays(weeksAhead*7 + weekday - 1);
01157       break;
01158     }
01159     case rMonthlyDay:
01160     case rMonthlyPos: {
01161       int startYear  = dStart.year();
01162       int startMonth = dStart.month();     // 1..12
01163       int earliestYear = earliestDate.year();
01164       int monthsAhead = (earliestYear - startYear)*12 + earliestDate.month() - startMonth;
01165       int notThisMonth = monthsAhead % rFreq;    // zero if this month is a recurring month
01166       monthsAhead -= notThisMonth;               // latest month which recurred
01167       // Check for the first later day in the current month
01168       if (!notThisMonth)
01169         nextDate = getFirstDateInMonth(earliestDate);
01170       if (!nextDate.isValid()  &&  earliestDate.day() > 1) {
01171         // Check for a day in the next scheduled month
01172         int months = startMonth - 1 + monthsAhead + rFreq;
01173         nextDate = getFirstDateInMonth(QDate(startYear + months/12, months%12 + 1, 1));
01174       }
01175       break;
01176     }
01177     case rYearlyMonth:
01178     case rYearlyPos:
01179     case rYearlyDay: {
01180       int startYear  = dStart.year();
01181       int yearsAhead = earliestDate.year() - startYear;
01182       int notThisYear = yearsAhead % rFreq;   // zero if this year is a recurring year
01183       yearsAhead -= notThisYear;              // latest year which recurred
01184       // Check for the first later date in the current year
01185       if (!notThisYear)
01186         nextDate = getFirstDateInYear(earliestDate);
01187       // Check for a date in the next scheduled year
01188       if (!nextDate.isValid()) {
01189         startYear += yearsAhead + rFreq;
01190         // The only valid reason for failure after the next check is that it only recurs
01191         // in a leap year. Since certain leap year recurrences could potentially never
01192         // actually occur (e.g. recurring on the 366th day of the year every 4 years,
01193         // starting in a non-leap year), limit the iterations to 8 (to allow for possible
01194         // century non-leap years).
01195         for (int i = 0;  i < 8;  ++i) {
01196           nextDate = getFirstDateInYear(QDate(startYear, 1, 1));
01197           if (nextDate.isValid())
01198             break;
01199           startYear += rFreq;
01200         }
01201       }
01202       break;
01203     }
01204     case rNone:
01205     default:
01206       return QDate();
01207   }
01208 
01209   if (rDuration >= 0 && nextDate.isValid()) {
01210     // Check that the date found is within the range of the recurrence
01211     QDate end = endDate();
01212     if (nextDate > end)
01213       return QDate();
01214     if (last  &&  nextDate == end)
01215       *last = true;
01216   }
01217   return nextDate;
01218 }
01219 
01220 /* Get the date of the last previous recurrence, before the specified date.
01221  * Reply = date of previous recurrence, or invalid date if none.
01222  */
01223 QDate Recurrence::getPreviousDateNoTime(const QDate &afterDate, bool *last) const
01224 {
01225   if (last)
01226     *last = false;
01227   QDate dStart = mRecurStart.date();
01228   QDate latestDate = afterDate.addDays(-1);
01229   if (latestDate < dStart)
01230     return QDate();
01231   QDate prevDate;
01232 
01233   switch (recurs) {
01234     case rDaily:
01235       prevDate = dStart.addDays((dStart.daysTo(latestDate) / rFreq) * rFreq);
01236       break;
01237 
01238     case rWeekly: {
01239       QDate start = dStart.addDays(1 - dStart.dayOfWeek());   // start of week for dStart
01240       int latestDayOfWeek = latestDate.dayOfWeek();
01241       int weeksAhead = start.daysTo(latestDate) / 7;
01242       int notThisWeek = weeksAhead % rFreq;    // zero if this week is a recurring week
01243       weeksAhead -= notThisWeek;               // latest week which recurred
01244       int weekday = 0;
01245       // First check for any previous day this week, if this week is a recurring week
01246       if (!notThisWeek)
01247         weekday = getLastDayInWeek(latestDayOfWeek);
01248       // Check for a day in the previous scheduled week
01249       if (!weekday) {
01250         int weekEnd = (rWeekStart + 5)%7 + 1;
01251         if (latestDayOfWeek < weekEnd) {
01252           if (!notThisWeek)
01253             weeksAhead -= rFreq;
01254           weekday = getLastDayInWeek(weekEnd);
01255         }
01256       }
01257       if (weekday)
01258         prevDate = start.addDays(weeksAhead*7 + weekday - 1);
01259       break;
01260     }
01261     case rMonthlyDay:
01262     case rMonthlyPos: {
01263       int startYear  = dStart.year();
01264       int startMonth = dStart.month();     // 1..12
01265       int latestYear = latestDate.year();
01266       int monthsAhead = (latestYear - startYear)*12 + latestDate.month() - startMonth;
01267       int notThisMonth = monthsAhead % rFreq;    // zero if this month is a recurring month
01268       monthsAhead -= notThisMonth;               // latest month which recurred
01269       // Check for the last earlier day in the current month
01270       if (!notThisMonth)
01271         prevDate = getLastDateInMonth(latestDate);
01272       if (!prevDate.isValid()  &&  latestDate.day() < latestDate.daysInMonth()) {
01273         // Check for a day in the previous scheduled month
01274         if (!notThisMonth)
01275           monthsAhead -= rFreq;
01276         int months = startMonth + monthsAhead;   // get the month after the one that recurs
01277         prevDate = getLastDateInMonth(QDate(startYear + months/12, months%12 + 1, 1).addDays(-1));
01278       }
01279       break;
01280     }
01281     case rYearlyMonth:
01282     case rYearlyPos:
01283     case rYearlyDay: {
01284       int startYear  = dStart.year();
01285       int yearsAhead = latestDate.year() - startYear;
01286       int notThisYear = yearsAhead % rFreq;   // zero if this year is a recurring year
01287       yearsAhead -= notThisYear;              // latest year which recurred
01288       // Check for the first later date in the current year
01289       if (!notThisYear)
01290         prevDate = getLastDateInYear(latestDate);
01291       if (!prevDate.isValid()  &&  latestDate.dayOfYear() < latestDate.daysInYear()) {
01292         // Check for a date in the next scheduled year
01293         if (!notThisYear)
01294           yearsAhead -= rFreq;
01295         prevDate = getLastDateInYear(QDate(startYear + yearsAhead, 12, 31));
01296       }
01297       break;
01298     }
01299     case rNone:
01300     default:
01301       return QDate();
01302   }
01303 
01304   if (prevDate.isValid()) {
01305     // Check that the date found is within the range of the recurrence
01306     if (prevDate < dStart)
01307       return QDate();
01308     if (rDuration >= 0) {
01309       QDate end = endDate();
01310       if (prevDate >= end) {
01311         if (last)
01312           *last = true;
01313         return end;
01314       }
01315     }
01316   }
01317   return prevDate;
01318 }
01319 
01320 void Recurrence::setDailySub(short type, int freq, int duration)
01321 {
01322   mUseCachedEndDT = false;
01323   recurs = type;
01324   rFreq = freq;
01325   rDuration = duration;
01326   rMonthPositions.clear();
01327   rMonthDays.clear();
01328   rYearNums.clear();
01329   if (type != rDaily)
01330     mFloats = false;     // sub-daily types can't be floating
01331 
01332   if (mParent) mParent->updated();
01333 }
01334 
01335 void Recurrence::setYearly_(short type, Feb29Type feb29type, int freq, int duration)
01336 {
01337   mUseCachedEndDT = false;
01338   recurs = type;
01339   if (mCompatVersion < 310 && type == rYearlyDay) {
01340     mCompatRecurs = rYearlyDay;
01341     recurs = rYearlyMonth;      // convert old yearly-by-day to yearly-by-month
01342     feb29type = rMar1;          // retain the same day number in the year
01343   }
01344 
01345   mFeb29YearlyType = feb29type;
01346   rFreq = freq;
01347   rDuration = duration;
01348   if (type != rYearlyPos)
01349     rMonthPositions.clear();
01350   rMonthDays.clear();
01351   if (mParent) mParent->updated();
01352 }
01353 
01354 int Recurrence::recurCalc(PeriodFunc func, QDateTime &endtime) const
01355 {
01356   QDate enddate = endtime.date();
01357   switch (func) {
01358     case END_DATE_AND_COUNT:
01359       if (rDuration < 0) {
01360         endtime = QDateTime();
01361         return 0;    // infinite recurrence
01362       }
01363       if (rDuration == 0) {
01364         endtime = rEndDateTime;
01365         func = COUNT_TO_DATE;
01366       }
01367       break;
01368     case COUNT_TO_DATE:
01369       // Count recurrences up to and including the specified date/time.
01370       if (endtime < mRecurStart)
01371         return 0;
01372       if (rDuration == 0 && endtime > rEndDateTime)
01373         enddate = rEndDateTime.date();
01374       else if (!mFloats && mRecurStart.time() > endtime.time())
01375         enddate = enddate.addDays(-1);
01376       break;
01377     case NEXT_AFTER_DATE:
01378       // Find next recurrence AFTER endtime
01379       if (endtime < mRecurStart) {
01380         endtime = mRecurStart;
01381         return 1;
01382       }
01383       if (rDuration == 0 && endtime >= rEndDateTime) {
01384         endtime = QDateTime();
01385         return 0;
01386       }
01387       if (!mFloats && mRecurStart.time() > endtime.time())
01388         enddate = enddate.addDays(-1);
01389       break;
01390     default:
01391       endtime = QDateTime();
01392       return 0;
01393   }
01394 
01395   int count = 0;     // default = error
01396   bool timed = false;
01397   switch (recurs) {
01398     case rMinutely:
01399       timed = true;
01400       count = secondlyCalc(func, endtime, rFreq*60);
01401       break;
01402     case rHourly:
01403       timed = true;
01404       count = secondlyCalc(func, endtime, rFreq*3600);
01405       break;
01406     case rDaily:
01407       count = dailyCalc(func, enddate);
01408       break;
01409     case rWeekly:
01410       count = weeklyCalc(func, enddate);
01411       break;
01412     case rMonthlyPos:
01413     case rMonthlyDay:
01414       count = monthlyCalc(func, enddate);
01415       break;
01416     case rYearlyMonth:
01417       count = yearlyMonthCalc(func, enddate);
01418       break;
01419     case rYearlyPos:
01420       count = yearlyPosCalc(func, enddate);
01421       break;
01422     case rYearlyDay:
01423       count = yearlyDayCalc(func, enddate);
01424       break;
01425     default:
01426       break;
01427   }
01428 
01429   switch (func) {
01430     case END_DATE_AND_COUNT:
01431     case NEXT_AFTER_DATE:
01432       if (count == 0)
01433         endtime = QDateTime();
01434       else if (!timed) {
01435         endtime.setDate(enddate);
01436         endtime.setTime(mRecurStart.time());
01437       }
01438       break;
01439     case COUNT_TO_DATE:
01440       break;
01441   }
01442   return count;
01443 }
01444 
01445 int Recurrence::recurCalc(PeriodFunc func, QDate &enddate) const
01446 {
01447   QDateTime endtime(enddate, QTime(23,59,59));
01448   switch (func) {
01449     case END_DATE_AND_COUNT:
01450       if (rDuration < 0) {
01451         enddate = QDate();
01452         return 0;    // infinite recurrence
01453       }
01454       if (rDuration == 0) {
01455         enddate = rEndDateTime.date();
01456         func = COUNT_TO_DATE;
01457       }
01458       break;
01459     case COUNT_TO_DATE:
01460       // Count recurrences up to and including the specified date.
01461       if (enddate < mRecurStart.date())
01462         return 0;
01463       if (rDuration == 0 && enddate > rEndDateTime.date()) {
01464         enddate = rEndDateTime.date();
01465         endtime.setDate(enddate);
01466       }
01467       break;
01468     case NEXT_AFTER_DATE:
01469       if (enddate < mRecurStart.date()) {
01470         enddate = mRecurStart.date();
01471         return 1;
01472       }
01473       if (rDuration == 0 && enddate >= rEndDateTime.date()) {
01474         enddate = QDate();
01475         return 0;
01476       }
01477       break;
01478     default:
01479       enddate = QDate();
01480       return 0;
01481   }
01482 
01483   int count = 0;     // default = error
01484   bool timed = false;
01485   switch (recurs) {
01486     case rMinutely:
01487       timed = true;
01488       count = secondlyCalc(func, endtime, rFreq*60);
01489       break;
01490     case rHourly:
01491       timed = true;
01492       count = secondlyCalc(func, endtime, rFreq*3600);
01493       break;
01494     case rDaily:
01495       count = dailyCalc(func, enddate);
01496       break;
01497     case rWeekly:
01498       count = weeklyCalc(func, enddate);
01499       break;
01500     case rMonthlyPos:
01501     case rMonthlyDay:
01502       count = monthlyCalc(func, enddate);
01503       break;
01504     case rYearlyMonth:
01505       count = yearlyMonthCalc(func, enddate);
01506       break;
01507     case rYearlyPos:
01508       count = yearlyPosCalc(func, enddate);
01509       break;
01510     case rYearlyDay:
01511       count = yearlyDayCalc(func, enddate);
01512       break;
01513     default:
01514       break;
01515   }
01516 
01517   switch (func) {
01518     case END_DATE_AND_COUNT:
01519     case NEXT_AFTER_DATE:
01520       if (count == 0)
01521         endtime = QDate();
01522       else if (timed)
01523         enddate = endtime.date();
01524       break;
01525     case COUNT_TO_DATE:
01526       break;
01527   }
01528   return count;
01529 }
01530 
01531 /* Find count and, depending on 'func', the end date/time of a secondly recurrence.
01532  * Reply = total number of occurrences up to 'endtime', or 0 if error.
01533  * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'endtime' is updated to the
01534  * recurrence end date/time.
01535  */
01536 int Recurrence::secondlyCalc(PeriodFunc func, QDateTime &endtime, int freq) const
01537 {
01538   switch (func) {
01539     case END_DATE_AND_COUNT:
01540       endtime = mRecurStart.addSecs((rDuration - 1) * freq);
01541       return rDuration;
01542     case COUNT_TO_DATE: {
01543       int n = mRecurStart.secsTo(endtime)/freq + 1;
01544       if (rDuration > 0 && n > rDuration)
01545         return rDuration;
01546       return n;
01547     }
01548     case NEXT_AFTER_DATE: {
01549       int count = mRecurStart.secsTo(endtime) / freq + 2;
01550       if (rDuration > 0 && count > rDuration)
01551         return 0;
01552       endtime = mRecurStart.addSecs((count - 1)*freq);
01553       return count;
01554     }
01555   }
01556   return 0;
01557 }
01558 
01559 /* Find count and, depending on 'func', the end date of a daily recurrence.
01560  * Reply = total number of occurrences up to 'enddate', or 0 if error.
01561  * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
01562  * recurrence end date.
01563  */
01564 int Recurrence::dailyCalc(PeriodFunc func, QDate &enddate) const
01565 {
01566   QDate dStart = mRecurStart.date();
01567   switch (func) {
01568     case END_DATE_AND_COUNT:
01569       enddate = dStart.addDays((rDuration - 1) * rFreq);
01570       return rDuration;
01571     case COUNT_TO_DATE: {
01572       int n = dStart.daysTo(enddate)/rFreq + 1;
01573       if (rDuration > 0 && n > rDuration)
01574         return rDuration;
01575       return n;
01576     }
01577     case NEXT_AFTER_DATE: {
01578       int count = dStart.daysTo(enddate) / rFreq + 2;
01579       if (rDuration > 0 && count > rDuration)
01580         return 0;
01581       enddate = dStart.addDays((count - 1)*rFreq);
01582       return count;
01583     }
01584   }
01585   return 0;
01586 }
01587 
01588 /* Find count and, depending on 'func', the end date of a weekly recurrence.
01589  * Reply = total number of occurrences up to 'enddate', or 0 if error.
01590  * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
01591  * recurrence end date.
01592  */
01593 int Recurrence::weeklyCalc(PeriodFunc func, QDate &enddate) const
01594 {
01595   int daysPerWeek = 0;
01596   for (int i = 0;  i < 7;  ++i) {
01597     if (rDays.testBit((uint)i))
01598       ++daysPerWeek;
01599   }
01600   if (!daysPerWeek)
01601     return 0;     // there are no days to recur on
01602 
01603   switch (func) {
01604     case END_DATE_AND_COUNT:
01605       return weeklyCalcEndDate(enddate, daysPerWeek);
01606     case COUNT_TO_DATE:
01607       return weeklyCalcToDate(enddate, daysPerWeek);
01608     case NEXT_AFTER_DATE:
01609       return weeklyCalcNextAfter(enddate, daysPerWeek);
01610   }
01611   return 0;
01612 }
01613 
01614 int Recurrence::weeklyCalcEndDate(QDate &enddate, int daysPerWeek) const
01615 {
01616   int startDayOfWeek = mRecurStart.date().dayOfWeek();     // 1..7
01617   int countGone = 0;
01618   int daysGone = 0;
01619   uint countTogo = rDuration;
01620   if (startDayOfWeek != rWeekStart) {
01621     // Check what remains of the start week
01622     for (int i = startDayOfWeek - 1;  i != rWeekStart - 1;  i = (i + 1) % 7) {
01623       ++daysGone;
01624       if (rDays.testBit((uint)i)) {
01625         ++countGone;
01626         if (--countTogo == 0)
01627           break;
01628       }
01629     }
01630     daysGone += 7 * (rFreq - 1);
01631   }
01632   if (countTogo) {
01633     // Skip the remaining whole weeks
01634     // Leave at least 1 recurrence remaining, in order to get its date
01635     int wholeWeeks = (countTogo - 1) / daysPerWeek;
01636     daysGone += wholeWeeks * 7 * rFreq;
01637     countGone += wholeWeeks * daysPerWeek;
01638     countTogo -= wholeWeeks * daysPerWeek;
01639     // Check the last week in the recurrence
01640     for (int i = rWeekStart - 1;  ;  i = (i + 1) % 7) {
01641       ++daysGone;
01642       if (rDays.testBit((uint)i)) {
01643         ++countGone;
01644         if (--countTogo == 0) 
01645           break;
01646       }
01647     }
01648   }
01649   enddate = mRecurStart.date().addDays(daysGone);
01650   return countGone;
01651 }
01652 
01653 int Recurrence::weeklyCalcToDate(const QDate &enddate, int daysPerWeek) const
01654 {
01655   QDate dStart = mRecurStart.date();
01656   int startDayOfWeek = dStart.dayOfWeek();     // 1..7
01657   int countGone = 0;
01658   int daysGone  = 0;
01659   int totalDays = dStart.daysTo(enddate) + 1;
01660   int countMax  = (rDuration > 0) ? rDuration : INT_MAX;
01661 
01662   if (startDayOfWeek != rWeekStart) {
01663     // Check what remains of the start week
01664     for (int i = startDayOfWeek - 1;  i != rWeekStart - 1;  i = (i + 1) % 7) {
01665       if (rDays.testBit((uint)i)) {
01666         if (++countGone >= countMax)
01667           return countMax;
01668       }
01669       if (++daysGone == totalDays)
01670         return countGone;
01671     }
01672     daysGone += 7 * (rFreq - 1);
01673     if (daysGone >= totalDays)
01674       return countGone;
01675   }
01676   // Skip the remaining whole weeks
01677   int wholeWeeks = (totalDays - daysGone) / 7;
01678   countGone += (wholeWeeks / rFreq) * daysPerWeek;
01679   if (countGone >= countMax)
01680     return countMax;
01681   daysGone += wholeWeeks * 7;
01682   if (daysGone >= totalDays     // have we reached the end date?
01683   ||  wholeWeeks % rFreq)       // is end week a recurrence week?
01684     return countGone;
01685 
01686   // Check the last week in the recurrence
01687   for (int i = rWeekStart - 1;  ;  i = (i + 1) % 7) {
01688     if (rDays.testBit((uint)i)) {
01689       if (++countGone >= countMax)
01690         return countMax;
01691     }
01692     if (++daysGone == totalDays)
01693       return countGone;
01694   }
01695   return countGone;
01696 }
01697 
01698 int Recurrence::weeklyCalcNextAfter(QDate &enddate, int daysPerWeek) const
01699 {
01700   QDate dStart = mRecurStart.date();
01701   int  startDayOfWeek = dStart.dayOfWeek();     // 1..7
01702   int  totalDays = dStart.daysTo(enddate) + 1;
01703   uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX;
01704   int  countGone = 0;
01705   int  daysGone = 0;
01706   int recurWeeks;
01707 
01708   if (startDayOfWeek != rWeekStart) {
01709     // Check what remains of the start week
01710     for (int i = startDayOfWeek - 1;  i != rWeekStart - 1;  i = (i + 1) % 7) {
01711       ++daysGone;
01712       if (rDays.testBit((uint)i)) {
01713         ++countGone;
01714         if (daysGone > totalDays)
01715           goto ex;
01716         if (--countTogo == 0)
01717           return 0;
01718       }
01719     }
01720     daysGone += 7 * (rFreq - 1);
01721   }
01722 
01723   // Skip the remaining whole weeks
01724   recurWeeks = (totalDays - daysGone) / (7 * rFreq);
01725   if (recurWeeks) {
01726     int n = recurWeeks * daysPerWeek;
01727     if (static_cast<uint>(n) > countTogo)
01728         return 0;     // reached end of recurrence
01729     countGone += n;
01730     countTogo -= n;
01731    daysGone += recurWeeks * 7 * rFreq;
01732   }
01733 
01734   // Check the last week or two in the recurrence
01735   for ( ; ; ) {
01736     for (int i = rWeekStart - 1;  ;  i = (i + 1) % 7) {
01737       ++daysGone;
01738       if (rDays.testBit((uint)i)) {
01739         ++countGone;
01740         if (daysGone > totalDays)
01741           goto ex;
01742         if (--countTogo == 0)
01743           return 0;
01744       }
01745     }
01746     daysGone += 7 * (rFreq - 1);
01747   }
01748 ex:
01749   enddate = dStart.addDays(daysGone);
01750   return countGone;
01751 }
01752 
01753 /* Find count and, depending on 'func', the end date of a monthly recurrence.
01754  * Reply = total number of occurrences up to 'enddate', or 0 if error.
01755  * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
01756  * recurrence end date.
01757  */
01758 class Recurrence::MonthlyData
01759 {
01760   public:
01761     const Recurrence *recurrence;
01762     int               year;          // current year
01763     int               month;         // current month 0..11
01764     int               day;           // current day of month 1..31
01765     bool              varies;        // true if recurring days vary between different months
01766 
01767   private:
01768     QValueList<int>   days28, days29, days30, days31;   // recurring days in months of each length
01769     QValueList<int>  *recurDays[4];
01770 
01771   public:
01772     MonthlyData(const Recurrence* r, const QDate &date)
01773              : recurrence(r), year(date.year()), month(date.month()-1), day(date.day())
01774              { recurDays[0] = &days28;
01775                recurDays[1] = &days29;
01776                recurDays[2] = &days30;
01777                recurDays[3] = &days31;
01778                varies = (recurrence->doesRecur() == rMonthlyPos)
01779                         ? true : recurrence->getMonthlyDayDays(days31, 31);
01780              }
01781     const QValueList<int>* dayList() const {
01782             if (!varies)
01783               return &days31;
01784             QDate startOfMonth(year, month + 1, 1);
01785             int daysInMonth = startOfMonth.daysInMonth();
01786             QValueList<int>* days = recurDays[daysInMonth - 28];
01787             if (recurrence->doesRecur() == rMonthlyPos)
01788               recurrence->getMonthlyPosDays(*days, daysInMonth, startOfMonth.dayOfWeek());
01789             else if (days->isEmpty())
01790               recurrence->getMonthlyDayDays(*days, daysInMonth);
01791             return days;
01792     }
01793     int    yearMonth() const    { return year*12 + month; }
01794     void   addMonths(int diff)  { month += diff;  year += month / 12;  month %= 12; }
01795     QDate  date() const         { return QDate(year, month + 1, day); }
01796 };
01797 
01798 int Recurrence::monthlyCalc(PeriodFunc func, QDate &enddate) const
01799 {
01800   if (recurs == rMonthlyPos && rMonthPositions.isEmpty()
01801   ||  recurs == rMonthlyDay && rMonthDays.isEmpty())
01802     return 0;
01803 
01804   MonthlyData data(this, mRecurStart.date());
01805   switch (func) {
01806     case END_DATE_AND_COUNT:
01807       return monthlyCalcEndDate(enddate, data);
01808     case COUNT_TO_DATE:
01809       return monthlyCalcToDate(enddate, data);
01810     case NEXT_AFTER_DATE:
01811       return monthlyCalcNextAfter(enddate, data);
01812   }
01813   return 0;
01814 }
01815 
01816 int Recurrence::monthlyCalcEndDate(QDate &enddate, MonthlyData &data) const
01817 {
01818   uint countTogo = rDuration;
01819   int  countGone = 0;
01820   QValueList<int>::ConstIterator it;
01821   const QValueList<int>* days = data.dayList();
01822 
01823   if (data.day > 1) {
01824     // Check what remains of the start month
01825     for (it = days->begin();  it != days->end();  ++it) {
01826       if (*it >= data.day) {
01827         ++countGone;
01828         if (--countTogo == 0) {
01829           data.day = *it;
01830           break;
01831         }
01832       }
01833     }
01834     if (countTogo) {
01835       data.day = 1;
01836       data.addMonths(rFreq);
01837     }
01838   }
01839   if (countTogo) {
01840     if (data.varies) {
01841       // The number of recurrence days varies from month to month,
01842       // so we need to check month by month.
01843       for ( ; ; ) {
01844         days = data.dayList();
01845         uint n = days->count();    // number of recurrence days in this month
01846         if (n >= countTogo)
01847           break;
01848         countTogo -= n;
01849         countGone += n;
01850         data.addMonths(rFreq);
01851       }
01852     } else {
01853       // The number of recurrences is the same every month,
01854       // so skip the month-by-month check.
01855       // Skip the remaining whole months, but leave at least
01856       // 1 recurrence remaining, in order to get its date.
01857       int daysPerMonth = days->count();
01858       int wholeMonths = (countTogo - 1) / daysPerMonth;
01859       data.addMonths(wholeMonths * rFreq);
01860       countGone += wholeMonths * daysPerMonth;
01861       countTogo -= wholeMonths * daysPerMonth;
01862     }
01863     if (countTogo) {
01864       // Check the last month in the recurrence
01865       for (it = days->begin();  it != days->end();  ++it) {
01866         ++countGone;
01867         if (--countTogo == 0) {
01868           data.day = *it;
01869           break;
01870         }
01871       }
01872     }
01873   }
01874   enddate = data.date();
01875   return countGone;
01876 }
01877 
01878 int Recurrence::monthlyCalcToDate(const QDate &enddate, MonthlyData &data) const
01879 {
01880   int countGone = 0;
01881   int countMax  = (rDuration > 0) ? rDuration : INT_MAX;
01882   int endYear  = enddate.year();
01883   int endMonth = enddate.month() - 1;     // zero-based
01884   int endDay   = enddate.day();
01885   int endYearMonth = endYear*12 + endMonth;
01886   QValueList<int>::ConstIterator it;
01887   const QValueList<int>* days = data.dayList();
01888 
01889   if (data.day > 1) {
01890     // Check what remains of the start month
01891     for (it = days->begin();  it != days->end();  ++it) {
01892       if (*it >= data.day) {
01893         if (data.yearMonth() == endYearMonth && *it > endDay)
01894           return countGone;
01895         if (++countGone >= countMax)
01896           return countMax;
01897       }
01898     }
01899     data.day = 1;
01900     data.addMonths(rFreq);
01901   }
01902 
01903   if (data.varies) {
01904     // The number of recurrence days varies from month to month,
01905     // so we need to check month by month.
01906     while (data.yearMonth() < endYearMonth) {
01907       countGone += data.dayList()->count();
01908       if (countGone >= countMax)
01909         return countMax;
01910       data.addMonths(rFreq);
01911     }
01912     days = data.dayList();
01913   } else {
01914     // The number of recurrences is the same every month,
01915     // so skip the month-by-month check.
01916     // Skip the remaining whole months.
01917     int daysPerMonth = days->count();
01918     int wholeMonths = endYearMonth - data.yearMonth();
01919     countGone += (wholeMonths / rFreq) * daysPerMonth;
01920     if (countGone >= countMax)
01921       return countMax;
01922     if (wholeMonths % rFreq)
01923       return countGone;      // end year isn't a recurrence year
01924     data.year  = endYear;
01925     data.month = endMonth;
01926   }
01927 
01928   // Check the last month in the recurrence
01929   for (it = days->begin();  it != days->end();  ++it) {
01930     if (*it > endDay)
01931       return countGone;
01932     if (++countGone >= countMax)
01933       return countMax;
01934   }
01935   return countGone;
01936 }
01937 
01938 int Recurrence::monthlyCalcNextAfter(QDate &enddate, MonthlyData &data) const
01939 {
01940   uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX;
01941   int countGone = 0;
01942   int endYear = enddate.year();
01943   int endDay  = enddate.day();
01944   int endYearMonth = endYear*12 + enddate.month() - 1;
01945   QValueList<int>::ConstIterator it;
01946   const QValueList<int>* days = data.dayList();
01947 
01948   if (data.day > 1) {
01949     // Check what remains of the start month
01950     for (it = days->begin();  it != days->end();  ++it) {
01951       if (*it >= data.day) {
01952         ++countGone;
01953         if (data.yearMonth() == endYearMonth && *it > endDay) {
01954           data.day = *it;
01955           goto ex;
01956         }
01957         if (--countTogo == 0)
01958           return 0;
01959       }
01960     }
01961     data.day = 1;
01962     data.addMonths(rFreq);
01963   }
01964 
01965   if (data.varies) {
01966     // The number of recurrence days varies from month to month,
01967     // so we need to check month by month.
01968     while (data.yearMonth() <= endYearMonth) {
01969       days = data.dayList();
01970       uint n = days->count();    // number of recurrence days in this month
01971       if (data.yearMonth() == endYearMonth && days->last() > endDay)
01972         break;
01973       if (n >= countTogo)
01974         return 0;
01975       countGone += n;
01976       countTogo -= n;
01977       data.addMonths(rFreq);
01978     }
01979     days = data.dayList();
01980   } else {
01981     // The number of recurrences is the same every month,
01982     // so skip the month-by-month check.
01983     // Skip the remaining whole months to at least end year/month.
01984     int daysPerMonth = days->count();
01985     int elapsed = endYearMonth - data.yearMonth();
01986     int recurMonths = (elapsed + rFreq - 1) / rFreq;
01987     if (elapsed % rFreq == 0  &&  days->last() <= endDay)
01988       ++recurMonths;    // required month is after endYearMonth
01989     if (recurMonths) {
01990       int n = recurMonths * daysPerMonth;
01991       if (static_cast<uint>(n) > countTogo)
01992         return 0;     // reached end of recurrence
01993       countTogo -= n;
01994       countGone += n;
01995       data.addMonths(recurMonths * rFreq);
01996     }
01997   }
01998 
01999   // Check the last month in the recurrence
02000   for (it = days->begin();  it != days->end();  ++it) {
02001     ++countGone;
02002     if (data.yearMonth() > endYearMonth  ||  *it > endDay) {
02003       data.day = *it;
02004       break;
02005     }
02006     if (--countTogo == 0)
02007       return 0;
02008   }
02009 ex:
02010   enddate = data.date();
02011   return countGone;
02012 }
02013 
02014 
02015 /* Find count and, depending on 'func', the end date of an annual recurrence by date.
02016  * Reply = total number of occurrences up to 'enddate', or 0 if error.
02017  * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
02018  * recurrence end date.
02019  *
02020  * WARNING: These methods currently do not cater for day of month < -28
02021  *          (which would need different months to be treated differently).
02022  */
02023 class Recurrence::YearlyMonthData
02024 {
02025   public:
02026     const Recurrence *recurrence;
02027     int               year;          // current year
02028     int               month;         // current month 1..12
02029     int               day;           // current day of month 1..31
02030     bool              leapyear;      // true if February 29th recurs and current year is a leap year
02031     bool              feb29;         // true if February 29th recurs
02032 
02033   private:
02034     QValueList<int>   months;        // recurring months in non-leap years  1..12
02035     QValueList<int>   leapMonths;    // recurring months in leap years  1..12
02036 
02037   public:
02038     YearlyMonthData(const Recurrence* r, const QDate &date, int d)
02039           : recurrence(r), year(date.year()), month(date.month()), day(d ? d : date.day())
02040           { feb29 = recurrence->getYearlyMonthMonths(day, months, leapMonths);
02041             leapyear = feb29 && QDate::leapYear(year);
02042           }
02043     const QValueList<int>* monthList() const
02044                          { return leapyear ? &leapMonths : &months; }
02045     const QValueList<int>* leapMonthList() const  { return &leapMonths; }
02046     QDate            date() const  { if (day > 0) return QDate(year, month, day);
02047                                      return QDate(year, month, QDate(year, month, 1).daysInMonth() + day + 1);
02048                                    }
02049 };
02050 
02051 int Recurrence::yearlyMonthCalc(PeriodFunc func, QDate &enddate) const
02052 {
02053   if (rYearNums.isEmpty())
02054     return 0;
02055   YearlyMonthData data(this, mRecurStart.date(), (rMonthDays.count() ? *rMonthDays.getFirst() : 0));
02056   switch (func) {
02057     case END_DATE_AND_COUNT:
02058       return yearlyMonthCalcEndDate(enddate, data);
02059     case COUNT_TO_DATE:
02060       return yearlyMonthCalcToDate(enddate, data);
02061     case NEXT_AFTER_DATE:
02062       return yearlyMonthCalcNextAfter(enddate, data);
02063   }
02064   return 0;
02065 }
02066 
02067 // Find total count and end date of an annual recurrence by date.
02068 // Reply = total number of occurrences.
02069 int Recurrence::yearlyMonthCalcEndDate(QDate &enddate, YearlyMonthData &data) const
02070 {
02071   uint countTogo = rDuration;
02072   int  countGone = 0;
02073   QValueList<int>::ConstIterator it;
02074   const QValueList<int>* mons = data.monthList();   // get recurring months for this year
02075 
02076   if (data.month > 1) {
02077     // Check what remains of the start year
02078     for (it = mons->begin();  it != mons->end();  ++it) {
02079       if (*it >= data.month) {
02080         ++countGone;
02081         if (--countTogo == 0) {
02082           data.month = *it;
02083           if (data.month == 2 && data.feb29 && !data.leapyear) {
02084             // The recurrence should end on February 29th, but it's a non-leap year
02085             switch (mFeb29YearlyType) {
02086               case rFeb28:
02087                 data.day = 28;
02088                 break;
02089               case rMar1:
02090                 data.month = 3;
02091                 data.day   = 1;
02092                 break;
02093               case rFeb29:
02094                 break;
02095             }
02096           }
02097           break;
02098         }
02099       }
02100     }
02101     if (countTogo) {
02102       data.month = 1;
02103       data.year += rFreq;
02104     }
02105   }
02106   if (countTogo) {
02107     if (data.feb29 && mFeb29YearlyType == rFeb29) {
02108       // The number of recurrences is different on leap years,
02109       // so check year-by-year.
02110       for ( ; ; ) {
02111         mons = data.monthList();
02112         uint n = mons->count();
02113         if (n >= countTogo)
02114           break;
02115         countTogo -= n;
02116         countGone += n;
02117         data.year += rFreq;
02118       }
02119     } else {
02120       // The number of recurrences is the same every year,
02121       // so skip the year-by-year check.
02122       // Skip the remaining whole years, but leave at least
02123       // 1 recurrence remaining, in order to get its date.
02124       int monthsPerYear = mons->count();
02125       int wholeYears = (countTogo - 1) / monthsPerYear;
02126       data.year += wholeYears * rFreq;
02127       countGone += wholeYears * monthsPerYear;
02128       countTogo -= wholeYears * monthsPerYear;
02129     }
02130     if (countTogo) {
02131       // Check the last year in the recurrence
02132       for (it = mons->begin();  it != mons->end();  ++it) {
02133         ++countGone;
02134         if (--countTogo == 0) {
02135           data.month = *it;
02136           if (data.month == 2 && data.feb29 && !QDate::leapYear(data.year)) {
02137             // The recurrence should end on February 29th, but it's a non-leap year
02138             switch (mFeb29YearlyType) {
02139               case rFeb28:
02140                 data.day = 28;
02141                 break;
02142               case rMar1:
02143                 data.month = 3;
02144                 data.day   = 1;
02145                 break;
02146               case rFeb29:
02147                 break;
02148             }
02149           }
02150           break;
02151         }
02152       }
02153     }
02154   }
02155   enddate = data.date();
02156   return countGone;
02157 }
02158 
02159 // Find count of an annual recurrence by date.
02160 // Reply = total number of occurrences up to 'enddate'.
02161 int Recurrence::yearlyMonthCalcToDate(const QDate &enddate, YearlyMonthData &data) const
02162 {
02163   int countGone = 0;
02164   int countMax  = (rDuration > 0) ? rDuration : INT_MAX;
02165   int endYear  = enddate.year();
02166   int endMonth = enddate.month();
02167   int endDay   = enddate.day();
02168   if (data.day < 0) {
02169     // The end day of the month is relative to the end of the month.
02170     if (endDay < enddate.daysInMonth() + data.day + 1) {
02171       if (--endMonth == 0) {
02172         endMonth = 12;
02173         --endYear;
02174       }
02175     }
02176   }
02177   else if (endDay < data.day) {
02178     /* The end day of the month is earlier than the recurrence day of the month.
02179      * If Feb 29th recurs and:
02180      * 1) it recurs on Feb 28th in non-leap years, don't adjust the end month
02181      *    if enddate is Feb 28th on a non-leap year.
02182      * 2) it recurs on Mar 1st in non-leap years, allow the end month to be
02183      *    adjusted to February, to simplify calculations.
02184      */
02185     if (data.feb29  &&  !QDate::leapYear(endYear)
02186     &&  mFeb29YearlyType == rFeb28  &&  endDay == 28  &&  endMonth == 2) {
02187     }
02188     else if (--endMonth == 0) {
02189       endMonth = 12;
02190       --endYear;
02191     }
02192   }
02193   QValueList<int>::ConstIterator it;
02194   const QValueList<int>* mons = data.monthList();
02195 
02196   if (data.month > 1) {
02197     // Check what remains of the start year
02198     for (it = mons->begin();  it != mons->end();  ++it) {
02199       if (*it >= data.month) {
02200         if (data.year == endYear && *it > endMonth)
02201           return countGone;
02202         if (++countGone >= countMax)
02203           return countMax;
02204       }
02205     }
02206     data.month = 1;
02207     data.year += rFreq;
02208   }
02209   if (data.feb29 && mFeb29YearlyType == rFeb29) {
02210     // The number of recurrences is different on leap years,
02211     // so check year-by-year.
02212     while (data.year < endYear) {
02213       countGone += data.monthList()->count();
02214       if (countGone >= countMax)
02215         return countMax;
02216       data.year += rFreq;
02217     }
02218     mons = data.monthList();
02219   } else {
02220     // The number of recurrences is the same every year,
02221     // so skip the year-by-year check.
02222     // Skip the remaining whole years.
02223     int monthsPerYear = mons->count();
02224     int wholeYears = endYear - data.year;
02225     countGone += (wholeYears / rFreq) * monthsPerYear;
02226     if (countGone >= countMax)
02227       return countMax;
02228     if (wholeYears % rFreq)
02229       return countGone;      // end year isn't a recurrence year
02230     data.year = endYear;
02231   }
02232 
02233   // Check the last year in the recurrence
02234   for (it = mons->begin();  it != mons->end();  ++it) {
02235     if (*it > endMonth)
02236       return countGone;
02237     if (++countGone >= countMax)
02238       return countMax;
02239   }
02240   return countGone;
02241 }
02242 
02243 // Find count and date of first recurrence after 'enddate' of an annual recurrence by date.
02244 // Reply = total number of occurrences up to 'enddate'.
02245 int Recurrence::yearlyMonthCalcNextAfter(QDate &enddate, YearlyMonthData &data) const
02246 {
02247   uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX;
02248   int  countGone = 0;
02249   int endYear  = enddate.year();
02250   int endMonth = enddate.month();
02251   int endDay   = enddate.day();
02252   bool mar1TooEarly = false;
02253   bool feb28ok      = false;
02254   if (data.day < 0) {
02255     // The end day of the month is relative to the end of the month.
02256     if (endDay < enddate.daysInMonth() + data.day + 1) {
02257       if (--endMonth == 0) {
02258         endMonth = 12;
02259         --endYear;
02260       }
02261     }
02262   }
02263   else if (endDay < data.day) {
02264     if (data.feb29 && mFeb29YearlyType == rMar1 && endMonth == 3)
02265       mar1TooEarly = true;
02266     if (data.feb29 && mFeb29YearlyType == rFeb28 && endMonth == 2 && endDay == 28)
02267       feb28ok = true;
02268     else if (--endMonth == 0) {
02269       endMonth = 12;
02270       --endYear;
02271     }
02272   }
02273   QValueList<int>::ConstIterator it;
02274   const QValueList<int>* mons = data.monthList();
02275 
02276   if (data.month > 1) {
02277     // Check what remains of the start year
02278     for (it = mons->begin();  it != mons->end();  ++it) {
02279       if (*it >= data.month) {
02280         ++countGone;
02281         if (data.year == endYear
02282         &&  (   *it > endMonth && (*it > 3 || !mar1TooEarly)
02283              || *it == 2 && feb28ok && data.leapyear)) {
02284           if (*it == 2 && data.feb29 && !data.leapyear) {
02285             // The next recurrence should be on February 29th, but it's a non-leap year
02286             switch (mFeb29YearlyType) {
02287               case rFeb28:
02288                 data.month = 2;
02289                 data.day   = 28;
02290                 break;
02291               case rMar1:
02292                 data.month = 3;
02293                 data.day   = 1;
02294                 break;
02295               case rFeb29:   // impossible in this context!
02296                 break;
02297             }
02298           }
02299           else
02300             data.month = *it;
02301           goto ex;
02302         }
02303         if (--countTogo == 0)
02304           return 0;
02305       }
02306     }
02307     data.month = 1;
02308     data.year += rFreq;
02309   }
02310 
02311   if (data.feb29 && mFeb29YearlyType == rFeb29) {
02312     // The number of recurrences is different on leap years,
02313     // so check year-by-year.
02314     while (data.year <= endYear) {
02315       mons = data.monthList();
02316       if (data.year == endYear && mons->last() > endMonth)
02317         break;
02318       uint n = mons->count();
02319       if (n >= countTogo)
02320         break;
02321       countTogo -= n;
02322       countGone += n;
02323       data.year += rFreq;
02324     }
02325     mons = data.monthList();
02326   } else {
02327     // The number of recurrences is the same every year,
02328     // so skip the year-by-year check.
02329     // Skip the remaining whole years to at least endYear.
02330     int monthsPerYear = mons->count();
02331     int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
02332     if ((endYear - data.year)%rFreq == 0
02333     &&  mons->last() <= endMonth)
02334       ++recurYears;    // required year is after endYear
02335     if (recurYears) {
02336       int n = recurYears * monthsPerYear;
02337       if (static_cast<uint>(n) > countTogo)
02338         return 0;     // reached end of recurrence
02339       countTogo -= n;
02340       countGone += n;
02341       data.year += recurYears * rFreq;
02342     }
02343   }
02344 
02345   // Check the last year in the recurrence
02346   for (it = mons->begin();  it != mons->end();  ++it) {
02347     ++countGone;
02348     if (data.year > endYear
02349     ||  (   *it > endMonth && (*it > 3 || !mar1TooEarly)
02350          || *it == 2 && feb28ok && QDate::leapYear(data.year))) {
02351       if (*it == 2 && data.feb29 && !QDate::leapYear(data.year)) {
02352         // The next recurrence should be on February 29th, but it's a non-leap year
02353         switch (mFeb29YearlyType) {
02354           case rFeb28:
02355             data.month = 2;
02356             data.day   = 28;
02357             break;
02358           case rMar1:
02359             data.month = 3;
02360             data.day   = 1;
02361             break;
02362           case rFeb29:   // impossible in this context!
02363             break;
02364         }
02365       }
02366       else
02367         data.month = *it;
02368       break;
02369     }
02370     if (--countTogo == 0)
02371       return 0;
02372   }
02373 ex:
02374   enddate = data.date();
02375   return countGone;
02376 }
02377 
02378 
02379 /* Find count and, depending on 'func', the end date of an annual recurrence by date.
02380  * Reply = total number of occurrences up to 'enddate', or 0 if error.
02381  * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
02382  * recurrence end date.
02383  */
02384 class Recurrence::YearlyPosData
02385 {
02386   public:
02387     const Recurrence *recurrence;
02388     int               year;          // current year
02389     int               month;         // current month 1..12
02390     int               day;           // current day of month 1..31
02391     int               daysPerMonth;  // number of days which recur each month, or -1 if variable
02392     int               count;         // number of days which recur each year, or -1 if variable
02393     bool              varies;        // true if number of days varies from year to year
02394 
02395   private:
02396     mutable QValueList<int> days;
02397 
02398   public:
02399     YearlyPosData(const Recurrence* r, const QDate &date)
02400           : recurrence(r), year(date.year()), month(date.month()), day(date.day()), count(-1)
02401             { if ((daysPerMonth = r->countMonthlyPosDays()) > 0)
02402                 count = daysPerMonth * r->yearNums().count();
02403               varies = (daysPerMonth < 0);
02404             }
02405     const QValueList<int>* dayList() const {
02406             QDate startOfMonth(year, month, 1);
02407             recurrence->getMonthlyPosDays(days, startOfMonth.daysInMonth(), startOfMonth.dayOfWeek());
02408             return &days;
02409     }
02410     int    yearMonth() const    { return year*12 + month - 1; }
02411     void   addMonths(int diff)  { month += diff - 1;  year += month / 12;  month = month % 12 + 1; }
02412     QDate  date() const         { return QDate(year, month, day); }
02413 };
02414 
02415 int Recurrence::yearlyPosCalc(PeriodFunc func, QDate &enddate) const
02416 {
02417   if (rYearNums.isEmpty() || rMonthPositions.isEmpty())
02418     return 0;
02419   YearlyPosData data(this, mRecurStart.date());
02420   switch (func) {
02421     case END_DATE_AND_COUNT:
02422       return yearlyPosCalcEndDate(enddate, data);
02423     case COUNT_TO_DATE:
02424       return yearlyPosCalcToDate(enddate, data);
02425     case NEXT_AFTER_DATE:
02426       return yearlyPosCalcNextAfter(enddate, data);
02427   }
02428   return 0;
02429 }
02430 
02431 int Recurrence::yearlyPosCalcEndDate(QDate &enddate, YearlyPosData &data) const
02432 {
02433   uint countTogo = rDuration;
02434   int  countGone = 0;
02435   QValueList<int>::ConstIterator id;
02436   const QValueList<int>* days;
02437 
02438   if (data.month > 1 || data.day > 1) {
02439     // Check what remains of the start year
02440     for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
02441       if (*im.current() >= data.month) {
02442         // Check what remains of the start month
02443         if (data.day > 1 || data.varies
02444         ||  static_cast<uint>(data.daysPerMonth) >= countTogo) {
02445           data.month = *im.current();
02446           days = data.dayList();
02447           for (id = days->begin();  id != days->end();  ++id) {
02448             if (*id >= data.day) {
02449               ++countGone;
02450               if (--countTogo == 0) {
02451                 data.month = *im.current();
02452                 data.day = *id;
02453                 goto ex;
02454               }
02455             }
02456           }
02457           data.day = 1;
02458         } else {
02459           // The number of days per month is constant, so skip
02460           // the whole month.
02461           countTogo -= data.daysPerMonth;
02462           countGone += data.daysPerMonth;
02463         }
02464       }
02465     }
02466     data.month = 1;
02467     data.year += rFreq;
02468   }
02469 
02470   if (data.varies) {
02471     // The number of recurrences varies from year to year.
02472     for ( ; ; ) {
02473       for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
02474         data.month = *im.current();
02475         days = data.dayList();
02476         int n = days->count();
02477         if (static_cast<uint>(n) >= countTogo) {
02478           // Check the last month in the recurrence
02479           for (id = days->begin();  id != days->end();  ++id) {
02480             ++countGone;
02481             if (--countTogo == 0) {
02482               data.day = *id;
02483               goto ex;
02484             }
02485           }
02486         }
02487         countTogo -= n;
02488         countGone += n;
02489       }
02490       data.year += rFreq;
02491     }
02492   } else {
02493     // The number of recurrences is the same every year,
02494     // so skip the year-by-year check.
02495     // Skip the remaining whole years, but leave at least
02496     // 1 recurrence remaining, in order to get its date.
02497     int wholeYears = (countTogo - 1) / data.count;
02498     data.year += wholeYears * rFreq;
02499     countGone += wholeYears * data.count;
02500     countTogo -= wholeYears * data.count;
02501 
02502     // Check the last year in the recurrence.
02503     for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
02504       if (static_cast<uint>(data.daysPerMonth) >= countTogo) {
02505         // Check the last month in the recurrence
02506         data.month = *im.current();
02507         days = data.dayList();
02508         for (id = days->begin();  id != days->end();  ++id) {
02509           ++countGone;
02510           if (--countTogo == 0) {
02511             data.day = *id;
02512             goto ex;
02513           }
02514         }
02515       }
02516       countTogo -= data.daysPerMonth;
02517       countGone += data.daysPerMonth;
02518     }
02519     data.year += rFreq;
02520   }
02521 ex:
02522   enddate = data.date();
02523   return countGone;
02524 }
02525 
02526 int Recurrence::yearlyPosCalcToDate(const QDate &enddate, YearlyPosData &data) const
02527 {
02528   int countGone = 0;
02529   int countMax  = (rDuration > 0) ? rDuration : INT_MAX;
02530   int endYear  = enddate.year();
02531   int endMonth = enddate.month();
02532   int endDay   = enddate.day();
02533   if (endDay < data.day && --endMonth == 0) {
02534     endMonth = 12;
02535     --endYear;
02536   }
02537   int endYearMonth = endYear*12 + endMonth;
02538   QValueList<int>::ConstIterator id;
02539   const QValueList<int>* days;
02540 
02541   if (data.month > 1 || data.day > 1) {
02542     // Check what remains of the start year
02543     for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
02544       if (*im.current() >= data.month) {
02545         data.month = *im.current();
02546         if (data.yearMonth() > endYearMonth)
02547           return countGone;
02548         // Check what remains of the start month
02549         bool lastMonth = (data.yearMonth() == endYearMonth);
02550         if (lastMonth || data.day > 1 || data.varies) {
02551           days = data.dayList();
02552           if (lastMonth || data.day > 1) {
02553             for (id = days->begin();  id != days->end();  ++id) {
02554               if (*id >= data.day) {
02555                 if (lastMonth && *id > endDay)
02556                   return countGone;
02557                 if (++countGone >= countMax)
02558                   return countMax;
02559               }
02560             }
02561           } else {
02562             countGone += days->count();
02563             if (countGone >= countMax)
02564               return countMax;
02565           }
02566           data.day = 1;
02567         } else {
02568           // The number of days per month is constant, so skip
02569           // the whole month.
02570           countGone += data.daysPerMonth;
02571           if (countGone >= countMax)
02572             return countMax;
02573         }
02574       }
02575     }
02576     data.month = 1;
02577     data.year += rFreq;
02578   }
02579 
02580   if (data.varies) {
02581     // The number of recurrences varies from year to year.
02582     for ( ; ; ) {
02583       for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
02584         data.month = *im.current();
02585         days = data.dayList();
02586         if (data.yearMonth() >= endYearMonth) {
02587           if (data.yearMonth() > endYearMonth)
02588             return countGone;
02589           // Check the last month in the recurrence
02590           for (id = days->begin();  id != days->end();  ++id) {
02591             if (*id > endDay)
02592               return countGone;
02593             if (++countGone >= countMax)
02594               return countMax;
02595           }
02596         } else {
02597           countGone += days->count();
02598           if (countGone >= countMax)
02599             return countMax;
02600         }
02601       }
02602       data.year += rFreq;
02603     }
02604   } else {
02605     // The number of recurrences is the same every year,
02606     // so skip the year-by-year check.
02607     // Skip the remaining whole years, but leave at least
02608     // 1 recurrence remaining, in order to get its date.
02609     int wholeYears = endYear - data.year;
02610     countGone += (wholeYears / rFreq) * data.count;
02611     if (countGone >= countMax)
02612       return countMax;
02613     if (wholeYears % rFreq)
02614       return countGone;      // end year isn't a recurrence year
02615     data.year = endYear;
02616 
02617     // Check the last year in the recurrence.
02618     for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
02619       data.month = *im.current();
02620       if (data.month >= endMonth) {
02621         if (data.month > endMonth)
02622           return countGone;
02623         // Check the last month in the recurrence
02624         days = data.dayList();
02625         for (id = days->begin();  id != days->end();  ++id) {
02626           if (*id > endDay)
02627             return countGone;
02628           if (++countGone >= countMax)
02629             return countMax;
02630         }
02631       } else {
02632         countGone += data.daysPerMonth;
02633         if (countGone >= countMax)
02634           return countMax;
02635       }
02636     }
02637   }
02638   return countGone;
02639 }
02640 
02641 int Recurrence::yearlyPosCalcNextAfter(QDate &enddate, YearlyPosData &data) const
02642 {
02643   uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX;
02644   int  countGone = 0;
02645   int endYear  = enddate.year();
02646   int endMonth = enddate.month();
02647   int endDay   = enddate.day();
02648   if (endDay < data.day && --endMonth == 0) {
02649     endMonth = 12;
02650     --endYear;
02651   }
02652   int endYearMonth = endYear*12 + endMonth;
02653   QValueList<int>::ConstIterator id;
02654   const QValueList<int>* days;
02655 
02656   if (data.varies) {
02657     // The number of recurrences varies from year to year.
02658     for ( ; ; ) {
02659       // Check the next year
02660       for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
02661         if (*im.current() >= data.month) {
02662           // Check the next month
02663           data.month = *im.current();
02664           int ended = data.yearMonth() - endYearMonth;
02665           days = data.dayList();
02666           if (ended >= 0 || data.day > 1) {
02667             // This is the start or end month, so check each day
02668             for (id = days->begin();  id != days->end();  ++id) {
02669               if (*id >= data.day) {
02670                 ++countGone;
02671                 if (ended > 0 || (ended == 0 && *id > endDay)) {
02672                   data.day = *id;
02673                   goto ex;
02674                 }
02675                 if (--countTogo == 0)
02676                   return 0;
02677               }
02678             }
02679           } else {
02680             // Skip the whole month
02681             uint n = days->count();
02682             if (n >= countTogo)
02683               return 0;
02684             countGone += n;
02685           }
02686           data.day = 1;      // we've checked the start month now
02687         }
02688       }
02689       data.month = 1;        // we've checked the start year now
02690       data.year += rFreq;
02691     }
02692   } else {
02693     // The number of recurrences is the same every year.
02694     if (data.month > 1 || data.day > 1) {
02695       // Check what remains of the start year
02696       for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
02697         if (*im.current() >= data.month) {
02698           // Check what remains of the start month
02699           data.month = *im.current();
02700           int ended = data.yearMonth() - endYearMonth;
02701           if (ended >= 0 || data.day > 1) {
02702             // This is the start or end month, so check each day
02703             days = data.dayList();
02704             for (id = days->begin();  id != days->end();  ++id) {
02705               if (*id >= data.day) {
02706                 ++countGone;
02707                 if (ended > 0 || (ended == 0 && *id > endDay)) {
02708                   data.day = *id;
02709                   goto ex;
02710                 }
02711                 if (--countTogo == 0)
02712                   return 0;
02713               }
02714             }
02715             data.day = 1;      // we've checked the start month now
02716           } else {
02717             // Skip the whole month.
02718             if (static_cast<uint>(data.daysPerMonth) >= countTogo)
02719               return 0;
02720             countGone += data.daysPerMonth;
02721           }
02722         }
02723       }
02724       data.year += rFreq;
02725     }
02726     // Skip the remaining whole years to at least endYear.
02727     int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
02728     if ((endYear - data.year)%rFreq == 0
02729     &&  *rYearNums.getLast() <= endMonth)
02730       ++recurYears;    // required year is after endYear
02731     if (recurYears) {
02732       int n = recurYears * data.count;
02733       if (static_cast<uint>(n) > countTogo)
02734         return 0;     // reached end of recurrence
02735       countTogo -= n;
02736       countGone += n;
02737       data.year += recurYears * rFreq;
02738     }
02739 
02740     // Check the last year in the recurrence
02741     for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
02742       data.month = *im.current();
02743       int ended = data.yearMonth() - endYearMonth;
02744       if (ended >= 0) {
02745         // This is the end month, so check each day
02746         days = data.dayList();
02747         for (id = days->begin();  id != days->end();  ++id) {
02748           ++countGone;
02749           if (ended > 0 || (ended == 0 && *id > endDay)) {
02750             data.day = *id;
02751             goto ex;
02752           }
02753           if (--countTogo == 0)
02754             return 0;
02755         }
02756       } else {
02757         // Skip the whole month.
02758         if (static_cast<uint>(data.daysPerMonth) >= countTogo)
02759           return 0;
02760         countGone += data.daysPerMonth;
02761       }
02762     }
02763   }
02764 ex:
02765   enddate = data.date();
02766   return countGone;
02767 }
02768 
02769 
02770 /* Find count and, depending on 'func', the end date of an annual recurrence by day.
02771  * Reply = total number of occurrences up to 'enddate', or 0 if error.
02772  * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
02773  * recurrence end date.
02774  */
02775 class Recurrence::YearlyDayData
02776 {
02777   public:
02778     int    year;       // current year
02779     int    day;        // current day of year 1..366
02780     bool   varies;     // true if day 366 recurs
02781 
02782   private:
02783     int    daycount;
02784 
02785   public:
02786     YearlyDayData(const Recurrence* r, const QDate &date)
02787       : year( date.year() ), day( date.dayOfYear() ),
02788         varies( *r->yearNums().getLast() == 366 ),
02789         daycount( r->yearNums().count() ) { }
02790     bool  leapYear() const       { return QDate::leapYear(year); }
02791     int   dayCount() const       { return daycount - (varies && !QDate::leapYear(year) ? 1 : 0); }
02792     bool  isMaxDayCount() const  { return !varies || QDate::leapYear(year); }
02793     QDate date() const           { return QDate(year, 1, 1).addDays(day - 1); }
02794 };
02795 
02796 int Recurrence::yearlyDayCalc(PeriodFunc func, QDate &enddate) const
02797 {
02798   if (rYearNums.isEmpty())
02799     return 0;
02800   YearlyDayData data(this, mRecurStart.date());
02801   switch (func) {
02802     case END_DATE_AND_COUNT:
02803       return yearlyDayCalcEndDate(enddate, data);
02804     case COUNT_TO_DATE:
02805       return yearlyDayCalcToDate(enddate, data);
02806     case NEXT_AFTER_DATE:
02807       return yearlyDayCalcNextAfter(enddate, data);
02808   }
02809   return 0;
02810 }
02811 
02812 int Recurrence::yearlyDayCalcEndDate(QDate &enddate, YearlyDayData &data) const
02813 {
02814   uint countTogo = rDuration;
02815   int countGone = 0;
02816 
02817   if (data.day > 1) {
02818     // Check what remains of the start year
02819     bool leapOK = data.isMaxDayCount();
02820     for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
02821       int d = *it.current();
02822       if (d >= data.day && (leapOK || d < 366)) {
02823         ++countGone;
02824         if (--countTogo == 0) {
02825           data.day = d;
02826           goto ex;
02827         }
02828       }
02829     }
02830     data.day = 1;
02831     data.year += rFreq;
02832   }
02833 
02834   if (data.varies) {
02835     // The number of recurrences is different in leap years,
02836     // so check year-by-year.
02837     for ( ; ; ) {
02838       uint n = data.dayCount();
02839       if (n >= countTogo)
02840         break;
02841       countTogo -= n;
02842       countGone += n;
02843       data.year += rFreq;
02844     }
02845   } else {
02846     // The number of recurrences is the same every year,
02847     // so skip the year-by-year check.
02848     // Skip the remaining whole years, but leave at least
02849     // 1 recurrence remaining, in order to get its date.
02850     int daysPerYear = rYearNums.count();
02851     int wholeYears = (countTogo - 1) / daysPerYear;
02852     data.year += wholeYears * rFreq;
02853     countGone += wholeYears * daysPerYear;
02854     countTogo -= wholeYears * daysPerYear;
02855   }
02856   if (countTogo) {
02857     // Check the last year in the recurrence
02858     for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
02859       ++countGone;
02860       if (--countTogo == 0) {
02861         data.day = *it.current();
02862         break;
02863       }
02864     }
02865   }
02866 ex:
02867   enddate = data.date();
02868   return countGone;
02869 }
02870 
02871 int Recurrence::yearlyDayCalcToDate(const QDate &enddate, YearlyDayData &data) const
02872 {
02873   int countGone = 0;
02874   int countMax  = (rDuration > 0) ? rDuration : INT_MAX;
02875   int endYear = enddate.year();
02876   int endDay  = enddate.dayOfYear();
02877 
02878   if (data.day > 1) {
02879     // Check what remains of the start year
02880     bool leapOK = data.isMaxDayCount();
02881     for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
02882       int d = *it.current();
02883       if (d >= data.day && (leapOK || d < 366)) {
02884         if (data.year == endYear && d > endDay)
02885           return countGone;
02886         if (++countGone >= countMax)
02887           return countMax;
02888       }
02889     }
02890     data.day = 1;
02891     data.year += rFreq;
02892   }
02893 
02894   if (data.varies) {
02895     // The number of recurrences is different in leap years,
02896     // so check year-by-year.
02897     while (data.year < endYear) {
02898       uint n = data.dayCount();
02899       countGone += n;
02900       if (countGone >= countMax)
02901         return countMax;
02902       data.year += rFreq;
02903     }
02904     if (data.year > endYear)
02905       return countGone;
02906   } else {
02907     // The number of recurrences is the same every year.
02908     // Skip the remaining whole years.
02909     int wholeYears = endYear - data.year;
02910     countGone += (wholeYears / rFreq) * rYearNums.count();
02911     if (countGone >= countMax)
02912       return countMax;
02913     if (wholeYears % rFreq)
02914       return countGone;      // end year isn't a recurrence year
02915     data.year = endYear;
02916   }
02917 
02918   if (data.year <= endYear) {
02919     // Check the last year in the recurrence
02920     for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
02921       if (*it.current() > endDay)
02922         return countGone;
02923       if (++countGone >= countMax)
02924         return countMax;
02925     }
02926   }
02927   return countGone;
02928 }
02929 
02930 int Recurrence::yearlyDayCalcNextAfter(QDate &enddate, YearlyDayData &data) const
02931 {
02932   uint countTogo = (rDuration > 0) ? rDuration : UINT_MAX;
02933   int  countGone = 0;
02934   int  endYear = enddate.year();
02935   int  endDay  = enddate.dayOfYear();
02936 
02937   if (data.day > 1) {
02938     // Check what remains of the start year
02939     bool leapOK = data.isMaxDayCount();
02940     for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
02941       int d = *it.current();
02942       if (d >= data.day && (leapOK || d < 366)) {
02943         ++countGone;
02944         if (data.year == endYear && d > endDay) {
02945           data.day = d;
02946           goto ex;
02947         }
02948         if (--countTogo == 0)
02949           return 0;
02950       }
02951     }
02952     data.day = 1;
02953     data.year += rFreq;
02954   }
02955 
02956   if (data.varies) {
02957     // The number of recurrences is different in leap years,
02958     // so check year-by-year.
02959     while (data.year <= endYear) {
02960       uint n = data.dayCount();
02961       if (data.year == endYear && *rYearNums.getLast() > endDay)
02962         break;
02963       if (n >= countTogo)
02964         break;
02965       countTogo -= n;
02966       countGone += n;
02967       data.year += rFreq;
02968     }
02969   } else {
02970     // The number of recurrences is the same every year,
02971     // so skip the year-by-year check.
02972     // Skip the remaining whole years to at least endYear.
02973     int daysPerYear = rYearNums.count();
02974     int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
02975     if ((endYear - data.year)%rFreq == 0
02976     &&  *rYearNums.getLast() <= endDay)
02977       ++recurYears;    // required year is after endYear
02978     if (recurYears) {
02979       int n = recurYears * daysPerYear;
02980       if (static_cast<uint>(n) > countTogo)
02981         return 0;     // reached end of recurrence
02982       countTogo -= n;
02983       countGone += n;
02984       data.year += recurYears * rFreq;
02985     }
02986   }
02987 
02988   // Check the last year in the recurrence
02989   for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
02990     ++countGone;
02991     int d = *it.current();
02992     if (data.year > endYear || d > endDay) {
02993       data.day = d;
02994       break;
02995     }
02996     if (--countTogo == 0)
02997       return 0;
02998   }
02999 ex:
03000   enddate = data.date();
03001   return countGone;
03002 }
03003 
03004 // Get the days in this month which recur, in numerical order.
03005 // Parameters: daysInMonth = number of days in this month
03006 //             startDayOfWeek = day of week for first day of month.
03007 void Recurrence::getMonthlyPosDays(QValueList<int> &list, int daysInMonth, int startDayOfWeek) const
03008 {
03009   list.clear();
03010   int endDayOfWeek = (startDayOfWeek + daysInMonth - 2) % 7 + 1;
03011   // Go through the list, compiling a bit list of actual day numbers
03012   Q_UINT32 days = 0;
03013   for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) {
03014     int weeknum = pos.current()->rPos - 1;   // get 0-based week number
03015     QBitArray &rdays = pos.current()->rDays;
03016     if (pos.current()->negative) {
03017       // nth days before the end of the month
03018       for (uint i = 1; i <= 7; ++i) {
03019         if (rdays.testBit(i - 1)) {
03020           int day = daysInMonth - weeknum*7 - (endDayOfWeek - i + 7) % 7;
03021           if (day > 0)
03022             days |= 1 << (day - 1);
03023         }
03024       }
03025     } else {
03026       // nth days after the start of the month
03027       for (uint i = 1; i <= 7; ++i) {
03028         if (rdays.testBit(i - 1)) {
03029           int day = 1 + weeknum*7 + (i - startDayOfWeek + 7) % 7;
03030           if (day <= daysInMonth)
03031             days |= 1 << (day - 1);
03032         }
03033       }
03034     }
03035   }
03036   // Compile the ordered list
03037   Q_UINT32 mask = 1;
03038   for (int i = 0; i < daysInMonth; mask <<= 1, ++i) {
03039     if (days & mask)
03040       list.append(i + 1);
03041   }
03042 }
03043 
03044 // Get the number of days in the month which recur.
03045 // Reply = -1 if the number varies from month to month.
03046 int Recurrence::countMonthlyPosDays() const
03047 {
03048   int count = 0;
03049   Q_UINT8 positive[5] = { 0, 0, 0, 0, 0 };
03050   Q_UINT8 negative[4] = { 0, 0, 0, 0 };
03051   for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) {
03052     int weeknum = pos.current()->rPos;
03053     Q_UINT8* wk;
03054     if (pos.current()->negative) {
03055       // nth days before the end of the month
03056       if (weeknum > 4)
03057         return -1;       // days in 5th week are often missing
03058       wk = &negative[4 - weeknum];
03059     } else {
03060       // nth days after the start of the month
03061       if (weeknum > 4)
03062         return -1;       // days in 5th week are often missing
03063       wk = &positive[weeknum - 1];
03064     }
03065     QBitArray &rdays = pos.current()->rDays;
03066     for (uint i = 0; i < 7; ++i) {
03067       if (rdays.testBit(i)) {
03068         ++count;
03069         *wk |= (1 << i);
03070       }
03071     }
03072   }
03073   // Check for any possible days which could be duplicated by
03074   // a positive and a negative position.
03075   for (int i = 0; i < 4; ++i) {
03076     if (negative[i] & (positive[i] | positive[i+1]))
03077       return -1;
03078   }
03079   return count;
03080 }
03081 
03082 // Get the days in this month which recur, in numerical order.
03083 // Reply = true if day numbers varies from month to month.
03084 bool Recurrence::getMonthlyDayDays(QValueList<int> &list, int daysInMonth) const
03085 {
03086   list.clear();
03087   bool variable = false;
03088   Q_UINT32 days = 0;
03089   for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
03090     int day = *it.current();
03091     if (day > 0) {
03092       // date in the month
03093       if (day <= daysInMonth)
03094         days |= 1 << (day - 1);
03095       if (day > 28 && day <= 31)
03096         variable = true;     // this date does not appear in some months
03097     } else if (day < 0) {
03098       // days before the end of the month
03099       variable = true;       // this date varies depending on the month length
03100       day = daysInMonth + day;    // zero-based day of month
03101       if (day >= 0)
03102         days |= 1 << day;
03103     }
03104   }
03105   // Compile the ordered list
03106   Q_UINT32 mask = 1;
03107   for (int i = 0; i < daysInMonth; mask <<= 1, ++i) {
03108     if (days & mask)
03109       list.append(i + 1);
03110   }
03111   return variable;
03112 }
03113 
03114 // Get the months which recur, in numerical order, for both leap years and non-leap years.
03115 // N.B. If February 29th recurs on March 1st in non-leap years, February (not March) is
03116 // included in the non-leap year month list.
03117 // Reply = true if February 29th also recurs.
03118 bool Recurrence::getYearlyMonthMonths(int day, QValueList<int> &list, QValueList<int> &leaplist) const
03119 {
03120   list.clear();
03121   leaplist.clear();
03122   bool feb29 = false;
03123   for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
03124     int month = *it.current();
03125     if (month == 2) {
03126       if (day <= 28) {
03127         list.append(month);     // date appears in February
03128         leaplist.append(month);
03129       }
03130       else if (day == 29) {
03131         // February 29th
03132         leaplist.append(month);
03133         switch (mFeb29YearlyType) {
03134           case rFeb28:
03135           case rMar1:
03136             list.append(2);
03137             break;
03138           case rFeb29:
03139             break;
03140         }
03141         feb29 = true;
03142       }
03143     }
03144     else if (day <= 30 || QDate(2000, month, 1).daysInMonth() == 31) {
03145       list.append(month);       // date appears in every month
03146       leaplist.append(month);
03147     }
03148   }
03149   return feb29;
03150 }
03151 
03152 /* From the recurrence day of the week list, get the earliest day in the
03153  * specified week which is >= the startDay.
03154  * Parameters:  startDay = 1..7 (Monday..Sunday)
03155  *              useWeekStart = true to end search at day before next rWeekStart
03156  *                           = false to search for a full 7 days
03157  * Reply = day of the week (1..7), or 0 if none found.
03158  */
03159 int Recurrence::getFirstDayInWeek(int startDay, bool useWeekStart) const
03160 {
03161   int last = ((useWeekStart ? rWeekStart : startDay) + 5)%7;
03162   for (int i = startDay - 1;  ;  i = (i + 1)%7) {
03163     if (rDays.testBit(i))
03164       return i + 1;
03165     if (i == last)
03166       return 0;
03167   }
03168 }
03169 
03170 /* From the recurrence day of the week list, get the latest day in the
03171  * specified week which is <= the endDay.
03172  * Parameters:  endDay = 1..7 (Monday..Sunday)
03173  *              useWeekStart = true to end search at rWeekStart
03174  *                           = false to search for a full 7 days
03175  * Reply = day of the week (1..7), or 0 if none found.
03176  */
03177 int Recurrence::getLastDayInWeek(int endDay, bool useWeekStart) const
03178 {
03179   int last = useWeekStart ? rWeekStart - 1 : endDay%7;
03180   for (int i = endDay - 1;  ;  i = (i + 6)%7) {
03181     if (rDays.testBit(i))
03182       return i + 1;
03183     if (i == last)
03184       return 0;
03185   }
03186 }
03187 
03188 /* From the recurrence monthly day number list or monthly day of week/week of
03189  * month list, get the earliest day in the specified month which is >= the
03190  * earliestDate.
03191  */
03192 QDate Recurrence::getFirstDateInMonth(const QDate &earliestDate) const
03193 {
03194   int earliestDay = earliestDate.day();
03195   int daysInMonth = earliestDate.daysInMonth();
03196   switch (recurs) {
03197     case rMonthlyDay: {
03198       int minday = daysInMonth + 1;
03199       for (QPtrListIterator<int> it(rMonthDays);  it.current();  ++it) {
03200         int day = *it.current();
03201         if (day < 0)
03202           day = daysInMonth + day + 1;
03203         if (day >= earliestDay  &&  day < minday)
03204           minday = day;
03205       }
03206       if (minday <= daysInMonth)
03207         return earliestDate.addDays(minday - earliestDay);
03208       break;
03209     }
03210     case rMonthlyPos:
03211     case rYearlyPos: {
03212       QDate monthBegin(earliestDate.addDays(1 - earliestDay));
03213       QValueList<int> dayList;
03214       getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek());
03215       for (QValueList<int>::ConstIterator id = dayList.begin();  id != dayList.end();  ++id) {
03216         if (*id >= earliestDay)
03217           return monthBegin.addDays(*id - 1);
03218       }
03219       break;
03220     }
03221   }
03222   return QDate();
03223 }
03224 
03225 /* From the recurrence monthly day number list or monthly day of week/week of
03226  * month list, get the latest day in the specified month which is <= the
03227  * latestDate.
03228  */
03229 QDate Recurrence::getLastDateInMonth(const QDate &latestDate) const
03230 {
03231   int latestDay = latestDate.day();
03232   int daysInMonth = latestDate.daysInMonth();
03233   switch (recurs) {
03234     case rMonthlyDay: {
03235       int maxday = -1;
03236       for (QPtrListIterator<int> it(rMonthDays);  it.current();  ++it) {
03237         int day = *it.current();
03238         if (day < 0)
03239           day = daysInMonth + day + 1;
03240         if (day <= latestDay  &&  day > maxday)
03241           maxday = day;
03242       }
03243       if (maxday > 0)
03244         return QDate(latestDate.year(), latestDate.month(), maxday);
03245       break;
03246     }
03247     case rMonthlyPos:
03248     case rYearlyPos: {
03249       QDate monthBegin(latestDate.addDays(1 - latestDay));
03250       QValueList<int> dayList;
03251       getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek());
03252       for (QValueList<int>::ConstIterator id = dayList.fromLast();  id != dayList.end();  --id) {
03253         if (*id <= latestDay)
03254           return monthBegin.addDays(*id - 1);
03255       }
03256       break;
03257     }
03258   }
03259   return QDate();
03260 }
03261 
03262 /* From the recurrence yearly month list or yearly day list, get the earliest
03263  * month or day in the specified year which is >= the earliestDate.
03264  * Note that rYearNums is sorted in numerical order.
03265  */
03266 QDate Recurrence::getFirstDateInYear(const QDate &earliestDate) const
03267 {
03268   QPtrListIterator<int> it(rYearNums);
03269   switch (recurs) {
03270     case rYearlyMonth: {
03271       int day = recurStart().date().day();
03272       int earliestYear  = earliestDate.year();
03273       int earliestMonth = earliestDate.month();
03274       int earliestDay   = earliestDate.day();
03275       if (earliestDay > day) {
03276         // The earliest date is later in the month than the recurrence date,
03277         // so skip to the next month before starting to check
03278         if (++earliestMonth > 12)
03279           return QDate();
03280       }
03281       for ( ;  it.current();  ++it) {
03282         int month = *it.current();
03283         if (month >= earliestMonth) {
03284           if (day <= 28  ||  QDate::isValid(earliestYear, month, day))
03285             return QDate(earliestYear, month, day);
03286           if (day == 29  &&  month == 2) {
03287             // It's a recurrence on February 29th, in a non-leap year
03288             switch (mFeb29YearlyType) {
03289               case rMar1:
03290                 return QDate(earliestYear, 3, 1);
03291               case rFeb28:
03292                 if (earliestDay <= 28)
03293                   return QDate(earliestYear, 2, 28);
03294                 break;
03295               case rFeb29:
03296                 break;
03297             }
03298           }
03299         }
03300       }
03301       break;
03302     }
03303     case rYearlyPos: {
03304       QValueList<int> dayList;
03305       int earliestYear  = earliestDate.year();
03306       int earliestMonth = earliestDate.month();
03307       int earliestDay   = earliestDate.day();
03308       for ( ;  it.current();  ++it) {
03309         int month = *it.current();
03310         if (month >= earliestMonth) {
03311           QDate monthBegin(earliestYear, month, 1);
03312           getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek());
03313           for (QValueList<int>::ConstIterator id = dayList.begin();  id != dayList.end();  ++id) {
03314             if (*id >= earliestDay)
03315               return monthBegin.addDays(*id - 1);
03316           }
03317           earliestDay = 1;
03318         }
03319       }
03320       break;
03321     }
03322     case rYearlyDay: {
03323       int earliestDay = earliestDate.dayOfYear();
03324       for ( ;  it.current();  ++it) {
03325         int day = *it.current();
03326         if (day >= earliestDay && (day <= 365 || day <= earliestDate.daysInYear()))
03327           return earliestDate.addDays(day - earliestDay);
03328       }
03329       break;
03330     }
03331   }
03332   return QDate();
03333 }
03334 
03335 /* From the recurrence yearly month list or yearly day list, get the latest
03336  * month or day in the specified year which is <= the latestDate.
03337  * Note that rYearNums is sorted in numerical order.
03338  */
03339 QDate Recurrence::getLastDateInYear(const QDate &latestDate) const
03340 {
03341   QPtrListIterator<int> it(rYearNums);
03342   switch (recurs) {
03343     case rYearlyMonth: {
03344       int day = recurStart().date().day();
03345       int latestYear  = latestDate.year();
03346       int latestMonth = latestDate.month();
03347       if (latestDate.day() > day) {
03348         // The latest date is earlier in the month than the recurrence date,
03349         // so skip to the previous month before starting to check
03350         if (--latestMonth <= 0)
03351           return QDate();
03352       }
03353       for (it.toLast();  it.current();  --it) {
03354         int month = *it.current();
03355         if (month <= latestMonth) {
03356           if (day <= 28  ||  QDate::isValid(latestYear, month, day))
03357             return QDate(latestYear, month, day);
03358           if (day == 29  &&  month == 2) {
03359             // It's a recurrence on February 29th, in a non-leap year
03360             switch (mFeb29YearlyType) {
03361               case rMar1:
03362                 if (latestMonth >= 3)
03363                   return QDate(latestYear, 3, 1);
03364                 break;
03365               case rFeb28:
03366                 return QDate(latestYear, 2, 28);
03367               case rFeb29:
03368                 break;
03369             }
03370           }
03371         }
03372       }
03373       break;
03374     }
03375     case rYearlyPos: {
03376       QValueList<int> dayList;
03377       int latestYear  = latestDate.year();
03378       int latestMonth = latestDate.month();
03379       int latestDay   = latestDate.day();
03380       for (it.toLast();  it.current();  --it) {
03381         int month = *it.current();
03382         if (month <= latestMonth) {
03383           QDate monthBegin(latestYear, month, 1);
03384           getMonthlyPosDays(dayList, monthBegin.daysInMonth(), monthBegin.dayOfWeek());
03385           for (QValueList<int>::ConstIterator id = dayList.fromLast();  id != dayList.end();  --id) {
03386             if (*id <= latestDay)
03387               return monthBegin.addDays(*id - 1);
03388           }
03389           latestDay = 31;
03390         }
03391       }
03392       break;
03393     }
03394     case rYearlyDay: {
03395       int latestDay = latestDate.dayOfYear();
03396       for (it.toLast();  it.current();  --it) {
03397         int day = *it.current();
03398         if (day <= latestDay)
03399           return latestDate.addDays(day - latestDay);
03400       }
03401       break;
03402     }
03403   }
03404   return QDate();
03405 }
03406 
03407 void Recurrence::dump() const
03408 {
03409   kdDebug() << "Recurrence::dump():" << endl;
03410 
03411   kdDebug() << "  type: " << recurs << endl;
03412   
03413   kdDebug() << "  rDays: " << endl;
03414   int i;
03415   for( i = 0; i < 7; ++i ) {
03416     kdDebug() << "    " << i << ": "
03417               << ( rDays.testBit( i ) ? "true" : "false" ) << endl;
03418   }
03419 }
KDE Logo
This file is part of the documentation for libkcal Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat May 1 11:36:22 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003