summaryrefslogtreecommitdiffabout
path: root/libkcal
authorzautrix <zautrix>2005-02-18 09:36:33 (UTC)
committer zautrix <zautrix>2005-02-18 09:36:33 (UTC)
commit59a00e603e2ba14885e81c09daf3bb89859ff176 (patch) (unidiff)
treee257e5b0e338675c5448f9d4c57eadf68ed5e76b /libkcal
parentb1e885da3b0a083121f940485d9c0eea53ff5139 (diff)
downloadkdepimpi-59a00e603e2ba14885e81c09daf3bb89859ff176.zip
kdepimpi-59a00e603e2ba14885e81c09daf3bb89859ff176.tar.gz
kdepimpi-59a00e603e2ba14885e81c09daf3bb89859ff176.tar.bz2
recur fixes
Diffstat (limited to 'libkcal') (more/less context) (ignore whitespace changes)
-rw-r--r--libkcal/recurrence.cpp12
1 files changed, 9 insertions, 3 deletions
diff --git a/libkcal/recurrence.cpp b/libkcal/recurrence.cpp
index 5181eaf..8a175c9 100644
--- a/libkcal/recurrence.cpp
+++ b/libkcal/recurrence.cpp
@@ -1,3284 +1,3290 @@
1/* 1/*
2 This file is part of libkcal. 2 This file is part of libkcal.
3 Copyright (c) 1998 Preston Brown 3 Copyright (c) 1998 Preston Brown
4 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> 4 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
5 Copyright (c) 2002 David Jarvie <software@astrojar.org.uk> 5 Copyright (c) 2002 David Jarvie <software@astrojar.org.uk>
6 6
7 This library is free software; you can redistribute it and/or 7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public 8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either 9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version. 10 version 2 of the License, or (at your option) any later version.
11 11
12 This library is distributed in the hope that it will be useful, 12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details. 15 Library General Public License for more details.
16 16
17 You should have received a copy of the GNU Library General Public License 17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to 18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. 20 Boston, MA 02111-1307, USA.
21*/ 21*/
22 22
23#include <limits.h> 23#include <limits.h>
24 24
25#include <kdebug.h> 25#include <kdebug.h>
26#include <kglobal.h> 26#include <kglobal.h>
27#include <klocale.h> 27#include <klocale.h>
28 28
29#include "incidence.h" 29#include "incidence.h"
30 30
31#include "recurrence.h" 31#include "recurrence.h"
32 32
33using namespace KCal; 33using namespace KCal;
34 34
35Recurrence::Feb29Type Recurrence::mFeb29YearlyDefaultType = Recurrence::rMar1; 35Recurrence::Feb29Type Recurrence::mFeb29YearlyDefaultType = Recurrence::rMar1;
36 36
37 37
38Recurrence::Recurrence(Incidence *parent, int compatVersion) 38Recurrence::Recurrence(Incidence *parent, int compatVersion)
39: recurs(rNone), // by default, it's not a recurring event 39: recurs(rNone), // by default, it's not a recurring event
40 rWeekStart(1), // default is Monday 40 rWeekStart(1), // default is Monday
41 rDays(7), 41 rDays(7),
42 mFloats(parent ? parent->doesFloat() : false), 42 mFloats(parent ? parent->doesFloat() : false),
43 mRecurReadOnly(false), 43 mRecurReadOnly(false),
44 mRecurExDatesCount(0), 44 mRecurExDatesCount(0),
45 mFeb29YearlyType(mFeb29YearlyDefaultType), 45 mFeb29YearlyType(mFeb29YearlyDefaultType),
46 mCompatVersion(compatVersion ? compatVersion : INT_MAX), 46 mCompatVersion(compatVersion ? compatVersion : INT_MAX),
47 mCompatRecurs(rNone), 47 mCompatRecurs(rNone),
48 mCompatDuration(0), 48 mCompatDuration(0),
49 mParent(parent) 49 mParent(parent)
50{ 50{
51 rMonthDays.setAutoDelete( true ); 51 rMonthDays.setAutoDelete( true );
52 rMonthPositions.setAutoDelete( true ); 52 rMonthPositions.setAutoDelete( true );
53 rYearNums.setAutoDelete( true ); 53 rYearNums.setAutoDelete( true );
54} 54}
55 55
56Recurrence::Recurrence(const Recurrence &r, Incidence *parent) 56Recurrence::Recurrence(const Recurrence &r, Incidence *parent)
57: recurs(r.recurs), 57: recurs(r.recurs),
58 rWeekStart(r.rWeekStart), 58 rWeekStart(r.rWeekStart),
59 rDays(r.rDays.copy()), 59 rDays(r.rDays.copy()),
60 rFreq(r.rFreq), 60 rFreq(r.rFreq),
61 rDuration(r.rDuration), 61 rDuration(r.rDuration),
62 rEndDateTime(r.rEndDateTime), 62 rEndDateTime(r.rEndDateTime),
63 mRecurStart(r.mRecurStart), 63 mRecurStart(r.mRecurStart),
64 mFloats(r.mFloats), 64 mFloats(r.mFloats),
65 mRecurReadOnly(r.mRecurReadOnly), 65 mRecurReadOnly(r.mRecurReadOnly),
66 mRecurExDatesCount(r.mRecurExDatesCount), 66 mRecurExDatesCount(r.mRecurExDatesCount),
67 mFeb29YearlyType(r.mFeb29YearlyType), 67 mFeb29YearlyType(r.mFeb29YearlyType),
68 mCompatVersion(r.mCompatVersion), 68 mCompatVersion(r.mCompatVersion),
69 mCompatRecurs(r.mCompatRecurs), 69 mCompatRecurs(r.mCompatRecurs),
70 mCompatDuration(r.mCompatDuration), 70 mCompatDuration(r.mCompatDuration),
71 mParent(parent) 71 mParent(parent)
72{ 72{
73 for (QPtrListIterator<rMonthPos> mp(r.rMonthPositions); mp.current(); ++mp) { 73 for (QPtrListIterator<rMonthPos> mp(r.rMonthPositions); mp.current(); ++mp) {
74 rMonthPos *tmp = new rMonthPos; 74 rMonthPos *tmp = new rMonthPos;
75 tmp->rPos = mp.current()->rPos; 75 tmp->rPos = mp.current()->rPos;
76 tmp->negative = mp.current()->negative; 76 tmp->negative = mp.current()->negative;
77 tmp->rDays = mp.current()->rDays.copy(); 77 tmp->rDays = mp.current()->rDays.copy();
78 rMonthPositions.append(tmp); 78 rMonthPositions.append(tmp);
79 } 79 }
80 for (QPtrListIterator<int> md(r.rMonthDays); md.current(); ++md) { 80 for (QPtrListIterator<int> md(r.rMonthDays); md.current(); ++md) {
81 int *tmp = new int; 81 int *tmp = new int;
82 *tmp = *md.current(); 82 *tmp = *md.current();
83 rMonthDays.append(tmp); 83 rMonthDays.append(tmp);
84 } 84 }
85 for (QPtrListIterator<int> yn(r.rYearNums); yn.current(); ++yn) { 85 for (QPtrListIterator<int> yn(r.rYearNums); yn.current(); ++yn) {
86 int *tmp = new int; 86 int *tmp = new int;
87 *tmp = *yn.current(); 87 *tmp = *yn.current();
88 rYearNums.append(tmp); 88 rYearNums.append(tmp);
89 } 89 }
90 rMonthDays.setAutoDelete( true ); 90 rMonthDays.setAutoDelete( true );
91 rMonthPositions.setAutoDelete( true ); 91 rMonthPositions.setAutoDelete( true );
92 rYearNums.setAutoDelete( true ); 92 rYearNums.setAutoDelete( true );
93} 93}
94 94
95Recurrence::~Recurrence() 95Recurrence::~Recurrence()
96{ 96{
97} 97}
98 98
99 99
100bool Recurrence::operator==( const Recurrence& r2 ) const 100bool Recurrence::operator==( const Recurrence& r2 ) const
101{ 101{
102 102
103 // the following line is obvious 103 // the following line is obvious
104 if ( recurs == rNone && r2.recurs == rNone ) 104 if ( recurs == rNone && r2.recurs == rNone )
105 return true; 105 return true;
106 // we need the above line, because two non recurring events may 106 // we need the above line, because two non recurring events may
107 // differ in the other settings, because one (or both) 107 // differ in the other settings, because one (or both)
108 // may be not initialized properly 108 // may be not initialized properly
109 109
110 if ( recurs != r2.recurs 110 if ( recurs != r2.recurs
111 || rFreq != r2.rFreq 111 || rFreq != r2.rFreq
112 || rDuration != r2.rDuration 112 || rDuration != r2.rDuration
113 || !rDuration && rEndDateTime != r2.rEndDateTime 113 || !rDuration && rEndDateTime != r2.rEndDateTime
114 || mRecurStart != r2.mRecurStart 114 || mRecurStart != r2.mRecurStart
115 || mFloats != r2.mFloats 115 || mFloats != r2.mFloats
116 || mRecurReadOnly != r2.mRecurReadOnly 116 || mRecurReadOnly != r2.mRecurReadOnly
117 || mRecurExDatesCount != r2.mRecurExDatesCount ) 117 || mRecurExDatesCount != r2.mRecurExDatesCount )
118 return false; 118 return false;
119 // no need to compare mCompat* and mParent 119 // no need to compare mCompat* and mParent
120 // OK to compare the pointers 120 // OK to compare the pointers
121 switch ( recurs ) 121 switch ( recurs )
122 { 122 {
123 case rWeekly: 123 case rWeekly:
124 return rDays == r2.rDays 124 return rDays == r2.rDays
125 && rWeekStart == r2.rWeekStart; 125 && rWeekStart == r2.rWeekStart;
126 case rMonthlyPos: { 126 case rMonthlyPos: {
127 QPtrList<rMonthPos> MonthPositions = rMonthPositions; 127 QPtrList<rMonthPos> MonthPositions = rMonthPositions;
128 QPtrList<rMonthPos> MonthPositions2 = r2.rMonthPositions; 128 QPtrList<rMonthPos> MonthPositions2 = r2.rMonthPositions;
129 if ( !MonthPositions.count() ) 129 if ( !MonthPositions.count() )
130 return false; 130 return false;
131 if ( !MonthPositions2.count() ) 131 if ( !MonthPositions2.count() )
132 return false; 132 return false;
133 return MonthPositions.first()->rPos == MonthPositions2.first()->rPos; 133 return MonthPositions.first()->rPos == MonthPositions2.first()->rPos;
134 } 134 }
135 case rMonthlyDay: { 135 case rMonthlyDay: {
136 QPtrList<int> MonthDays = rMonthDays ; 136 QPtrList<int> MonthDays = rMonthDays ;
137 QPtrList<int> MonthDays2 = r2.rMonthDays ; 137 QPtrList<int> MonthDays2 = r2.rMonthDays ;
138 if ( !MonthDays.count() ) 138 if ( !MonthDays.count() )
139 return false; 139 return false;
140 if ( !MonthDays2.count() ) 140 if ( !MonthDays2.count() )
141 return false; 141 return false;
142 return *MonthDays.first() == *MonthDays2.first() ; 142 return *MonthDays.first() == *MonthDays2.first() ;
143 } 143 }
144 case rYearlyPos: { 144 case rYearlyPos: {
145 145
146 QPtrList<int> YearNums = rYearNums; 146 QPtrList<int> YearNums = rYearNums;
147 QPtrList<int> YearNums2 = r2.rYearNums; 147 QPtrList<int> YearNums2 = r2.rYearNums;
148 if ( *YearNums.first() != *YearNums2.first() ) 148 if ( *YearNums.first() != *YearNums2.first() )
149 return false; 149 return false;
150 QPtrList<rMonthPos> MonthPositions = rMonthPositions; 150 QPtrList<rMonthPos> MonthPositions = rMonthPositions;
151 QPtrList<rMonthPos> MonthPositions2 = r2.rMonthPositions; 151 QPtrList<rMonthPos> MonthPositions2 = r2.rMonthPositions;
152 if ( !MonthPositions.count() ) 152 if ( !MonthPositions.count() )
153 return false; 153 return false;
154 if ( !MonthPositions2.count() ) 154 if ( !MonthPositions2.count() )
155 return false; 155 return false;
156 return MonthPositions.first()->rPos == MonthPositions2.first()->rPos; 156 return MonthPositions.first()->rPos == MonthPositions2.first()->rPos;
157 157
158 } 158 }
159 case rYearlyMonth: { 159 case rYearlyMonth: {
160 QPtrList<int> YearNums = rYearNums; 160 QPtrList<int> YearNums = rYearNums;
161 QPtrList<int> YearNums2 = r2.rYearNums; 161 QPtrList<int> YearNums2 = r2.rYearNums;
162 return ( *YearNums.first() == *YearNums2.first() && mFeb29YearlyType == r2.mFeb29YearlyType); 162 return ( *YearNums.first() == *YearNums2.first() && mFeb29YearlyType == r2.mFeb29YearlyType);
163 } 163 }
164 case rYearlyDay: { 164 case rYearlyDay: {
165 QPtrList<int> YearNums = rYearNums; 165 QPtrList<int> YearNums = rYearNums;
166 QPtrList<int> YearNums2 = r2.rYearNums; 166 QPtrList<int> YearNums2 = r2.rYearNums;
167 return ( *YearNums.first() == *YearNums2.first() ); 167 return ( *YearNums.first() == *YearNums2.first() );
168 } 168 }
169 case rNone: 169 case rNone:
170 case rMinutely: 170 case rMinutely:
171 case rHourly: 171 case rHourly:
172 case rDaily: 172 case rDaily:
173 default: 173 default:
174 return true; 174 return true;
175 } 175 }
176} 176}
177/* 177/*
178bool Recurrence::compareLists( const QPtrList<int> &l1 ,const QPtrList<int> &l2) 178bool Recurrence::compareLists( const QPtrList<int> &l1 ,const QPtrList<int> &l2)
179{ 179{
180 if ( l1.count() != l2.count() ) 180 if ( l1.count() != l2.count() )
181 return false; 181 return false;
182 int count = l1.count(); 182 int count = l1.count();
183 int i; 183 int i;
184 for ( i = 0; i < count ; ++i ) { 184 for ( i = 0; i < count ; ++i ) {
185 // if ( l1.at(i) != l2.at(i) ) 185 // if ( l1.at(i) != l2.at(i) )
186 return false; 186 return false;
187 qDebug("compüare "); 187 qDebug("compüare ");
188 } 188 }
189 return true; 189 return true;
190} 190}
191*/ 191*/
192QString Recurrence::recurrenceText() const 192QString Recurrence::recurrenceText() const
193{ 193{
194 QString recurText = i18n("No"); 194 QString recurText;
195 if ( recurs == Recurrence::rMinutely ) 195 if ( recurs == Recurrence::rMinutely )
196 recurText = i18n("minutely"); 196 recurText = i18n("minutely");
197 else if ( recurs == Recurrence::rHourly ) 197 else if ( recurs == Recurrence::rHourly )
198 recurText = i18n("hourly"); 198 recurText = i18n("hourly");
199 else if ( recurs == Recurrence::rDaily ) 199 else if ( recurs == Recurrence::rDaily )
200 recurText = i18n("daily"); 200 recurText = i18n("daily");
201 else if ( recurs == Recurrence::rWeekly ) 201 else if ( recurs == Recurrence::rWeekly )
202 recurText = i18n("weekly"); 202 recurText = i18n("weekly");
203 else if ( recurs == Recurrence::rMonthlyPos ) 203 else if ( recurs == Recurrence::rMonthlyPos )
204 recurText = i18n("monthly"); 204 recurText = i18n("monthly");
205 else if ( recurs == Recurrence::rMonthlyDay ) 205 else if ( recurs == Recurrence::rMonthlyDay )
206 recurText = i18n("day-monthly"); 206 recurText = i18n("monthly");
207 else if ( recurs == Recurrence::rYearlyMonth ) 207 else if ( recurs == Recurrence::rYearlyMonth )
208 recurText = i18n("month-yearly"); 208 recurText = i18n("yearly");
209 else if ( recurs == Recurrence::rYearlyDay ) 209 else if ( recurs == Recurrence::rYearlyDay )
210 recurText = i18n("day-yearly"); 210 recurText = i18n("day-yearly");
211 else if ( recurs == Recurrence::rYearlyPos ) 211 else if ( recurs == Recurrence::rYearlyPos )
212 recurText = i18n("position-yearly"); 212 recurText = i18n("position-yearly");
213 if ( !recurText.isEmpty() ) {
214 if ( rFreq > 1 ){
215 recurText = i18n("(%1) ").arg(rFreq ) + recurText;
216 }
217 } else
218 recurText = i18n("No");
213 return recurText; 219 return recurText;
214} 220}
215 221
216void Recurrence::setCompatVersion(int version) 222void Recurrence::setCompatVersion(int version)
217{ 223{
218 mCompatVersion = version ? version : INT_MAX; 224 mCompatVersion = version ? version : INT_MAX;
219} 225}
220 226
221ushort Recurrence::doesRecur() const 227ushort Recurrence::doesRecur() const
222{ 228{
223 return recurs; 229 return recurs;
224} 230}
225 231
226bool Recurrence::recursOnPure(const QDate &qd) const 232bool Recurrence::recursOnPure(const QDate &qd) const
227{ 233{
228 switch(recurs) { 234 switch(recurs) {
229 case rMinutely: 235 case rMinutely:
230 return recursSecondly(qd, rFreq*60); 236 return recursSecondly(qd, rFreq*60);
231 case rHourly: 237 case rHourly:
232 return recursSecondly(qd, rFreq*3600); 238 return recursSecondly(qd, rFreq*3600);
233 case rDaily: 239 case rDaily:
234 return recursDaily(qd); 240 return recursDaily(qd);
235 case rWeekly: 241 case rWeekly:
236 return recursWeekly(qd); 242 return recursWeekly(qd);
237 case rMonthlyPos: 243 case rMonthlyPos:
238 case rMonthlyDay: 244 case rMonthlyDay:
239 return recursMonthly(qd); 245 return recursMonthly(qd);
240 case rYearlyMonth: 246 case rYearlyMonth:
241 return recursYearlyByMonth(qd); 247 return recursYearlyByMonth(qd);
242 case rYearlyDay: 248 case rYearlyDay:
243 return recursYearlyByDay(qd); 249 return recursYearlyByDay(qd);
244 case rYearlyPos: 250 case rYearlyPos:
245 return recursYearlyByPos(qd); 251 return recursYearlyByPos(qd);
246 default: 252 default:
247 return false; 253 return false;
248 case rNone: 254 case rNone:
249 return false; 255 return false;
250 } // case 256 } // case
251 return false; 257 return false;
252} 258}
253 259
254bool Recurrence::recursAtPure(const QDateTime &dt) const 260bool Recurrence::recursAtPure(const QDateTime &dt) const
255{ 261{
256 switch(recurs) { 262 switch(recurs) {
257 case rMinutely: 263 case rMinutely:
258 return recursMinutelyAt(dt, rFreq); 264 return recursMinutelyAt(dt, rFreq);
259 case rHourly: 265 case rHourly:
260 return recursMinutelyAt(dt, rFreq*60); 266 return recursMinutelyAt(dt, rFreq*60);
261 default: 267 default:
262 if (dt.time() != mRecurStart.time()) 268 if (dt.time() != mRecurStart.time())
263 return false; 269 return false;
264 switch(recurs) { 270 switch(recurs) {
265 case rDaily: 271 case rDaily:
266 return recursDaily(dt.date()); 272 return recursDaily(dt.date());
267 case rWeekly: 273 case rWeekly:
268 return recursWeekly(dt.date()); 274 return recursWeekly(dt.date());
269 case rMonthlyPos: 275 case rMonthlyPos:
270 case rMonthlyDay: 276 case rMonthlyDay:
271 return recursMonthly(dt.date()); 277 return recursMonthly(dt.date());
272 case rYearlyMonth: 278 case rYearlyMonth:
273 return recursYearlyByMonth(dt.date()); 279 return recursYearlyByMonth(dt.date());
274 case rYearlyDay: 280 case rYearlyDay:
275 return recursYearlyByDay(dt.date()); 281 return recursYearlyByDay(dt.date());
276 case rYearlyPos: 282 case rYearlyPos:
277 return recursYearlyByPos(dt.date()); 283 return recursYearlyByPos(dt.date());
278 default: 284 default:
279 return false; 285 return false;
280 case rNone: 286 case rNone:
281 return false; 287 return false;
282 } 288 }
283 } // case 289 } // case
284 return false; 290 return false;
285} 291}
286 292
287QDate Recurrence::endDate() const 293QDate Recurrence::endDate() const
288{ 294{
289 int count = 0; 295 int count = 0;
290 QDate end; 296 QDate end;
291 if (recurs != rNone) { 297 if (recurs != rNone) {
292 if (rDuration < 0) 298 if (rDuration < 0)
293 return QDate(); // infinite recurrence 299 return QDate(); // infinite recurrence
294 if (rDuration == 0) 300 if (rDuration == 0)
295 return rEndDateTime.date(); 301 return rEndDateTime.date();
296 302
297 // The end date is determined by the recurrence count 303 // The end date is determined by the recurrence count
298 QDate dStart = mRecurStart.date(); 304 QDate dStart = mRecurStart.date();
299 switch (recurs) 305 switch (recurs)
300 { 306 {
301 case rMinutely: 307 case rMinutely:
302 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60).date(); 308 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60).date();
303 case rHourly: 309 case rHourly:
304 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600).date(); 310 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600).date();
305 case rDaily: 311 case rDaily:
306 return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq); 312 return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq);
307 313
308 case rWeekly: 314 case rWeekly:
309 count = weeklyCalc(END_DATE_AND_COUNT, end); 315 count = weeklyCalc(END_DATE_AND_COUNT, end);
310 break; 316 break;
311 case rMonthlyPos: 317 case rMonthlyPos:
312 case rMonthlyDay: 318 case rMonthlyDay:
313 count = monthlyCalc(END_DATE_AND_COUNT, end); 319 count = monthlyCalc(END_DATE_AND_COUNT, end);
314 break; 320 break;
315 case rYearlyMonth: 321 case rYearlyMonth:
316 count = yearlyMonthCalc(END_DATE_AND_COUNT, end); 322 count = yearlyMonthCalc(END_DATE_AND_COUNT, end);
317 break; 323 break;
318 case rYearlyDay: 324 case rYearlyDay:
319 count = yearlyDayCalc(END_DATE_AND_COUNT, end); 325 count = yearlyDayCalc(END_DATE_AND_COUNT, end);
320 break; 326 break;
321 case rYearlyPos: 327 case rYearlyPos:
322 count = yearlyPosCalc(END_DATE_AND_COUNT, end); 328 count = yearlyPosCalc(END_DATE_AND_COUNT, end);
323 break; 329 break;
324 default: 330 default:
325 // catch-all. Should never get here. 331 // catch-all. Should never get here.
326 kdDebug(5800) << "Control should never reach here in endDate()!" << endl; 332 kdDebug(5800) << "Control should never reach here in endDate()!" << endl;
327 break; 333 break;
328 } 334 }
329 } 335 }
330 if (!count) 336 if (!count)
331 return QDate(); // error - there is no recurrence 337 return QDate(); // error - there is no recurrence
332 return end; 338 return end;
333} 339}
334 340
335QDateTime Recurrence::endDateTime() const 341QDateTime Recurrence::endDateTime() const
336{ 342{
337 int count = 0; 343 int count = 0;
338 QDate end; 344 QDate end;
339 if (recurs != rNone) { 345 if (recurs != rNone) {
340 if (rDuration < 0) 346 if (rDuration < 0)
341 return QDateTime(); // infinite recurrence 347 return QDateTime(); // infinite recurrence
342 if (rDuration == 0) 348 if (rDuration == 0)
343 return rEndDateTime; 349 return rEndDateTime;
344 350
345 // The end date is determined by the recurrence count 351 // The end date is determined by the recurrence count
346 QDate dStart = mRecurStart.date(); 352 QDate dStart = mRecurStart.date();
347 switch (recurs) 353 switch (recurs)
348 { 354 {
349 case rMinutely: 355 case rMinutely:
350 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60); 356 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*60);
351 case rHourly: 357 case rHourly:
352 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600); 358 return mRecurStart.addSecs((rDuration-1+mRecurExDatesCount)*rFreq*3600);
353 case rDaily: 359 case rDaily:
354 return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq); 360 return dStart.addDays((rDuration-1+mRecurExDatesCount)*rFreq);
355 361
356 case rWeekly: 362 case rWeekly:
357 count = weeklyCalc(END_DATE_AND_COUNT, end); 363 count = weeklyCalc(END_DATE_AND_COUNT, end);
358 break; 364 break;
359 case rMonthlyPos: 365 case rMonthlyPos:
360 case rMonthlyDay: 366 case rMonthlyDay:
361 count = monthlyCalc(END_DATE_AND_COUNT, end); 367 count = monthlyCalc(END_DATE_AND_COUNT, end);
362 break; 368 break;
363 case rYearlyMonth: 369 case rYearlyMonth:
364 count = yearlyMonthCalc(END_DATE_AND_COUNT, end); 370 count = yearlyMonthCalc(END_DATE_AND_COUNT, end);
365 break; 371 break;
366 case rYearlyDay: 372 case rYearlyDay:
367 count = yearlyDayCalc(END_DATE_AND_COUNT, end); 373 count = yearlyDayCalc(END_DATE_AND_COUNT, end);
368 break; 374 break;
369 case rYearlyPos: 375 case rYearlyPos:
370 count = yearlyPosCalc(END_DATE_AND_COUNT, end); 376 count = yearlyPosCalc(END_DATE_AND_COUNT, end);
371 break; 377 break;
372 default: 378 default:
373 // catch-all. Should never get here. 379 // catch-all. Should never get here.
374 kdDebug(5800) << "Control should never reach here in endDate()!" << endl; 380 kdDebug(5800) << "Control should never reach here in endDate()!" << endl;
375 break; 381 break;
376 } 382 }
377 } 383 }
378 if (!count) 384 if (!count)
379 return QDateTime(); // error - there is no recurrence 385 return QDateTime(); // error - there is no recurrence
380 return QDateTime(end, mRecurStart.time()); 386 return QDateTime(end, mRecurStart.time());
381} 387}
382 388
383int Recurrence::durationTo(const QDate &date) const 389int Recurrence::durationTo(const QDate &date) const
384{ 390{
385 QDate d = date; 391 QDate d = date;
386 return recurCalc(COUNT_TO_DATE, d); 392 return recurCalc(COUNT_TO_DATE, d);
387} 393}
388 394
389int Recurrence::durationTo(const QDateTime &datetime) const 395int Recurrence::durationTo(const QDateTime &datetime) const
390{ 396{
391 QDateTime dt = datetime; 397 QDateTime dt = datetime;
392 return recurCalc(COUNT_TO_DATE, dt); 398 return recurCalc(COUNT_TO_DATE, dt);
393} 399}
394 400
395void Recurrence::unsetRecurs() 401void Recurrence::unsetRecurs()
396{ 402{
397 if (mRecurReadOnly) return; 403 if (mRecurReadOnly) return;
398 recurs = rNone; 404 recurs = rNone;
399 rMonthPositions.clear(); 405 rMonthPositions.clear();
400 rMonthDays.clear(); 406 rMonthDays.clear();
401 rYearNums.clear(); 407 rYearNums.clear();
402} 408}
403 409
404void Recurrence::setRecurStart(const QDateTime &start) 410void Recurrence::setRecurStart(const QDateTime &start)
405{ 411{
406 mRecurStart = start; 412 mRecurStart = start;
407 mFloats = false; 413 mFloats = false;
408 switch (recurs) 414 switch (recurs)
409 { 415 {
410 case rMinutely: 416 case rMinutely:
411 case rHourly: 417 case rHourly:
412 break; 418 break;
413 case rDaily: 419 case rDaily:
414 case rWeekly: 420 case rWeekly:
415 case rMonthlyPos: 421 case rMonthlyPos:
416 case rMonthlyDay: 422 case rMonthlyDay:
417 case rYearlyMonth: 423 case rYearlyMonth:
418 case rYearlyDay: 424 case rYearlyDay:
419 case rYearlyPos: 425 case rYearlyPos:
420 default: 426 default:
421 rEndDateTime.setTime(start.time()); 427 rEndDateTime.setTime(start.time());
422 break; 428 break;
423 } 429 }
424} 430}
425 431
426void Recurrence::setRecurStart(const QDate &start) 432void Recurrence::setRecurStart(const QDate &start)
427{ 433{
428 mRecurStart.setDate(start); 434 mRecurStart.setDate(start);
429 mRecurStart.setTime(QTime(0,0,0)); 435 mRecurStart.setTime(QTime(0,0,0));
430 switch (recurs) 436 switch (recurs)
431 { 437 {
432 case rMinutely: 438 case rMinutely:
433 case rHourly: 439 case rHourly:
434 break; 440 break;
435 case rDaily: 441 case rDaily:
436 case rWeekly: 442 case rWeekly:
437 case rMonthlyPos: 443 case rMonthlyPos:
438 case rMonthlyDay: 444 case rMonthlyDay:
439 case rYearlyMonth: 445 case rYearlyMonth:
440 case rYearlyDay: 446 case rYearlyDay:
441 case rYearlyPos: 447 case rYearlyPos:
442 default: 448 default:
443 mFloats = true; 449 mFloats = true;
444 break; 450 break;
445 } 451 }
446} 452}
447 453
448void Recurrence::setFloats(bool f) 454void Recurrence::setFloats(bool f)
449{ 455{
450 switch (recurs) 456 switch (recurs)
451 { 457 {
452 case rDaily: 458 case rDaily:
453 case rWeekly: 459 case rWeekly:
454 case rMonthlyPos: 460 case rMonthlyPos:
455 case rMonthlyDay: 461 case rMonthlyDay:
456 case rYearlyMonth: 462 case rYearlyMonth:
457 case rYearlyDay: 463 case rYearlyDay:
458 case rYearlyPos: 464 case rYearlyPos:
459 break; 465 break;
460 case rMinutely: 466 case rMinutely:
461 case rHourly: 467 case rHourly:
462 default: 468 default:
463 return; // can't set sub-daily to floating 469 return; // can't set sub-daily to floating
464 } 470 }
465 mFloats = f; 471 mFloats = f;
466 if (f) { 472 if (f) {
467 mRecurStart.setTime(QTime(0,0,0)); 473 mRecurStart.setTime(QTime(0,0,0));
468 rEndDateTime.setTime(QTime(0,0,0)); 474 rEndDateTime.setTime(QTime(0,0,0));
469 } 475 }
470} 476}
471 477
472int Recurrence::frequency() const 478int Recurrence::frequency() const
473{ 479{
474 return rFreq; 480 return rFreq;
475} 481}
476 482
477int Recurrence::duration() const 483int Recurrence::duration() const
478{ 484{
479 return rDuration; 485 return rDuration;
480} 486}
481 487
482void Recurrence::setDuration(int _rDuration) 488void Recurrence::setDuration(int _rDuration)
483{ 489{
484 if (mRecurReadOnly) return; 490 if (mRecurReadOnly) return;
485 if (_rDuration > 0) { 491 if (_rDuration > 0) {
486 rDuration = _rDuration; 492 rDuration = _rDuration;
487 // Compatibility mode is only needed when reading the calendar in ICalFormatImpl, 493 // Compatibility mode is only needed when reading the calendar in ICalFormatImpl,
488 // so explicitly setting the duration means no backwards compatibility is needed. 494 // so explicitly setting the duration means no backwards compatibility is needed.
489 mCompatDuration = 0; 495 mCompatDuration = 0;
490 } 496 }
491} 497}
492 498
493QString Recurrence::endDateStr(bool shortfmt) const 499QString Recurrence::endDateStr(bool shortfmt) const
494{ 500{
495 return KGlobal::locale()->formatDate(rEndDateTime.date(),shortfmt); 501 return KGlobal::locale()->formatDate(rEndDateTime.date(),shortfmt);
496} 502}
497 503
498const QBitArray &Recurrence::days() const 504const QBitArray &Recurrence::days() const
499{ 505{
500 return rDays; 506 return rDays;
501} 507}
502 508
503const QPtrList<Recurrence::rMonthPos> &Recurrence::monthPositions() const 509const QPtrList<Recurrence::rMonthPos> &Recurrence::monthPositions() const
504{ 510{
505 return rMonthPositions; 511 return rMonthPositions;
506} 512}
507 513
508const QPtrList<Recurrence::rMonthPos> &Recurrence::yearMonthPositions() const 514const QPtrList<Recurrence::rMonthPos> &Recurrence::yearMonthPositions() const
509{ 515{
510 return rMonthPositions; 516 return rMonthPositions;
511} 517}
512 518
513const QPtrList<int> &Recurrence::monthDays() const 519const QPtrList<int> &Recurrence::monthDays() const
514{ 520{
515 return rMonthDays; 521 return rMonthDays;
516} 522}
517 523
518void Recurrence::setMinutely(int _rFreq, int _rDuration) 524void Recurrence::setMinutely(int _rFreq, int _rDuration)
519{ 525{
520 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 526 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
521 return; 527 return;
522 setDailySub(rMinutely, _rFreq, _rDuration); 528 setDailySub(rMinutely, _rFreq, _rDuration);
523} 529}
524 530
525void Recurrence::setMinutely(int _rFreq, const QDateTime &_rEndDateTime) 531void Recurrence::setMinutely(int _rFreq, const QDateTime &_rEndDateTime)
526{ 532{
527 if (mRecurReadOnly) return; 533 if (mRecurReadOnly) return;
528 rEndDateTime = _rEndDateTime; 534 rEndDateTime = _rEndDateTime;
529 setDailySub(rMinutely, _rFreq, 0); 535 setDailySub(rMinutely, _rFreq, 0);
530} 536}
531 537
532void Recurrence::setHourly(int _rFreq, int _rDuration) 538void Recurrence::setHourly(int _rFreq, int _rDuration)
533{ 539{
534 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 540 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
535 return; 541 return;
536 setDailySub(rHourly, _rFreq, _rDuration); 542 setDailySub(rHourly, _rFreq, _rDuration);
537} 543}
538 544
539void Recurrence::setHourly(int _rFreq, const QDateTime &_rEndDateTime) 545void Recurrence::setHourly(int _rFreq, const QDateTime &_rEndDateTime)
540{ 546{
541 if (mRecurReadOnly) return; 547 if (mRecurReadOnly) return;
542 rEndDateTime = _rEndDateTime; 548 rEndDateTime = _rEndDateTime;
543 setDailySub(rHourly, _rFreq, 0); 549 setDailySub(rHourly, _rFreq, 0);
544} 550}
545 551
546void Recurrence::setDaily(int _rFreq, int _rDuration) 552void Recurrence::setDaily(int _rFreq, int _rDuration)
547{ 553{
548 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 554 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
549 return; 555 return;
550 setDailySub(rDaily, _rFreq, _rDuration); 556 setDailySub(rDaily, _rFreq, _rDuration);
551} 557}
552 558
553void Recurrence::setDaily(int _rFreq, const QDate &_rEndDate) 559void Recurrence::setDaily(int _rFreq, const QDate &_rEndDate)
554{ 560{
555 if (mRecurReadOnly) return; 561 if (mRecurReadOnly) return;
556 rEndDateTime.setDate(_rEndDate); 562 rEndDateTime.setDate(_rEndDate);
557 rEndDateTime.setTime(mRecurStart.time()); 563 rEndDateTime.setTime(mRecurStart.time());
558 setDailySub(rDaily, _rFreq, 0); 564 setDailySub(rDaily, _rFreq, 0);
559} 565}
560 566
561void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays, 567void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
562 int _rDuration, int _rWeekStart) 568 int _rDuration, int _rWeekStart)
563{ 569{
564 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 570 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
565 return; 571 return;
566 recurs = rWeekly; 572 recurs = rWeekly;
567 573
568 rFreq = _rFreq; 574 rFreq = _rFreq;
569 rDays = _rDays; 575 rDays = _rDays;
570 rWeekStart = _rWeekStart; 576 rWeekStart = _rWeekStart;
571 rDuration = _rDuration; 577 rDuration = _rDuration;
572 if (mCompatVersion < 310 && _rDuration > 0) { 578 if (mCompatVersion < 310 && _rDuration > 0) {
573 // Backwards compatibility for KDE < 3.1. 579 // Backwards compatibility for KDE < 3.1.
574 // rDuration was set to the number of time periods to recur, 580 // rDuration was set to the number of time periods to recur,
575 // with week start always on a Monday. 581 // with week start always on a Monday.
576 // Convert this to the number of occurrences. 582 // Convert this to the number of occurrences.
577 mCompatDuration = _rDuration; 583 mCompatDuration = _rDuration;
578 int weeks = ((mCompatDuration-1+mRecurExDatesCount)*7) + (7 - mRecurStart.date().dayOfWeek()); 584 int weeks = ((mCompatDuration-1+mRecurExDatesCount)*7) + (7 - mRecurStart.date().dayOfWeek());
579 QDate end(mRecurStart.date().addDays(weeks * rFreq)); 585 QDate end(mRecurStart.date().addDays(weeks * rFreq));
580 rDuration = INT_MAX; // ensure that weeklyCalc() does its job correctly 586 rDuration = INT_MAX; // ensure that weeklyCalc() does its job correctly
581 rDuration = weeklyCalc(COUNT_TO_DATE, end); 587 rDuration = weeklyCalc(COUNT_TO_DATE, end);
582 } else { 588 } else {
583 mCompatDuration = 0; 589 mCompatDuration = 0;
584 } 590 }
585 rMonthPositions.clear(); 591 rMonthPositions.clear();
586 rMonthDays.clear(); 592 rMonthDays.clear();
587 if (mParent) mParent->updated(); 593 if (mParent) mParent->updated();
588} 594}
589 595
590void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays, 596void Recurrence::setWeekly(int _rFreq, const QBitArray &_rDays,
591 const QDate &_rEndDate, int _rWeekStart) 597 const QDate &_rEndDate, int _rWeekStart)
592{ 598{
593 if (mRecurReadOnly) return; 599 if (mRecurReadOnly) return;
594 recurs = rWeekly; 600 recurs = rWeekly;
595 601
596 rFreq = _rFreq; 602 rFreq = _rFreq;
597 rDays = _rDays; 603 rDays = _rDays;
598 rWeekStart = _rWeekStart; 604 rWeekStart = _rWeekStart;
599 rEndDateTime.setDate(_rEndDate); 605 rEndDateTime.setDate(_rEndDate);
600 rEndDateTime.setTime(mRecurStart.time()); 606 rEndDateTime.setTime(mRecurStart.time());
601 rDuration = 0; // set to 0 because there is an end date 607 rDuration = 0; // set to 0 because there is an end date
602 mCompatDuration = 0; 608 mCompatDuration = 0;
603 rMonthPositions.clear(); 609 rMonthPositions.clear();
604 rMonthDays.clear(); 610 rMonthDays.clear();
605 rYearNums.clear(); 611 rYearNums.clear();
606 if (mParent) mParent->updated(); 612 if (mParent) mParent->updated();
607} 613}
608 614
609void Recurrence::setMonthly(short type, int _rFreq, int _rDuration) 615void Recurrence::setMonthly(short type, int _rFreq, int _rDuration)
610{ 616{
611 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 617 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
612 return; 618 return;
613 recurs = type; 619 recurs = type;
614 620
615 rFreq = _rFreq; 621 rFreq = _rFreq;
616 rDuration = _rDuration; 622 rDuration = _rDuration;
617 if (mCompatVersion < 310) 623 if (mCompatVersion < 310)
618 mCompatDuration = (_rDuration > 0) ? _rDuration : 0; 624 mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
619 rYearNums.clear(); 625 rYearNums.clear();
620 if (mParent) mParent->updated(); 626 if (mParent) mParent->updated();
621} 627}
622 628
623void Recurrence::setMonthly(short type, int _rFreq, 629void Recurrence::setMonthly(short type, int _rFreq,
624 const QDate &_rEndDate) 630 const QDate &_rEndDate)
625{ 631{
626 if (mRecurReadOnly) return; 632 if (mRecurReadOnly) return;
627 recurs = type; 633 recurs = type;
628 634
629 rFreq = _rFreq; 635 rFreq = _rFreq;
630 rEndDateTime.setDate(_rEndDate); 636 rEndDateTime.setDate(_rEndDate);
631 rEndDateTime.setTime(mRecurStart.time()); 637 rEndDateTime.setTime(mRecurStart.time());
632 rDuration = 0; // set to 0 because there is an end date 638 rDuration = 0; // set to 0 because there is an end date
633 mCompatDuration = 0; 639 mCompatDuration = 0;
634 rYearNums.clear(); 640 rYearNums.clear();
635 if (mParent) mParent->updated(); 641 if (mParent) mParent->updated();
636} 642}
637 643
638void Recurrence::addMonthlyPos(short _rPos, const QBitArray &_rDays) 644void Recurrence::addMonthlyPos(short _rPos, const QBitArray &_rDays)
639{ 645{
640 if (recurs == rMonthlyPos) 646 if (recurs == rMonthlyPos)
641 addMonthlyPos_(_rPos, _rDays); 647 addMonthlyPos_(_rPos, _rDays);
642} 648}
643 649
644void Recurrence::addMonthlyPos_(short _rPos, const QBitArray &_rDays) 650void Recurrence::addMonthlyPos_(short _rPos, const QBitArray &_rDays)
645{ 651{
646 if (mRecurReadOnly 652 if (mRecurReadOnly
647 || _rPos == 0 || _rPos > 5 || _rPos < -5) // invalid week number 653 || _rPos == 0 || _rPos > 5 || _rPos < -5) // invalid week number
648 return; 654 return;
649 655
650 for (rMonthPos* it = rMonthPositions.first(); it; it = rMonthPositions.next()) { 656 for (rMonthPos* it = rMonthPositions.first(); it; it = rMonthPositions.next()) {
651 int itPos = it->negative ? -it->rPos : it->rPos; 657 int itPos = it->negative ? -it->rPos : it->rPos;
652 if (_rPos == itPos) { 658 if (_rPos == itPos) {
653 // This week is already in the list. 659 // This week is already in the list.
654 // Combine the specified days with those in the list. 660 // Combine the specified days with those in the list.
655 it->rDays |= _rDays; 661 it->rDays |= _rDays;
656 if (mParent) mParent->updated(); 662 if (mParent) mParent->updated();
657 return; 663 return;
658 } 664 }
659 } 665 }
660 // Add the new position to the list 666 // Add the new position to the list
661 rMonthPos *tmpPos = new rMonthPos; 667 rMonthPos *tmpPos = new rMonthPos;
662 if (_rPos > 0) { 668 if (_rPos > 0) {
663 tmpPos->rPos = _rPos; 669 tmpPos->rPos = _rPos;
664 tmpPos->negative = false; 670 tmpPos->negative = false;
665 } else { 671 } else {
666 tmpPos->rPos = -_rPos; // take abs() 672 tmpPos->rPos = -_rPos; // take abs()
667 tmpPos->negative = true; 673 tmpPos->negative = true;
668 } 674 }
669 tmpPos->rDays = _rDays; 675 tmpPos->rDays = _rDays;
670 tmpPos->rDays.detach(); 676 tmpPos->rDays.detach();
671 rMonthPositions.append(tmpPos); 677 rMonthPositions.append(tmpPos);
672 678
673 if (mCompatVersion < 310 && mCompatDuration > 0) { 679 if (mCompatVersion < 310 && mCompatDuration > 0) {
674 // Backwards compatibility for KDE < 3.1. 680 // Backwards compatibility for KDE < 3.1.
675 // rDuration was set to the number of time periods to recur. 681 // rDuration was set to the number of time periods to recur.
676 // Convert this to the number of occurrences. 682 // Convert this to the number of occurrences.
677 int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq; 683 int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq;
678 int month = mRecurStart.date().month() - 1 + monthsAhead; 684 int month = mRecurStart.date().month() - 1 + monthsAhead;
679 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31); 685 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
680 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly 686 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
681 rDuration = recurCalc(COUNT_TO_DATE, end); 687 rDuration = recurCalc(COUNT_TO_DATE, end);
682 } 688 }
683 689
684 if (mParent) mParent->updated(); 690 if (mParent) mParent->updated();
685} 691}
686 692
687void Recurrence::addMonthlyDay(short _rDay) 693void Recurrence::addMonthlyDay(short _rDay)
688{ 694{
689 if (mRecurReadOnly || (recurs != rMonthlyDay && recurs != rYearlyMonth) 695 if (mRecurReadOnly || (recurs != rMonthlyDay && recurs != rYearlyMonth)
690 || _rDay == 0 || _rDay > 31 || _rDay < -31) // invalid day number 696 || _rDay == 0 || _rDay > 31 || _rDay < -31) // invalid day number
691 return; 697 return;
692 for (int* it = rMonthDays.first(); it; it = rMonthDays.next()) { 698 for (int* it = rMonthDays.first(); it; it = rMonthDays.next()) {
693 if (_rDay == *it) 699 if (_rDay == *it)
694 return; // this day is already in the list - avoid duplication 700 return; // this day is already in the list - avoid duplication
695 } 701 }
696 int *tmpDay = new int; 702 int *tmpDay = new int;
697 *tmpDay = _rDay; 703 *tmpDay = _rDay;
698 rMonthDays.append(tmpDay); 704 rMonthDays.append(tmpDay);
699 705
700 if (mCompatVersion < 310 && mCompatDuration > 0) { 706 if (mCompatVersion < 310 && mCompatDuration > 0) {
701 // Backwards compatibility for KDE < 3.1. 707 // Backwards compatibility for KDE < 3.1.
702 // rDuration was set to the number of time periods to recur. 708 // rDuration was set to the number of time periods to recur.
703 // Convert this to the number of occurrences. 709 // Convert this to the number of occurrences.
704 int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq; 710 int monthsAhead = (mCompatDuration-1+mRecurExDatesCount) * rFreq;
705 int month = mRecurStart.date().month() - 1 + monthsAhead; 711 int month = mRecurStart.date().month() - 1 + monthsAhead;
706 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31); 712 QDate end(mRecurStart.date().year() + month/12, month%12 + 1, 31);
707 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly 713 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
708 rDuration = recurCalc(COUNT_TO_DATE, end); 714 rDuration = recurCalc(COUNT_TO_DATE, end);
709 } 715 }
710 716
711 if (mParent) mParent->updated(); 717 if (mParent) mParent->updated();
712} 718}
713 719
714void Recurrence::setYearly(int type, int _rFreq, int _rDuration) 720void Recurrence::setYearly(int type, int _rFreq, int _rDuration)
715{ 721{
716 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 722 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
717 return; 723 return;
718 if (mCompatVersion < 310) 724 if (mCompatVersion < 310)
719 mCompatDuration = (_rDuration > 0) ? _rDuration : 0; 725 mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
720 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, _rDuration); 726 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, _rDuration);
721} 727}
722 728
723void Recurrence::setYearly(int type, int _rFreq, const QDate &_rEndDate) 729void Recurrence::setYearly(int type, int _rFreq, const QDate &_rEndDate)
724{ 730{
725 if (mRecurReadOnly) return; 731 if (mRecurReadOnly) return;
726 rEndDateTime.setDate(_rEndDate); 732 rEndDateTime.setDate(_rEndDate);
727 rEndDateTime.setTime(mRecurStart.time()); 733 rEndDateTime.setTime(mRecurStart.time());
728 mCompatDuration = 0; 734 mCompatDuration = 0;
729 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, 0); 735 setYearly_(type, mFeb29YearlyDefaultType, _rFreq, 0);
730} 736}
731 737
732void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, int _rDuration) 738void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, int _rDuration)
733{ 739{
734 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1) 740 if (mRecurReadOnly || _rDuration == 0 || _rDuration < -1)
735 return; 741 return;
736 if (mCompatVersion < 310) 742 if (mCompatVersion < 310)
737 mCompatDuration = (_rDuration > 0) ? _rDuration : 0; 743 mCompatDuration = (_rDuration > 0) ? _rDuration : 0;
738 setYearly_(rYearlyMonth, type, _rFreq, _rDuration); 744 setYearly_(rYearlyMonth, type, _rFreq, _rDuration);
739} 745}
740 746
741void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, const QDate &_rEndDate) 747void Recurrence::setYearlyByDate(Feb29Type type, int _rFreq, const QDate &_rEndDate)
742{ 748{
743 if (mRecurReadOnly) return; 749 if (mRecurReadOnly) return;
744 rEndDateTime.setDate(_rEndDate); 750 rEndDateTime.setDate(_rEndDate);
745 rEndDateTime.setTime(mRecurStart.time()); 751 rEndDateTime.setTime(mRecurStart.time());
746 mCompatDuration = 0; 752 mCompatDuration = 0;
747 setYearly_(rYearlyMonth, type, _rFreq, 0); 753 setYearly_(rYearlyMonth, type, _rFreq, 0);
748} 754}
749 755
750void Recurrence::addYearlyMonthPos(short _rPos, const QBitArray &_rDays) 756void Recurrence::addYearlyMonthPos(short _rPos, const QBitArray &_rDays)
751{ 757{
752 if (recurs == rYearlyPos) 758 if (recurs == rYearlyPos)
753 addMonthlyPos_(_rPos, _rDays); 759 addMonthlyPos_(_rPos, _rDays);
754} 760}
755 761
756const QPtrList<int> &Recurrence::yearNums() const 762const QPtrList<int> &Recurrence::yearNums() const
757{ 763{
758 return rYearNums; 764 return rYearNums;
759} 765}
760void Recurrence::addYearlyMonth(short _rPos ) 766void Recurrence::addYearlyMonth(short _rPos )
761{ 767{
762 if (mRecurReadOnly || recurs != rYearlyMonth) // invalid day/month number 768 if (mRecurReadOnly || recurs != rYearlyMonth) // invalid day/month number
763 return; 769 return;
764 rMonthPos *tmpPos = new rMonthPos; 770 rMonthPos *tmpPos = new rMonthPos;
765 if ( _rPos > 0) { 771 if ( _rPos > 0) {
766 tmpPos->rPos = _rPos; 772 tmpPos->rPos = _rPos;
767 tmpPos->negative = false; 773 tmpPos->negative = false;
768 } else { 774 } else {
769 tmpPos->rPos = -_rPos; // take abs() 775 tmpPos->rPos = -_rPos; // take abs()
770 tmpPos->negative = true; 776 tmpPos->negative = true;
771 } 777 }
772 rMonthPositions.append(tmpPos); 778 rMonthPositions.append(tmpPos);
773} 779}
774void Recurrence::addYearlyNum(short _rNum) 780void Recurrence::addYearlyNum(short _rNum)
775{ 781{
776 if (mRecurReadOnly 782 if (mRecurReadOnly
777 || (recurs != rYearlyMonth && recurs != rYearlyDay && recurs != rYearlyPos) 783 || (recurs != rYearlyMonth && recurs != rYearlyDay && recurs != rYearlyPos)
778 || _rNum <= 0) // invalid day/month number 784 || _rNum <= 0) // invalid day/month number
779 return; 785 return;
780 786
781 if (mCompatVersion < 310 && mCompatRecurs == rYearlyDay) { 787 if (mCompatVersion < 310 && mCompatRecurs == rYearlyDay) {
782 // Backwards compatibility for KDE < 3.1. 788 // Backwards compatibility for KDE < 3.1.
783 // Dates were stored as day numbers, with a fiddle to take account of leap years. 789 // Dates were stored as day numbers, with a fiddle to take account of leap years.
784 // Convert the day number to a month. 790 // Convert the day number to a month.
785 if (_rNum <= 0 || _rNum > 366 || (_rNum == 366 && mRecurStart.date().daysInYear() < 366)) 791 if (_rNum <= 0 || _rNum > 366 || (_rNum == 366 && mRecurStart.date().daysInYear() < 366))
786 return; // invalid day number 792 return; // invalid day number
787 _rNum = QDate(mRecurStart.date().year(), 1, 1).addDays(_rNum - 1).month(); 793 _rNum = QDate(mRecurStart.date().year(), 1, 1).addDays(_rNum - 1).month();
788 } else 794 } else
789 if ((recurs == rYearlyMonth || recurs == rYearlyPos) && _rNum > 12 795 if ((recurs == rYearlyMonth || recurs == rYearlyPos) && _rNum > 12
790 || recurs == rYearlyDay && _rNum > 366) 796 || recurs == rYearlyDay && _rNum > 366)
791 return; // invalid day number 797 return; // invalid day number
792 798
793 uint i = 0; 799 uint i = 0;
794 for (int* it = rYearNums.first(); it && _rNum >= *it; it = rYearNums.next()) { 800 for (int* it = rYearNums.first(); it && _rNum >= *it; it = rYearNums.next()) {
795 if (_rNum == *it) 801 if (_rNum == *it)
796 return; // this day/month is already in the list - avoid duplication 802 return; // this day/month is already in the list - avoid duplication
797 ++i; 803 ++i;
798 } 804 }
799 805
800 int *tmpNum = new int; 806 int *tmpNum = new int;
801 *tmpNum = _rNum; 807 *tmpNum = _rNum;
802 rYearNums.insert(i, tmpNum); // insert the day/month in a sorted position 808 rYearNums.insert(i, tmpNum); // insert the day/month in a sorted position
803 809
804 if (mCompatVersion < 310 && mCompatDuration > 0) { 810 if (mCompatVersion < 310 && mCompatDuration > 0) {
805 // Backwards compatibility for KDE < 3.1. 811 // Backwards compatibility for KDE < 3.1.
806 // rDuration was set to the number of time periods to recur. 812 // rDuration was set to the number of time periods to recur.
807 // Convert this to the number of occurrences. 813 // Convert this to the number of occurrences.
808 QDate end(mRecurStart.date().year() + (mCompatDuration-1+mRecurExDatesCount)*rFreq, 12, 31); 814 QDate end(mRecurStart.date().year() + (mCompatDuration-1+mRecurExDatesCount)*rFreq, 12, 31);
809 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly 815 rDuration = INT_MAX; // ensure that recurCalc() does its job correctly
810 rDuration = recurCalc(COUNT_TO_DATE, end); 816 rDuration = recurCalc(COUNT_TO_DATE, end);
811 } 817 }
812 818
813 if (mParent) mParent->updated(); 819 if (mParent) mParent->updated();
814} 820}
815 821
816 822
817QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime, bool *last) const 823QDateTime Recurrence::getNextDateTime(const QDateTime &preDateTime, bool *last) const
818{ 824{
819 if (last) 825 if (last)
820 *last = false; 826 *last = false;
821 int freq; 827 int freq;
822 switch (recurs) 828 switch (recurs)
823 { 829 {
824 case rMinutely: 830 case rMinutely:
825 freq = rFreq * 60; 831 freq = rFreq * 60;
826 break; 832 break;
827 case rHourly: 833 case rHourly:
828 freq = rFreq * 3600; 834 freq = rFreq * 3600;
829 break; 835 break;
830 case rDaily: 836 case rDaily:
831 case rWeekly: 837 case rWeekly:
832 case rMonthlyPos: 838 case rMonthlyPos:
833 case rMonthlyDay: 839 case rMonthlyDay:
834 case rYearlyMonth: 840 case rYearlyMonth:
835 case rYearlyDay: 841 case rYearlyDay:
836 case rYearlyPos: { 842 case rYearlyPos: {
837 QDate preDate = preDateTime.date(); 843 QDate preDate = preDateTime.date();
838 if (!mFloats && mRecurStart.time() > preDateTime.time()) 844 if (!mFloats && mRecurStart.time() > preDateTime.time())
839 preDate = preDate.addDays(-1); 845 preDate = preDate.addDays(-1);
840 return QDateTime(getNextDateNoTime(preDate, last), mRecurStart.time()); 846 return QDateTime(getNextDateNoTime(preDate, last), mRecurStart.time());
841 } 847 }
842 default: 848 default:
843 return QDateTime(); 849 return QDateTime();
844 } 850 }
845 851
846 // It's a sub-daily recurrence 852 // It's a sub-daily recurrence
847 if (preDateTime < mRecurStart) 853 if (preDateTime < mRecurStart)
848 return mRecurStart; 854 return mRecurStart;
849 int count = mRecurStart.secsTo(preDateTime) / freq + 2; 855 int count = mRecurStart.secsTo(preDateTime) / freq + 2;
850 if (rDuration > 0) { 856 if (rDuration > 0) {
851 if (count > rDuration) 857 if (count > rDuration)
852 return QDateTime(); 858 return QDateTime();
853 if (last && count == rDuration) 859 if (last && count == rDuration)
854 *last = true; 860 *last = true;
855 } 861 }
856 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq); 862 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
857 if (rDuration == 0) { 863 if (rDuration == 0) {
858 if (endtime > rEndDateTime) 864 if (endtime > rEndDateTime)
859 return QDateTime(); 865 return QDateTime();
860 if (last && endtime == rEndDateTime) 866 if (last && endtime == rEndDateTime)
861 *last = true; 867 *last = true;
862 } 868 }
863 return endtime; 869 return endtime;
864} 870}
865 871
866QDate Recurrence::getNextDate(const QDate &preDate, bool *last) const 872QDate Recurrence::getNextDate(const QDate &preDate, bool *last) const
867{ 873{
868 if (last) 874 if (last)
869 *last = false; 875 *last = false;
870 switch (recurs) 876 switch (recurs)
871 { 877 {
872 case rMinutely: 878 case rMinutely:
873 case rHourly: 879 case rHourly:
874 return getNextDateTime(QDateTime(preDate, QTime(23,59,59)), last).date(); 880 return getNextDateTime(QDateTime(preDate, QTime(23,59,59)), last).date();
875 case rDaily: 881 case rDaily:
876 case rWeekly: 882 case rWeekly:
877 case rMonthlyPos: 883 case rMonthlyPos:
878 case rMonthlyDay: 884 case rMonthlyDay:
879 case rYearlyMonth: 885 case rYearlyMonth:
880 case rYearlyDay: 886 case rYearlyDay:
881 case rYearlyPos: 887 case rYearlyPos:
882 qDebug("Recurrence::getNextDate: MAY BE BROKEN "); 888 qDebug("Recurrence::getNextDate: MAY BE BROKEN ");
883 return getNextDateNoTime(preDate, last); 889 return getNextDateNoTime(preDate, last);
884 default: 890 default:
885 return QDate(); 891 return QDate();
886 } 892 }
887} 893}
888 894
889 895
890QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime, bool *last) const 896QDateTime Recurrence::getPreviousDateTime(const QDateTime &afterDateTime, bool *last) const
891{ 897{
892 if (last) 898 if (last)
893 *last = false; 899 *last = false;
894 int freq; 900 int freq;
895 switch (recurs) 901 switch (recurs)
896 { 902 {
897 case rMinutely: 903 case rMinutely:
898 freq = rFreq * 60; 904 freq = rFreq * 60;
899 break; 905 break;
900 case rHourly: 906 case rHourly:
901 freq = rFreq * 3600; 907 freq = rFreq * 3600;
902 break; 908 break;
903 case rDaily: 909 case rDaily:
904 case rWeekly: 910 case rWeekly:
905 case rMonthlyPos: 911 case rMonthlyPos:
906 case rMonthlyDay: 912 case rMonthlyDay:
907 case rYearlyMonth: 913 case rYearlyMonth:
908 case rYearlyDay: 914 case rYearlyDay:
909 case rYearlyPos: { 915 case rYearlyPos: {
910 QDate afterDate = afterDateTime.date(); 916 QDate afterDate = afterDateTime.date();
911 if (!mFloats && mRecurStart.time() < afterDateTime.time()) 917 if (!mFloats && mRecurStart.time() < afterDateTime.time())
912 afterDate = afterDate.addDays(1); 918 afterDate = afterDate.addDays(1);
913 return QDateTime(getPreviousDateNoTime(afterDate, last), mRecurStart.time()); 919 return QDateTime(getPreviousDateNoTime(afterDate, last), mRecurStart.time());
914 } 920 }
915 default: 921 default:
916 return QDateTime(); 922 return QDateTime();
917 } 923 }
918 924
919 // It's a sub-daily recurrence 925 // It's a sub-daily recurrence
920 if (afterDateTime <= mRecurStart) 926 if (afterDateTime <= mRecurStart)
921 return QDateTime(); 927 return QDateTime();
922 int count = (mRecurStart.secsTo(afterDateTime) - 1) / freq + 1; 928 int count = (mRecurStart.secsTo(afterDateTime) - 1) / freq + 1;
923 if (rDuration > 0) { 929 if (rDuration > 0) {
924 if (count > rDuration) 930 if (count > rDuration)
925 count = rDuration; 931 count = rDuration;
926 if (last && count == rDuration) 932 if (last && count == rDuration)
927 *last = true; 933 *last = true;
928 } 934 }
929 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq); 935 QDateTime endtime = mRecurStart.addSecs((count - 1)*freq);
930 if (rDuration == 0) { 936 if (rDuration == 0) {
931 if (endtime > rEndDateTime) 937 if (endtime > rEndDateTime)
932 endtime = rEndDateTime; 938 endtime = rEndDateTime;
933 if (last && endtime == rEndDateTime) 939 if (last && endtime == rEndDateTime)
934 *last = true; 940 *last = true;
935 } 941 }
936 return endtime; 942 return endtime;
937} 943}
938 944
939QDate Recurrence::getPreviousDate(const QDate &afterDate, bool *last) const 945QDate Recurrence::getPreviousDate(const QDate &afterDate, bool *last) const
940{ 946{
941 if (last) 947 if (last)
942 *last = false; 948 *last = false;
943 switch (recurs) 949 switch (recurs)
944 { 950 {
945 case rMinutely: 951 case rMinutely:
946 case rHourly: 952 case rHourly:
947 return getPreviousDateTime(QDateTime(afterDate, QTime(0,0,0)), last).date(); 953 return getPreviousDateTime(QDateTime(afterDate, QTime(0,0,0)), last).date();
948 case rDaily: 954 case rDaily:
949 case rWeekly: 955 case rWeekly:
950 case rMonthlyPos: 956 case rMonthlyPos:
951 case rMonthlyDay: 957 case rMonthlyDay:
952 case rYearlyMonth: 958 case rYearlyMonth:
953 case rYearlyDay: 959 case rYearlyDay:
954 case rYearlyPos: 960 case rYearlyPos:
955 return getPreviousDateNoTime(afterDate, last); 961 return getPreviousDateNoTime(afterDate, last);
956 default: 962 default:
957 return QDate(); 963 return QDate();
958 } 964 }
959} 965}
960 966
961 967
962/***************************** PROTECTED FUNCTIONS ***************************/ 968/***************************** PROTECTED FUNCTIONS ***************************/
963 969
964bool Recurrence::recursSecondly(const QDate &qd, int secondFreq) const 970bool Recurrence::recursSecondly(const QDate &qd, int secondFreq) const
965{ 971{
966 if ((qd >= mRecurStart.date()) && 972 if ((qd >= mRecurStart.date()) &&
967 ((rDuration > 0) && (qd <= endDate()) || 973 ((rDuration > 0) && (qd <= endDate()) ||
968 ((rDuration == 0) && (qd <= rEndDateTime.date())) || 974 ((rDuration == 0) && (qd <= rEndDateTime.date())) ||
969 (rDuration == -1))) { 975 (rDuration == -1))) {
970 // The date queried falls within the range of the event. 976 // The date queried falls within the range of the event.
971 if (secondFreq < 24*3600) 977 if (secondFreq < 24*3600)
972 return true; // the event recurs at least once each day 978 return true; // the event recurs at least once each day
973 int after = mRecurStart.secsTo(QDateTime(qd)); 979 int after = mRecurStart.secsTo(QDateTime(qd));
974 if (after / secondFreq != (after + 24*3600) / secondFreq) 980 if (after / secondFreq != (after + 24*3600) / secondFreq)
975 return true; 981 return true;
976 } 982 }
977 return false; 983 return false;
978} 984}
979 985
980bool Recurrence::recursMinutelyAt(const QDateTime &dt, int minuteFreq) const 986bool Recurrence::recursMinutelyAt(const QDateTime &dt, int minuteFreq) const
981{ 987{
982 if ((dt >= mRecurStart) && 988 if ((dt >= mRecurStart) &&
983 ((rDuration > 0) && (dt <= endDateTime()) || 989 ((rDuration > 0) && (dt <= endDateTime()) ||
984 ((rDuration == 0) && (dt <= rEndDateTime)) || 990 ((rDuration == 0) && (dt <= rEndDateTime)) ||
985 (rDuration == -1))) { 991 (rDuration == -1))) {
986 // The time queried falls within the range of the event. 992 // The time queried falls within the range of the event.
987 if (((mRecurStart.secsTo(dt) / 60) % minuteFreq) == 0) 993 if (((mRecurStart.secsTo(dt) / 60) % minuteFreq) == 0)
988 return true; 994 return true;
989 } 995 }
990 return false; 996 return false;
991} 997}
992 998
993bool Recurrence::recursDaily(const QDate &qd) const 999bool Recurrence::recursDaily(const QDate &qd) const
994{ 1000{
995 QDate dStart = mRecurStart.date(); 1001 QDate dStart = mRecurStart.date();
996 if ((dStart.daysTo(qd) % rFreq) == 0) { 1002 if ((dStart.daysTo(qd) % rFreq) == 0) {
997 // The date is a day which recurs 1003 // The date is a day which recurs
998 if (qd >= dStart 1004 if (qd >= dStart
999 && ((rDuration > 0 && qd <= endDate()) || 1005 && ((rDuration > 0 && qd <= endDate()) ||
1000 (rDuration == 0 && qd <= rEndDateTime.date()) || 1006 (rDuration == 0 && qd <= rEndDateTime.date()) ||
1001 rDuration == -1)) { 1007 rDuration == -1)) {
1002 // The date queried falls within the range of the event. 1008 // The date queried falls within the range of the event.
1003 return true; 1009 return true;
1004 } 1010 }
1005 } 1011 }
1006 return false; 1012 return false;
1007} 1013}
1008 1014
1009bool Recurrence::recursWeekly(const QDate &qd) const 1015bool Recurrence::recursWeekly(const QDate &qd) const
1010{ 1016{
1011 QDate dStart = mRecurStart.date(); 1017 QDate dStart = mRecurStart.date();
1012 if ((dStart.daysTo(qd)/7) % rFreq == 0) { 1018 if ((dStart.daysTo(qd)/7) % rFreq == 0) {
1013 // The date is in a week which recurs 1019 // The date is in a week which recurs
1014 if (qd >= dStart 1020 if (qd >= dStart
1015 && ((rDuration > 0 && qd <= endDate()) || 1021 && ((rDuration > 0 && qd <= endDate()) ||
1016 (rDuration == 0 && qd <= rEndDateTime.date()) || 1022 (rDuration == 0 && qd <= rEndDateTime.date()) ||
1017 rDuration == -1)) { 1023 rDuration == -1)) {
1018 // The date queried falls within the range of the event. 1024 // The date queried falls within the range of the event.
1019 // check if the bits set match today. 1025 // check if the bits set match today.
1020 int i = qd.dayOfWeek()-1; 1026 int i = qd.dayOfWeek()-1;
1021 if (rDays.testBit((uint) i)) 1027 if (rDays.testBit((uint) i))
1022 return true; 1028 return true;
1023 } 1029 }
1024 } 1030 }
1025 return false; 1031 return false;
1026} 1032}
1027 1033
1028bool Recurrence::recursMonthly(const QDate &qd) const 1034bool Recurrence::recursMonthly(const QDate &qd) const
1029{ 1035{
1030 QDate dStart = mRecurStart.date(); 1036 QDate dStart = mRecurStart.date();
1031 int year = qd.year(); 1037 int year = qd.year();
1032 int month = qd.month(); 1038 int month = qd.month();
1033 int day = qd.day(); 1039 int day = qd.day();
1034 // calculate how many months ahead this date is from the original 1040 // calculate how many months ahead this date is from the original
1035 // event's date 1041 // event's date
1036 int monthsAhead = (year - dStart.year()) * 12 + (month - dStart.month()); 1042 int monthsAhead = (year - dStart.year()) * 12 + (month - dStart.month());
1037 if ((monthsAhead % rFreq) == 0) { 1043 if ((monthsAhead % rFreq) == 0) {
1038 // The date is in a month which recurs 1044 // The date is in a month which recurs
1039 if (qd >= dStart 1045 if (qd >= dStart
1040 && ((rDuration > 0 && qd <= endDate()) || 1046 && ((rDuration > 0 && qd <= endDate()) ||
1041 (rDuration == 0 && qd <= rEndDateTime.date()) || 1047 (rDuration == 0 && qd <= rEndDateTime.date()) ||
1042 rDuration == -1)) { 1048 rDuration == -1)) {
1043 // The date queried falls within the range of the event. 1049 // The date queried falls within the range of the event.
1044 QValueList<int> days; 1050 QValueList<int> days;
1045 int daysInMonth = qd.daysInMonth(); 1051 int daysInMonth = qd.daysInMonth();
1046 if (recurs == rMonthlyDay) 1052 if (recurs == rMonthlyDay)
1047 getMonthlyDayDays(days, daysInMonth); 1053 getMonthlyDayDays(days, daysInMonth);
1048 else if (recurs == rMonthlyPos) 1054 else if (recurs == rMonthlyPos)
1049 getMonthlyPosDays(days, daysInMonth, QDate(year, month, 1).dayOfWeek()); 1055 getMonthlyPosDays(days, daysInMonth, QDate(year, month, 1).dayOfWeek());
1050 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) { 1056 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) {
1051 if (*it == day) 1057 if (*it == day)
1052 return true; 1058 return true;
1053 } 1059 }
1054 // no dates matched 1060 // no dates matched
1055 } 1061 }
1056 } 1062 }
1057 return false; 1063 return false;
1058} 1064}
1059 1065
1060bool Recurrence::recursYearlyByMonth(const QDate &qd) const 1066bool Recurrence::recursYearlyByMonth(const QDate &qd) const
1061{ 1067{
1062 QDate dStart = mRecurStart.date(); 1068 QDate dStart = mRecurStart.date();
1063 int startDay = dStart.day(); 1069 int startDay = dStart.day();
1064 int qday = qd.day(); 1070 int qday = qd.day();
1065 int qmonth = qd.month(); 1071 int qmonth = qd.month();
1066 int qyear = qd.year(); 1072 int qyear = qd.year();
1067 bool match = (qday == startDay); 1073 bool match = (qday == startDay);
1068 if (!match && startDay == 29 && dStart.month() == 2) { 1074 if (!match && startDay == 29 && dStart.month() == 2) {
1069 // It's a recurrence on February 29th 1075 // It's a recurrence on February 29th
1070 switch (mFeb29YearlyType) { 1076 switch (mFeb29YearlyType) {
1071 case rFeb28: 1077 case rFeb28:
1072 if (qday == 28 && qmonth == 2 && !QDate::leapYear(qyear)) 1078 if (qday == 28 && qmonth == 2 && !QDate::leapYear(qyear))
1073 match = true; 1079 match = true;
1074 break; 1080 break;
1075 case rMar1: 1081 case rMar1:
1076 if (qday == 1 && qmonth == 3 && !QDate::leapYear(qyear)) { 1082 if (qday == 1 && qmonth == 3 && !QDate::leapYear(qyear)) {
1077 qmonth = 2; 1083 qmonth = 2;
1078 match = true; 1084 match = true;
1079 } 1085 }
1080 break; 1086 break;
1081 case rFeb29: 1087 case rFeb29:
1082 break; 1088 break;
1083 } 1089 }
1084 } 1090 }
1085 1091
1086 if (match) { 1092 if (match) {
1087 // The day of the month matches. Calculate how many years ahead 1093 // The day of the month matches. Calculate how many years ahead
1088 // this date is from the original event's date. 1094 // this date is from the original event's date.
1089 int yearsAhead = (qyear - dStart.year()); 1095 int yearsAhead = (qyear - dStart.year());
1090 if (yearsAhead % rFreq == 0) { 1096 if (yearsAhead % rFreq == 0) {
1091 // The date is in a year which recurs 1097 // The date is in a year which recurs
1092 if (qd >= dStart 1098 if (qd >= dStart
1093 && ((rDuration > 0 && qd <= endDate()) || 1099 && ((rDuration > 0 && qd <= endDate()) ||
1094 (rDuration == 0 && qd <= rEndDateTime.date()) || 1100 (rDuration == 0 && qd <= rEndDateTime.date()) ||
1095 rDuration == -1)) { 1101 rDuration == -1)) {
1096 // The date queried falls within the range of the event. 1102 // The date queried falls within the range of the event.
1097 int i = qmonth; 1103 int i = qmonth;
1098 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { 1104 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
1099 if (i == *qlin.current()) 1105 if (i == *qlin.current())
1100 return true; 1106 return true;
1101 } 1107 }
1102 } 1108 }
1103 } 1109 }
1104 } 1110 }
1105 return false; 1111 return false;
1106} 1112}
1107 1113
1108bool Recurrence::recursYearlyByPos(const QDate &qd) const 1114bool Recurrence::recursYearlyByPos(const QDate &qd) const
1109{ 1115{
1110 QDate dStart = mRecurStart.date(); 1116 QDate dStart = mRecurStart.date();
1111 int year = qd.year(); 1117 int year = qd.year();
1112 int month = qd.month(); 1118 int month = qd.month();
1113 int day = qd.day(); 1119 int day = qd.day();
1114 // calculate how many years ahead this date is from the original 1120 // calculate how many years ahead this date is from the original
1115 // event's date 1121 // event's date
1116 int yearsAhead = (year - dStart.year()); 1122 int yearsAhead = (year - dStart.year());
1117 if (yearsAhead % rFreq == 0) { 1123 if (yearsAhead % rFreq == 0) {
1118 // The date is in a year which recurs 1124 // The date is in a year which recurs
1119 if (qd >= dStart 1125 if (qd >= dStart
1120 && ((rDuration > 0 && qd <= endDate()) || 1126 && ((rDuration > 0 && qd <= endDate()) ||
1121 (rDuration == 0 && qd <= rEndDateTime.date()) || 1127 (rDuration == 0 && qd <= rEndDateTime.date()) ||
1122 rDuration == -1)) { 1128 rDuration == -1)) {
1123 // The date queried falls within the range of the event. 1129 // The date queried falls within the range of the event.
1124 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { 1130 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
1125 if (month == *qlin.current()) { 1131 if (month == *qlin.current()) {
1126 // The month recurs 1132 // The month recurs
1127 QValueList<int> days; 1133 QValueList<int> days;
1128 getMonthlyPosDays(days, qd.daysInMonth(), QDate(year, month, 1).dayOfWeek()); 1134 getMonthlyPosDays(days, qd.daysInMonth(), QDate(year, month, 1).dayOfWeek());
1129 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) { 1135 for (QValueList<int>::Iterator it = days.begin(); it != days.end(); ++it) {
1130 if (*it == day) 1136 if (*it == day)
1131 return true; 1137 return true;
1132 } 1138 }
1133 } 1139 }
1134 } 1140 }
1135 } 1141 }
1136 } 1142 }
1137 return false; 1143 return false;
1138} 1144}
1139 1145
1140bool Recurrence::recursYearlyByDay(const QDate &qd) const 1146bool Recurrence::recursYearlyByDay(const QDate &qd) const
1141{ 1147{
1142 QDate dStart = mRecurStart.date(); 1148 QDate dStart = mRecurStart.date();
1143 // calculate how many years ahead this date is from the original 1149 // calculate how many years ahead this date is from the original
1144 // event's date 1150 // event's date
1145 int yearsAhead = (qd.year() - dStart.year()); 1151 int yearsAhead = (qd.year() - dStart.year());
1146 if (yearsAhead % rFreq == 0) { 1152 if (yearsAhead % rFreq == 0) {
1147 // The date is in a year which recurs 1153 // The date is in a year which recurs
1148 if (qd >= dStart 1154 if (qd >= dStart
1149 && ((rDuration > 0 && qd <= endDate()) || 1155 && ((rDuration > 0 && qd <= endDate()) ||
1150 (rDuration == 0 && qd <= rEndDateTime.date()) || 1156 (rDuration == 0 && qd <= rEndDateTime.date()) ||
1151 rDuration == -1)) { 1157 rDuration == -1)) {
1152 // The date queried falls within the range of the event. 1158 // The date queried falls within the range of the event.
1153 int i = qd.dayOfYear(); 1159 int i = qd.dayOfYear();
1154 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) { 1160 for (QPtrListIterator<int> qlin(rYearNums); qlin.current(); ++qlin) {
1155 if (i == *qlin.current()) 1161 if (i == *qlin.current())
1156 return true; 1162 return true;
1157 } 1163 }
1158 } 1164 }
1159 } 1165 }
1160 return false; 1166 return false;
1161} 1167}
1162 1168
1163/* Get the date of the next recurrence, after the specified date. 1169/* Get the date of the next recurrence, after the specified date.
1164 * If 'last' is non-null, '*last' is set to true if the next recurrence is the 1170 * If 'last' is non-null, '*last' is set to true if the next recurrence is the
1165 * last recurrence, else false. 1171 * last recurrence, else false.
1166 * Reply = date of next recurrence, or invalid date if none. 1172 * Reply = date of next recurrence, or invalid date if none.
1167 */ 1173 */
1168QDate Recurrence::getNextDateNoTime(const QDate &preDate, bool *last) const 1174QDate Recurrence::getNextDateNoTime(const QDate &preDate, bool *last) const
1169{ 1175{
1170 1176
1171 if (last) 1177 if (last)
1172 *last = false; 1178 *last = false;
1173 QDate dStart = mRecurStart.date(); 1179 QDate dStart = mRecurStart.date();
1174 if (preDate < dStart) 1180 if (preDate < dStart)
1175 return dStart; 1181 return dStart;
1176 QDate earliestDate = preDate.addDays(1); 1182 QDate earliestDate = preDate.addDays(1);
1177 QDate nextDate; 1183 QDate nextDate;
1178 1184
1179 switch (recurs) { 1185 switch (recurs) {
1180 case rDaily: 1186 case rDaily:
1181 nextDate = dStart.addDays((dStart.daysTo(preDate)/rFreq + 1) * rFreq); 1187 nextDate = dStart.addDays((dStart.daysTo(preDate)/rFreq + 1) * rFreq);
1182 break; 1188 break;
1183 1189
1184 case rWeekly: { 1190 case rWeekly: {
1185 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart 1191 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart
1186 int earliestDayOfWeek = earliestDate.dayOfWeek(); 1192 int earliestDayOfWeek = earliestDate.dayOfWeek();
1187 int weeksAhead = start.daysTo(earliestDate) / 7; 1193 int weeksAhead = start.daysTo(earliestDate) / 7;
1188 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week 1194 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week
1189 weeksAhead -= notThisWeek; // latest week which recurred 1195 weeksAhead -= notThisWeek; // latest week which recurred
1190 int weekday = 0; 1196 int weekday = 0;
1191 // First check for any remaining day this week, if this week is a recurring week 1197 // First check for any remaining day this week, if this week is a recurring week
1192 if (!notThisWeek) 1198 if (!notThisWeek)
1193 weekday = getFirstDayInWeek(earliestDayOfWeek); 1199 weekday = getFirstDayInWeek(earliestDayOfWeek);
1194 // Check for a day in the next scheduled week 1200 // Check for a day in the next scheduled week
1195 if (!weekday && earliestDayOfWeek > 1) 1201 if (!weekday && earliestDayOfWeek > 1)
1196 weekday = getFirstDayInWeek(rWeekStart) + rFreq*7; 1202 weekday = getFirstDayInWeek(rWeekStart) + rFreq*7;
1197 if (weekday) 1203 if (weekday)
1198 nextDate = start.addDays(weeksAhead*7 + weekday - 1); 1204 nextDate = start.addDays(weeksAhead*7 + weekday - 1);
1199 break; 1205 break;
1200 } 1206 }
1201 case rMonthlyDay: 1207 case rMonthlyDay:
1202 case rMonthlyPos: { 1208 case rMonthlyPos: {
1203 int startYear = dStart.year(); 1209 int startYear = dStart.year();
1204 int startMonth = dStart.month(); // 1..12 1210 int startMonth = dStart.month(); // 1..12
1205 int earliestYear = earliestDate.year(); 1211 int earliestYear = earliestDate.year();
1206 int monthsAhead = (earliestYear - startYear)*12 + earliestDate.month() - startMonth; 1212 int monthsAhead = (earliestYear - startYear)*12 + earliestDate.month() - startMonth;
1207 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month 1213 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month
1208 monthsAhead -= notThisMonth; // latest month which recurred 1214 monthsAhead -= notThisMonth; // latest month which recurred
1209 // Check for the first later day in the current month 1215 // Check for the first later day in the current month
1210 if (!notThisMonth) 1216 if (!notThisMonth)
1211 nextDate = getFirstDateInMonth(earliestDate); 1217 nextDate = getFirstDateInMonth(earliestDate);
1212 if (!nextDate.isValid() && earliestDate.day() > 1) { 1218 if (!nextDate.isValid() && earliestDate.day() > 1) {
1213 // Check for a day in the next scheduled month 1219 // Check for a day in the next scheduled month
1214 int months = startMonth - 1 + monthsAhead + rFreq; 1220 int months = startMonth - 1 + monthsAhead + rFreq;
1215 nextDate = getFirstDateInMonth(QDate(startYear + months/12, months%12 + 1, 1)); 1221 nextDate = getFirstDateInMonth(QDate(startYear + months/12, months%12 + 1, 1));
1216 } 1222 }
1217 break; 1223 break;
1218 } 1224 }
1219 case rYearlyMonth: 1225 case rYearlyMonth:
1220 case rYearlyPos: 1226 case rYearlyPos:
1221 case rYearlyDay: { 1227 case rYearlyDay: {
1222 int startYear = dStart.year(); 1228 int startYear = dStart.year();
1223 int yearsAhead = earliestDate.year() - startYear; 1229 int yearsAhead = earliestDate.year() - startYear;
1224 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year 1230 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year
1225 yearsAhead -= notThisYear; // latest year which recurred 1231 yearsAhead -= notThisYear; // latest year which recurred
1226 // Check for the first later date in the current year 1232 // Check for the first later date in the current year
1227 if (!notThisYear) 1233 if (!notThisYear)
1228 nextDate = getFirstDateInYear(earliestDate); 1234 nextDate = getFirstDateInYear(earliestDate);
1229 // Check for a date in the next scheduled year 1235 // Check for a date in the next scheduled year
1230 if (!nextDate.isValid() && earliestDate.dayOfYear() > 1) 1236 if (!nextDate.isValid() && earliestDate.dayOfYear() > 1)
1231 nextDate = getFirstDateInYear(QDate(startYear + yearsAhead + rFreq, 1, 1)); 1237 nextDate = getFirstDateInYear(QDate(startYear + yearsAhead + rFreq, 1, 1));
1232 break; 1238 break;
1233 } 1239 }
1234 case rNone: 1240 case rNone:
1235 default: 1241 default:
1236 return QDate(); 1242 return QDate();
1237 } 1243 }
1238 1244
1239 if (rDuration >= 0 && nextDate.isValid()) { 1245 if (rDuration >= 0 && nextDate.isValid()) {
1240 // Check that the date found is within the range of the recurrence 1246 // Check that the date found is within the range of the recurrence
1241 QDate end = endDate(); 1247 QDate end = endDate();
1242 if (nextDate > end) 1248 if (nextDate > end)
1243 return QDate(); 1249 return QDate();
1244 if (last && nextDate == end) 1250 if (last && nextDate == end)
1245 *last = true; 1251 *last = true;
1246 } 1252 }
1247 return nextDate; 1253 return nextDate;
1248} 1254}
1249 1255
1250/* Get the date of the last previous recurrence, before the specified date. 1256/* Get the date of the last previous recurrence, before the specified date.
1251 * Reply = date of previous recurrence, or invalid date if none. 1257 * Reply = date of previous recurrence, or invalid date if none.
1252 */ 1258 */
1253QDate Recurrence::getPreviousDateNoTime(const QDate &afterDate, bool *last) const 1259QDate Recurrence::getPreviousDateNoTime(const QDate &afterDate, bool *last) const
1254{ 1260{
1255 if (last) 1261 if (last)
1256 *last = false; 1262 *last = false;
1257 QDate dStart = mRecurStart.date(); 1263 QDate dStart = mRecurStart.date();
1258 QDate latestDate = afterDate.addDays(-1); 1264 QDate latestDate = afterDate.addDays(-1);
1259 if (latestDate < dStart) 1265 if (latestDate < dStart)
1260 return QDate(); 1266 return QDate();
1261 QDate prevDate; 1267 QDate prevDate;
1262 1268
1263 switch (recurs) { 1269 switch (recurs) {
1264 case rDaily: 1270 case rDaily:
1265 prevDate = dStart.addDays((dStart.daysTo(latestDate) / rFreq) * rFreq); 1271 prevDate = dStart.addDays((dStart.daysTo(latestDate) / rFreq) * rFreq);
1266 break; 1272 break;
1267 1273
1268 case rWeekly: { 1274 case rWeekly: {
1269 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart 1275 QDate start = dStart.addDays(1 - dStart.dayOfWeek()); // start of week for dStart
1270 int latestDayOfWeek = latestDate.dayOfWeek(); 1276 int latestDayOfWeek = latestDate.dayOfWeek();
1271 int weeksAhead = start.daysTo(latestDate) / 7; 1277 int weeksAhead = start.daysTo(latestDate) / 7;
1272 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week 1278 int notThisWeek = weeksAhead % rFreq; // zero if this week is a recurring week
1273 weeksAhead -= notThisWeek; // latest week which recurred 1279 weeksAhead -= notThisWeek; // latest week which recurred
1274 int weekday = 0; 1280 int weekday = 0;
1275 // First check for any previous day this week, if this week is a recurring week 1281 // First check for any previous day this week, if this week is a recurring week
1276 if (!notThisWeek) 1282 if (!notThisWeek)
1277 weekday = getLastDayInWeek(latestDayOfWeek); 1283 weekday = getLastDayInWeek(latestDayOfWeek);
1278 // Check for a day in the previous scheduled week 1284 // Check for a day in the previous scheduled week
1279 if (!weekday) { 1285 if (!weekday) {
1280 int weekEnd = (rWeekStart + 5)%7 + 1; 1286 int weekEnd = (rWeekStart + 5)%7 + 1;
1281 if (latestDayOfWeek < weekEnd) { 1287 if (latestDayOfWeek < weekEnd) {
1282 if (!notThisWeek) 1288 if (!notThisWeek)
1283 weeksAhead -= rFreq; 1289 weeksAhead -= rFreq;
1284 weekday = getLastDayInWeek(weekEnd); 1290 weekday = getLastDayInWeek(weekEnd);
1285 } 1291 }
1286 } 1292 }
1287 if (weekday) 1293 if (weekday)
1288 prevDate = start.addDays(weeksAhead*7 + weekday - 1); 1294 prevDate = start.addDays(weeksAhead*7 + weekday - 1);
1289 break; 1295 break;
1290 } 1296 }
1291 case rMonthlyDay: 1297 case rMonthlyDay:
1292 case rMonthlyPos: { 1298 case rMonthlyPos: {
1293 int startYear = dStart.year(); 1299 int startYear = dStart.year();
1294 int startMonth = dStart.month(); // 1..12 1300 int startMonth = dStart.month(); // 1..12
1295 int latestYear = latestDate.year(); 1301 int latestYear = latestDate.year();
1296 int monthsAhead = (latestYear - startYear)*12 + latestDate.month() - startMonth; 1302 int monthsAhead = (latestYear - startYear)*12 + latestDate.month() - startMonth;
1297 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month 1303 int notThisMonth = monthsAhead % rFreq; // zero if this month is a recurring month
1298 monthsAhead -= notThisMonth; // latest month which recurred 1304 monthsAhead -= notThisMonth; // latest month which recurred
1299 // Check for the last earlier day in the current month 1305 // Check for the last earlier day in the current month
1300 if (!notThisMonth) 1306 if (!notThisMonth)
1301 prevDate = getLastDateInMonth(latestDate); 1307 prevDate = getLastDateInMonth(latestDate);
1302 if (!prevDate.isValid() && latestDate.day() < latestDate.daysInMonth()) { 1308 if (!prevDate.isValid() && latestDate.day() < latestDate.daysInMonth()) {
1303 // Check for a day in the previous scheduled month 1309 // Check for a day in the previous scheduled month
1304 if (!notThisMonth) 1310 if (!notThisMonth)
1305 monthsAhead -= rFreq; 1311 monthsAhead -= rFreq;
1306 int months = startMonth + monthsAhead; // get the month after the one that recurs 1312 int months = startMonth + monthsAhead; // get the month after the one that recurs
1307 prevDate = getLastDateInMonth(QDate(startYear + months/12, months%12 + 1, 1).addDays(-1)); 1313 prevDate = getLastDateInMonth(QDate(startYear + months/12, months%12 + 1, 1).addDays(-1));
1308 } 1314 }
1309 break; 1315 break;
1310 } 1316 }
1311 case rYearlyMonth: 1317 case rYearlyMonth:
1312 case rYearlyPos: 1318 case rYearlyPos:
1313 case rYearlyDay: { 1319 case rYearlyDay: {
1314 int startYear = dStart.year(); 1320 int startYear = dStart.year();
1315 int yearsAhead = latestDate.year() - startYear; 1321 int yearsAhead = latestDate.year() - startYear;
1316 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year 1322 int notThisYear = yearsAhead % rFreq; // zero if this year is a recurring year
1317 yearsAhead -= notThisYear; // latest year which recurred 1323 yearsAhead -= notThisYear; // latest year which recurred
1318 // Check for the first later date in the current year 1324 // Check for the first later date in the current year
1319 if (!notThisYear) 1325 if (!notThisYear)
1320 prevDate = getLastDateInYear(latestDate); 1326 prevDate = getLastDateInYear(latestDate);
1321 if (!prevDate.isValid() && latestDate.dayOfYear() < latestDate.daysInYear()) { 1327 if (!prevDate.isValid() && latestDate.dayOfYear() < latestDate.daysInYear()) {
1322 // Check for a date in the next scheduled year 1328 // Check for a date in the next scheduled year
1323 if (!notThisYear) 1329 if (!notThisYear)
1324 yearsAhead -= rFreq; 1330 yearsAhead -= rFreq;
1325 prevDate = getLastDateInYear(QDate(startYear + yearsAhead, 12, 31)); 1331 prevDate = getLastDateInYear(QDate(startYear + yearsAhead, 12, 31));
1326 } 1332 }
1327 break; 1333 break;
1328 } 1334 }
1329 case rNone: 1335 case rNone:
1330 default: 1336 default:
1331 return QDate(); 1337 return QDate();
1332 } 1338 }
1333 1339
1334 if (prevDate.isValid()) { 1340 if (prevDate.isValid()) {
1335 // Check that the date found is within the range of the recurrence 1341 // Check that the date found is within the range of the recurrence
1336 if (prevDate < dStart) 1342 if (prevDate < dStart)
1337 return QDate(); 1343 return QDate();
1338 if (rDuration >= 0) { 1344 if (rDuration >= 0) {
1339 QDate end = endDate(); 1345 QDate end = endDate();
1340 if (prevDate >= end) { 1346 if (prevDate >= end) {
1341 if (last) 1347 if (last)
1342 *last = true; 1348 *last = true;
1343 return end; 1349 return end;
1344 } 1350 }
1345 } 1351 }
1346 } 1352 }
1347 return prevDate; 1353 return prevDate;
1348} 1354}
1349 1355
1350void Recurrence::setDailySub(short type, int freq, int duration) 1356void Recurrence::setDailySub(short type, int freq, int duration)
1351{ 1357{
1352 recurs = type; 1358 recurs = type;
1353 rFreq = freq; 1359 rFreq = freq;
1354 rDuration = duration; 1360 rDuration = duration;
1355 rMonthPositions.clear(); 1361 rMonthPositions.clear();
1356 rMonthDays.clear(); 1362 rMonthDays.clear();
1357 rYearNums.clear(); 1363 rYearNums.clear();
1358 if (type != rDaily) 1364 if (type != rDaily)
1359 mFloats = false; // sub-daily types can't be floating 1365 mFloats = false; // sub-daily types can't be floating
1360 1366
1361 if (mParent) mParent->updated(); 1367 if (mParent) mParent->updated();
1362} 1368}
1363 1369
1364void Recurrence::setYearly_(short type, Feb29Type feb29type, int freq, int duration) 1370void Recurrence::setYearly_(short type, Feb29Type feb29type, int freq, int duration)
1365{ 1371{
1366 recurs = type; 1372 recurs = type;
1367 if (mCompatVersion < 310 && type == rYearlyDay) { 1373 if (mCompatVersion < 310 && type == rYearlyDay) {
1368 mCompatRecurs = rYearlyDay; 1374 mCompatRecurs = rYearlyDay;
1369 recurs = rYearlyMonth; // convert old yearly-by-day to yearly-by-month 1375 recurs = rYearlyMonth; // convert old yearly-by-day to yearly-by-month
1370 feb29type = rMar1; // retain the same day number in the year 1376 feb29type = rMar1; // retain the same day number in the year
1371 } 1377 }
1372 1378
1373 mFeb29YearlyType = feb29type; 1379 mFeb29YearlyType = feb29type;
1374 rFreq = freq; 1380 rFreq = freq;
1375 rDuration = duration; 1381 rDuration = duration;
1376 if (type != rYearlyPos) 1382 if (type != rYearlyPos)
1377 rMonthPositions.clear(); 1383 rMonthPositions.clear();
1378 rMonthDays.clear(); 1384 rMonthDays.clear();
1379 if (mParent) mParent->updated(); 1385 if (mParent) mParent->updated();
1380} 1386}
1381 1387
1382int Recurrence::recurCalc(PeriodFunc func, QDateTime &endtime) const 1388int Recurrence::recurCalc(PeriodFunc func, QDateTime &endtime) const
1383{ 1389{
1384 QDate enddate = endtime.date(); 1390 QDate enddate = endtime.date();
1385 switch (func) { 1391 switch (func) {
1386 case END_DATE_AND_COUNT: 1392 case END_DATE_AND_COUNT:
1387 if (rDuration < 0) { 1393 if (rDuration < 0) {
1388 endtime = QDateTime(); 1394 endtime = QDateTime();
1389 return 0; // infinite recurrence 1395 return 0; // infinite recurrence
1390 } 1396 }
1391 if (rDuration == 0) { 1397 if (rDuration == 0) {
1392 endtime = rEndDateTime; 1398 endtime = rEndDateTime;
1393 func = COUNT_TO_DATE; 1399 func = COUNT_TO_DATE;
1394 } 1400 }
1395 break; 1401 break;
1396 case COUNT_TO_DATE: 1402 case COUNT_TO_DATE:
1397 // Count recurrences up to and including the specified date/time. 1403 // Count recurrences up to and including the specified date/time.
1398 if (endtime < mRecurStart) 1404 if (endtime < mRecurStart)
1399 return 0; 1405 return 0;
1400 if (rDuration == 0 && endtime > rEndDateTime) 1406 if (rDuration == 0 && endtime > rEndDateTime)
1401 enddate = rEndDateTime.date(); 1407 enddate = rEndDateTime.date();
1402 else if (!mFloats && mRecurStart.time() > endtime.time()) 1408 else if (!mFloats && mRecurStart.time() > endtime.time())
1403 enddate = enddate.addDays(-1); 1409 enddate = enddate.addDays(-1);
1404 break; 1410 break;
1405 case NEXT_AFTER_DATE: 1411 case NEXT_AFTER_DATE:
1406 // Find next recurrence AFTER endtime 1412 // Find next recurrence AFTER endtime
1407 if (endtime < mRecurStart) { 1413 if (endtime < mRecurStart) {
1408 endtime = mRecurStart; 1414 endtime = mRecurStart;
1409 return 1; 1415 return 1;
1410 } 1416 }
1411 if (rDuration == 0 && endtime >= rEndDateTime) { 1417 if (rDuration == 0 && endtime >= rEndDateTime) {
1412 endtime = QDateTime(); 1418 endtime = QDateTime();
1413 return 0; 1419 return 0;
1414 } 1420 }
1415 if (!mFloats && mRecurStart.time() > endtime.time()) 1421 if (!mFloats && mRecurStart.time() > endtime.time())
1416 enddate = enddate.addDays(-1); 1422 enddate = enddate.addDays(-1);
1417 break; 1423 break;
1418 default: 1424 default:
1419 endtime = QDateTime(); 1425 endtime = QDateTime();
1420 return 0; 1426 return 0;
1421 } 1427 }
1422 1428
1423 int count = 0; // default = error 1429 int count = 0; // default = error
1424 bool timed = false; 1430 bool timed = false;
1425 switch (recurs) { 1431 switch (recurs) {
1426 case rMinutely: 1432 case rMinutely:
1427 timed = true; 1433 timed = true;
1428 count = secondlyCalc(func, endtime, rFreq*60); 1434 count = secondlyCalc(func, endtime, rFreq*60);
1429 break; 1435 break;
1430 case rHourly: 1436 case rHourly:
1431 timed = true; 1437 timed = true;
1432 count = secondlyCalc(func, endtime, rFreq*3600); 1438 count = secondlyCalc(func, endtime, rFreq*3600);
1433 break; 1439 break;
1434 case rDaily: 1440 case rDaily:
1435 count = dailyCalc(func, enddate); 1441 count = dailyCalc(func, enddate);
1436 break; 1442 break;
1437 case rWeekly: 1443 case rWeekly:
1438 count = weeklyCalc(func, enddate); 1444 count = weeklyCalc(func, enddate);
1439 break; 1445 break;
1440 case rMonthlyPos: 1446 case rMonthlyPos:
1441 case rMonthlyDay: 1447 case rMonthlyDay:
1442 count = monthlyCalc(func, enddate); 1448 count = monthlyCalc(func, enddate);
1443 break; 1449 break;
1444 case rYearlyMonth: 1450 case rYearlyMonth:
1445 count = yearlyMonthCalc(func, enddate); 1451 count = yearlyMonthCalc(func, enddate);
1446 break; 1452 break;
1447 case rYearlyPos: 1453 case rYearlyPos:
1448 count = yearlyPosCalc(func, enddate); 1454 count = yearlyPosCalc(func, enddate);
1449 break; 1455 break;
1450 case rYearlyDay: 1456 case rYearlyDay:
1451 count = yearlyDayCalc(func, enddate); 1457 count = yearlyDayCalc(func, enddate);
1452 break; 1458 break;
1453 default: 1459 default:
1454 break; 1460 break;
1455 } 1461 }
1456 1462
1457 switch (func) { 1463 switch (func) {
1458 case END_DATE_AND_COUNT: 1464 case END_DATE_AND_COUNT:
1459 case NEXT_AFTER_DATE: 1465 case NEXT_AFTER_DATE:
1460 if (count == 0) 1466 if (count == 0)
1461 endtime = QDateTime(); 1467 endtime = QDateTime();
1462 else if (!timed) { 1468 else if (!timed) {
1463 endtime.setDate(enddate); 1469 endtime.setDate(enddate);
1464 endtime.setTime(mRecurStart.time()); 1470 endtime.setTime(mRecurStart.time());
1465 } 1471 }
1466 break; 1472 break;
1467 case COUNT_TO_DATE: 1473 case COUNT_TO_DATE:
1468 break; 1474 break;
1469 } 1475 }
1470 return count; 1476 return count;
1471} 1477}
1472 1478
1473int Recurrence::recurCalc(PeriodFunc func, QDate &enddate) const 1479int Recurrence::recurCalc(PeriodFunc func, QDate &enddate) const
1474{ 1480{
1475 QDateTime endtime(enddate, QTime(23,59,59)); 1481 QDateTime endtime(enddate, QTime(23,59,59));
1476 switch (func) { 1482 switch (func) {
1477 case END_DATE_AND_COUNT: 1483 case END_DATE_AND_COUNT:
1478 if (rDuration < 0) { 1484 if (rDuration < 0) {
1479 enddate = QDate(); 1485 enddate = QDate();
1480 return 0; // infinite recurrence 1486 return 0; // infinite recurrence
1481 } 1487 }
1482 if (rDuration == 0) { 1488 if (rDuration == 0) {
1483 enddate = rEndDateTime.date(); 1489 enddate = rEndDateTime.date();
1484 func = COUNT_TO_DATE; 1490 func = COUNT_TO_DATE;
1485 } 1491 }
1486 break; 1492 break;
1487 case COUNT_TO_DATE: 1493 case COUNT_TO_DATE:
1488 // Count recurrences up to and including the specified date. 1494 // Count recurrences up to and including the specified date.
1489 if (enddate < mRecurStart.date()) 1495 if (enddate < mRecurStart.date())
1490 return 0; 1496 return 0;
1491 if (rDuration == 0 && enddate > rEndDateTime.date()) { 1497 if (rDuration == 0 && enddate > rEndDateTime.date()) {
1492 enddate = rEndDateTime.date(); 1498 enddate = rEndDateTime.date();
1493 endtime.setDate(enddate); 1499 endtime.setDate(enddate);
1494 } 1500 }
1495 break; 1501 break;
1496 case NEXT_AFTER_DATE: 1502 case NEXT_AFTER_DATE:
1497 if (enddate < mRecurStart.date()) { 1503 if (enddate < mRecurStart.date()) {
1498 enddate = mRecurStart.date(); 1504 enddate = mRecurStart.date();
1499 return 1; 1505 return 1;
1500 } 1506 }
1501 if (rDuration == 0 && enddate >= rEndDateTime.date()) { 1507 if (rDuration == 0 && enddate >= rEndDateTime.date()) {
1502 enddate = QDate(); 1508 enddate = QDate();
1503 return 0; 1509 return 0;
1504 } 1510 }
1505 break; 1511 break;
1506 default: 1512 default:
1507 enddate = QDate(); 1513 enddate = QDate();
1508 return 0; 1514 return 0;
1509 } 1515 }
1510 1516
1511 int count = 0; // default = error 1517 int count = 0; // default = error
1512 bool timed = false; 1518 bool timed = false;
1513 switch (recurs) { 1519 switch (recurs) {
1514 case rMinutely: 1520 case rMinutely:
1515 timed = true; 1521 timed = true;
1516 count = secondlyCalc(func, endtime, rFreq*60); 1522 count = secondlyCalc(func, endtime, rFreq*60);
1517 break; 1523 break;
1518 case rHourly: 1524 case rHourly:
1519 timed = true; 1525 timed = true;
1520 count = secondlyCalc(func, endtime, rFreq*3600); 1526 count = secondlyCalc(func, endtime, rFreq*3600);
1521 break; 1527 break;
1522 case rDaily: 1528 case rDaily:
1523 count = dailyCalc(func, enddate); 1529 count = dailyCalc(func, enddate);
1524 break; 1530 break;
1525 case rWeekly: 1531 case rWeekly:
1526 count = weeklyCalc(func, enddate); 1532 count = weeklyCalc(func, enddate);
1527 break; 1533 break;
1528 case rMonthlyPos: 1534 case rMonthlyPos:
1529 case rMonthlyDay: 1535 case rMonthlyDay:
1530 count = monthlyCalc(func, enddate); 1536 count = monthlyCalc(func, enddate);
1531 break; 1537 break;
1532 case rYearlyMonth: 1538 case rYearlyMonth:
1533 count = yearlyMonthCalc(func, enddate); 1539 count = yearlyMonthCalc(func, enddate);
1534 break; 1540 break;
1535 case rYearlyPos: 1541 case rYearlyPos:
1536 count = yearlyPosCalc(func, enddate); 1542 count = yearlyPosCalc(func, enddate);
1537 break; 1543 break;
1538 case rYearlyDay: 1544 case rYearlyDay:
1539 count = yearlyDayCalc(func, enddate); 1545 count = yearlyDayCalc(func, enddate);
1540 break; 1546 break;
1541 default: 1547 default:
1542 break; 1548 break;
1543 } 1549 }
1544 1550
1545 switch (func) { 1551 switch (func) {
1546 case END_DATE_AND_COUNT: 1552 case END_DATE_AND_COUNT:
1547 case NEXT_AFTER_DATE: 1553 case NEXT_AFTER_DATE:
1548 if (count == 0) 1554 if (count == 0)
1549 endtime = QDate(); 1555 endtime = QDate();
1550 else if (timed) 1556 else if (timed)
1551 enddate = endtime.date(); 1557 enddate = endtime.date();
1552 break; 1558 break;
1553 case COUNT_TO_DATE: 1559 case COUNT_TO_DATE:
1554 break; 1560 break;
1555 } 1561 }
1556 return count; 1562 return count;
1557} 1563}
1558 1564
1559/* Find count and, depending on 'func', the end date/time of a secondly recurrence. 1565/* Find count and, depending on 'func', the end date/time of a secondly recurrence.
1560 * Reply = total number of occurrences up to 'endtime', or 0 if error. 1566 * Reply = total number of occurrences up to 'endtime', or 0 if error.
1561 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'endtime' is updated to the 1567 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'endtime' is updated to the
1562 * recurrence end date/time. 1568 * recurrence end date/time.
1563 */ 1569 */
1564int Recurrence::secondlyCalc(PeriodFunc func, QDateTime &endtime, int freq) const 1570int Recurrence::secondlyCalc(PeriodFunc func, QDateTime &endtime, int freq) const
1565{ 1571{
1566 switch (func) { 1572 switch (func) {
1567 case END_DATE_AND_COUNT: 1573 case END_DATE_AND_COUNT:
1568 endtime = mRecurStart.addSecs((rDuration + mRecurExDatesCount - 1) * freq); 1574 endtime = mRecurStart.addSecs((rDuration + mRecurExDatesCount - 1) * freq);
1569 return rDuration + mRecurExDatesCount; 1575 return rDuration + mRecurExDatesCount;
1570 case COUNT_TO_DATE: { 1576 case COUNT_TO_DATE: {
1571 int n = mRecurStart.secsTo(endtime)/freq + 1; 1577 int n = mRecurStart.secsTo(endtime)/freq + 1;
1572 if (rDuration > 0 && n > rDuration + mRecurExDatesCount) 1578 if (rDuration > 0 && n > rDuration + mRecurExDatesCount)
1573 return rDuration + mRecurExDatesCount; 1579 return rDuration + mRecurExDatesCount;
1574 return n; 1580 return n;
1575 } 1581 }
1576 case NEXT_AFTER_DATE: { 1582 case NEXT_AFTER_DATE: {
1577 int count = mRecurStart.secsTo(endtime) / freq + 2; 1583 int count = mRecurStart.secsTo(endtime) / freq + 2;
1578 if (rDuration > 0 && count > rDuration) 1584 if (rDuration > 0 && count > rDuration)
1579 return 0; 1585 return 0;
1580 endtime = mRecurStart.addSecs((count - 1)*freq); 1586 endtime = mRecurStart.addSecs((count - 1)*freq);
1581 return count; 1587 return count;
1582 } 1588 }
1583 } 1589 }
1584 return 0; 1590 return 0;
1585} 1591}
1586 1592
1587/* Find count and, depending on 'func', the end date of a daily recurrence. 1593/* Find count and, depending on 'func', the end date of a daily recurrence.
1588 * Reply = total number of occurrences up to 'enddate', or 0 if error. 1594 * Reply = total number of occurrences up to 'enddate', or 0 if error.
1589 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 1595 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
1590 * recurrence end date. 1596 * recurrence end date.
1591 */ 1597 */
1592int Recurrence::dailyCalc(PeriodFunc func, QDate &enddate) const 1598int Recurrence::dailyCalc(PeriodFunc func, QDate &enddate) const
1593{ 1599{
1594 QDate dStart = mRecurStart.date(); 1600 QDate dStart = mRecurStart.date();
1595 switch (func) { 1601 switch (func) {
1596 case END_DATE_AND_COUNT: 1602 case END_DATE_AND_COUNT:
1597 enddate = dStart.addDays((rDuration + mRecurExDatesCount - 1) * rFreq); 1603 enddate = dStart.addDays((rDuration + mRecurExDatesCount - 1) * rFreq);
1598 return rDuration + mRecurExDatesCount; 1604 return rDuration + mRecurExDatesCount;
1599 case COUNT_TO_DATE: { 1605 case COUNT_TO_DATE: {
1600 int n = dStart.daysTo(enddate)/rFreq + 1; 1606 int n = dStart.daysTo(enddate)/rFreq + 1;
1601 if (rDuration > 0 && n > rDuration + mRecurExDatesCount) 1607 if (rDuration > 0 && n > rDuration + mRecurExDatesCount)
1602 return rDuration + mRecurExDatesCount; 1608 return rDuration + mRecurExDatesCount;
1603 return n; 1609 return n;
1604 } 1610 }
1605 case NEXT_AFTER_DATE: { 1611 case NEXT_AFTER_DATE: {
1606 int count = dStart.daysTo(enddate) / rFreq + 2; 1612 int count = dStart.daysTo(enddate) / rFreq + 2;
1607 if (rDuration > 0 && count > rDuration) 1613 if (rDuration > 0 && count > rDuration)
1608 return 0; 1614 return 0;
1609 enddate = dStart.addDays((count - 1)*rFreq); 1615 enddate = dStart.addDays((count - 1)*rFreq);
1610 return count; 1616 return count;
1611 } 1617 }
1612 } 1618 }
1613 return 0; 1619 return 0;
1614} 1620}
1615 1621
1616/* Find count and, depending on 'func', the end date of a weekly recurrence. 1622/* Find count and, depending on 'func', the end date of a weekly recurrence.
1617 * Reply = total number of occurrences up to 'enddate', or 0 if error. 1623 * Reply = total number of occurrences up to 'enddate', or 0 if error.
1618 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 1624 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
1619 * recurrence end date. 1625 * recurrence end date.
1620 */ 1626 */
1621int Recurrence::weeklyCalc(PeriodFunc func, QDate &enddate) const 1627int Recurrence::weeklyCalc(PeriodFunc func, QDate &enddate) const
1622{ 1628{
1623 int daysPerWeek = 0; 1629 int daysPerWeek = 0;
1624 for (int i = 0; i < 7; ++i) { 1630 for (int i = 0; i < 7; ++i) {
1625 if (rDays.testBit((uint)i)) 1631 if (rDays.testBit((uint)i))
1626 ++daysPerWeek; 1632 ++daysPerWeek;
1627 } 1633 }
1628 if (!daysPerWeek) 1634 if (!daysPerWeek)
1629 return 0; // there are no days to recur on 1635 return 0; // there are no days to recur on
1630 1636
1631 switch (func) { 1637 switch (func) {
1632 case END_DATE_AND_COUNT: 1638 case END_DATE_AND_COUNT:
1633 return weeklyCalcEndDate(enddate, daysPerWeek); 1639 return weeklyCalcEndDate(enddate, daysPerWeek);
1634 case COUNT_TO_DATE: 1640 case COUNT_TO_DATE:
1635 return weeklyCalcToDate(enddate, daysPerWeek); 1641 return weeklyCalcToDate(enddate, daysPerWeek);
1636 case NEXT_AFTER_DATE: 1642 case NEXT_AFTER_DATE:
1637 return weeklyCalcNextAfter(enddate, daysPerWeek); 1643 return weeklyCalcNextAfter(enddate, daysPerWeek);
1638 } 1644 }
1639 return 0; 1645 return 0;
1640} 1646}
1641 1647
1642int Recurrence::weeklyCalcEndDate(QDate &enddate, int daysPerWeek) const 1648int Recurrence::weeklyCalcEndDate(QDate &enddate, int daysPerWeek) const
1643{ 1649{
1644 int startDayOfWeek = mRecurStart.date().dayOfWeek(); // 1..7 1650 int startDayOfWeek = mRecurStart.date().dayOfWeek(); // 1..7
1645 int countGone = 0; 1651 int countGone = 0;
1646 int daysGone = 0; 1652 int daysGone = 0;
1647 uint countTogo = rDuration + mRecurExDatesCount; 1653 uint countTogo = rDuration + mRecurExDatesCount;
1648 if (startDayOfWeek != rWeekStart) { 1654 if (startDayOfWeek != rWeekStart) {
1649 // Check what remains of the start week 1655 // Check what remains of the start week
1650 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { 1656 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
1651 ++daysGone; 1657 ++daysGone;
1652 if (rDays.testBit((uint)i)) { 1658 if (rDays.testBit((uint)i)) {
1653 ++countGone; 1659 ++countGone;
1654 if (--countTogo == 0) 1660 if (--countTogo == 0)
1655 break; 1661 break;
1656 } 1662 }
1657 } 1663 }
1658 daysGone += 7 * (rFreq - 1); 1664 daysGone += 7 * (rFreq - 1);
1659 } 1665 }
1660 if (countTogo) { 1666 if (countTogo) {
1661 // Skip the remaining whole weeks 1667 // Skip the remaining whole weeks
1662 // Leave at least 1 recurrence remaining, in order to get its date 1668 // Leave at least 1 recurrence remaining, in order to get its date
1663 int wholeWeeks = (countTogo - 1) / daysPerWeek; 1669 int wholeWeeks = (countTogo - 1) / daysPerWeek;
1664 daysGone += wholeWeeks * 7 * rFreq; 1670 daysGone += wholeWeeks * 7 * rFreq;
1665 countGone += wholeWeeks * daysPerWeek; 1671 countGone += wholeWeeks * daysPerWeek;
1666 countTogo -= wholeWeeks * daysPerWeek; 1672 countTogo -= wholeWeeks * daysPerWeek;
1667 // Check the last week in the recurrence 1673 // Check the last week in the recurrence
1668 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { 1674 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
1669 ++daysGone; 1675 ++daysGone;
1670 if (rDays.testBit((uint)i)) { 1676 if (rDays.testBit((uint)i)) {
1671 ++countGone; 1677 ++countGone;
1672 if (--countTogo == 0) 1678 if (--countTogo == 0)
1673 break; 1679 break;
1674 } 1680 }
1675 } 1681 }
1676 } 1682 }
1677 enddate = mRecurStart.date().addDays(daysGone); 1683 enddate = mRecurStart.date().addDays(daysGone);
1678 return countGone; 1684 return countGone;
1679} 1685}
1680 1686
1681int Recurrence::weeklyCalcToDate(const QDate &enddate, int daysPerWeek) const 1687int Recurrence::weeklyCalcToDate(const QDate &enddate, int daysPerWeek) const
1682{ 1688{
1683 QDate dStart = mRecurStart.date(); 1689 QDate dStart = mRecurStart.date();
1684 int startDayOfWeek = dStart.dayOfWeek(); // 1..7 1690 int startDayOfWeek = dStart.dayOfWeek(); // 1..7
1685 int countGone = 0; 1691 int countGone = 0;
1686 int daysGone = 0; 1692 int daysGone = 0;
1687 int totalDays = dStart.daysTo(enddate) + 1; 1693 int totalDays = dStart.daysTo(enddate) + 1;
1688 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 1694 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
1689 1695
1690 if (startDayOfWeek != rWeekStart) { 1696 if (startDayOfWeek != rWeekStart) {
1691 // Check what remains of the start week 1697 // Check what remains of the start week
1692 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { 1698 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
1693 if (rDays.testBit((uint)i)) { 1699 if (rDays.testBit((uint)i)) {
1694 if (++countGone >= countMax) 1700 if (++countGone >= countMax)
1695 return countMax; 1701 return countMax;
1696 } 1702 }
1697 if (++daysGone == totalDays) 1703 if (++daysGone == totalDays)
1698 return countGone; 1704 return countGone;
1699 } 1705 }
1700 daysGone += 7 * (rFreq - 1); 1706 daysGone += 7 * (rFreq - 1);
1701 if (daysGone >= totalDays) 1707 if (daysGone >= totalDays)
1702 return countGone; 1708 return countGone;
1703 } 1709 }
1704 // Skip the remaining whole weeks 1710 // Skip the remaining whole weeks
1705 int wholeWeeks = (totalDays - daysGone) / 7; 1711 int wholeWeeks = (totalDays - daysGone) / 7;
1706 countGone += (wholeWeeks / rFreq) * daysPerWeek; 1712 countGone += (wholeWeeks / rFreq) * daysPerWeek;
1707 if (countGone >= countMax) 1713 if (countGone >= countMax)
1708 return countMax; 1714 return countMax;
1709 daysGone += wholeWeeks * 7; 1715 daysGone += wholeWeeks * 7;
1710 if (daysGone >= totalDays // have we reached the end date? 1716 if (daysGone >= totalDays // have we reached the end date?
1711 || wholeWeeks % rFreq) // is end week a recurrence week? 1717 || wholeWeeks % rFreq) // is end week a recurrence week?
1712 return countGone; 1718 return countGone;
1713 1719
1714 // Check the last week in the recurrence 1720 // Check the last week in the recurrence
1715 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { 1721 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
1716 if (rDays.testBit((uint)i)) { 1722 if (rDays.testBit((uint)i)) {
1717 if (++countGone >= countMax) 1723 if (++countGone >= countMax)
1718 return countMax; 1724 return countMax;
1719 } 1725 }
1720 if (++daysGone == totalDays) 1726 if (++daysGone == totalDays)
1721 return countGone; 1727 return countGone;
1722 } 1728 }
1723 return countGone; 1729 return countGone;
1724} 1730}
1725 1731
1726int Recurrence::weeklyCalcNextAfter(QDate &enddate, int daysPerWeek) const 1732int Recurrence::weeklyCalcNextAfter(QDate &enddate, int daysPerWeek) const
1727{ 1733{
1728 QDate dStart = mRecurStart.date(); 1734 QDate dStart = mRecurStart.date();
1729 int startDayOfWeek = dStart.dayOfWeek(); // 1..7 1735 int startDayOfWeek = dStart.dayOfWeek(); // 1..7
1730 int totalDays = dStart.daysTo(enddate) + 1; 1736 int totalDays = dStart.daysTo(enddate) + 1;
1731 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 1737 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
1732 int countGone = 0; 1738 int countGone = 0;
1733 int daysGone = 0; 1739 int daysGone = 0;
1734 int recurWeeks; 1740 int recurWeeks;
1735 1741
1736 if (startDayOfWeek != rWeekStart) { 1742 if (startDayOfWeek != rWeekStart) {
1737 // Check what remains of the start week 1743 // Check what remains of the start week
1738 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) { 1744 for (int i = startDayOfWeek - 1; i != rWeekStart - 1; i = (i + 1) % 7) {
1739 ++daysGone; 1745 ++daysGone;
1740 if (rDays.testBit((uint)i)) { 1746 if (rDays.testBit((uint)i)) {
1741 ++countGone; 1747 ++countGone;
1742 if (daysGone > totalDays) 1748 if (daysGone > totalDays)
1743 goto ex; 1749 goto ex;
1744 if (--countTogo == 0) 1750 if (--countTogo == 0)
1745 return 0; 1751 return 0;
1746 } 1752 }
1747 } 1753 }
1748 daysGone += 7 * (rFreq - 1); 1754 daysGone += 7 * (rFreq - 1);
1749 } 1755 }
1750 1756
1751 // Skip the remaining whole weeks 1757 // Skip the remaining whole weeks
1752 recurWeeks = (totalDays - daysGone) / (7 * rFreq); 1758 recurWeeks = (totalDays - daysGone) / (7 * rFreq);
1753 if (recurWeeks) { 1759 if (recurWeeks) {
1754 int n = recurWeeks * daysPerWeek; 1760 int n = recurWeeks * daysPerWeek;
1755 if (static_cast<uint>(n) > countTogo) 1761 if (static_cast<uint>(n) > countTogo)
1756 return 0; // reached end of recurrence 1762 return 0; // reached end of recurrence
1757 countGone += n; 1763 countGone += n;
1758 countTogo -= n; 1764 countTogo -= n;
1759 daysGone += recurWeeks * 7 * rFreq; 1765 daysGone += recurWeeks * 7 * rFreq;
1760 } 1766 }
1761 1767
1762 // Check the last week or two in the recurrence 1768 // Check the last week or two in the recurrence
1763 for ( ; ; ) { 1769 for ( ; ; ) {
1764 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) { 1770 for (int i = rWeekStart - 1; ; i = (i + 1) % 7) {
1765 ++daysGone; 1771 ++daysGone;
1766 if (rDays.testBit((uint)i)) { 1772 if (rDays.testBit((uint)i)) {
1767 ++countGone; 1773 ++countGone;
1768 if (daysGone > totalDays) 1774 if (daysGone > totalDays)
1769 goto ex; 1775 goto ex;
1770 if (--countTogo == 0) 1776 if (--countTogo == 0)
1771 return 0; 1777 return 0;
1772 } 1778 }
1773 } 1779 }
1774 daysGone += 7 * (rFreq - 1); 1780 daysGone += 7 * (rFreq - 1);
1775 } 1781 }
1776ex: 1782ex:
1777 enddate = dStart.addDays(daysGone); 1783 enddate = dStart.addDays(daysGone);
1778 return countGone; 1784 return countGone;
1779} 1785}
1780 1786
1781/* Find count and, depending on 'func', the end date of a monthly recurrence. 1787/* Find count and, depending on 'func', the end date of a monthly recurrence.
1782 * Reply = total number of occurrences up to 'enddate', or 0 if error. 1788 * Reply = total number of occurrences up to 'enddate', or 0 if error.
1783 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 1789 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
1784 * recurrence end date. 1790 * recurrence end date.
1785 */ 1791 */
1786struct Recurrence::MonthlyData { 1792struct Recurrence::MonthlyData {
1787 const Recurrence *recurrence; 1793 const Recurrence *recurrence;
1788 int year; // current year 1794 int year; // current year
1789 int month; // current month 0..11 1795 int month; // current month 0..11
1790 int day; // current day of month 1..31 1796 int day; // current day of month 1..31
1791 bool varies; // true if recurring days vary between different months 1797 bool varies; // true if recurring days vary between different months
1792 private: 1798 private:
1793 QValueList<int> days28, days29, days30, days31; // recurring days in months of each length 1799 QValueList<int> days28, days29, days30, days31; // recurring days in months of each length
1794 QValueList<int> *recurDays[4]; 1800 QValueList<int> *recurDays[4];
1795 public: 1801 public:
1796 MonthlyData(const Recurrence* r, const QDate &date) 1802 MonthlyData(const Recurrence* r, const QDate &date)
1797 : recurrence(r), year(date.year()), month(date.month()-1), day(date.day()) 1803 : recurrence(r), year(date.year()), month(date.month()-1), day(date.day())
1798 { recurDays[0] = &days28; 1804 { recurDays[0] = &days28;
1799 recurDays[1] = &days29; 1805 recurDays[1] = &days29;
1800 recurDays[2] = &days30; 1806 recurDays[2] = &days30;
1801 recurDays[3] = &days31; 1807 recurDays[3] = &days31;
1802 varies = (recurrence->recurs == rMonthlyPos) 1808 varies = (recurrence->recurs == rMonthlyPos)
1803 ? true : recurrence->getMonthlyDayDays(days31, 31); 1809 ? true : recurrence->getMonthlyDayDays(days31, 31);
1804 } 1810 }
1805 const QValueList<int>* dayList() const { 1811 const QValueList<int>* dayList() const {
1806 if (!varies) 1812 if (!varies)
1807 return &days31; 1813 return &days31;
1808 QDate startOfMonth(year, month + 1, 1); 1814 QDate startOfMonth(year, month + 1, 1);
1809 int daysInMonth = startOfMonth.daysInMonth(); 1815 int daysInMonth = startOfMonth.daysInMonth();
1810 QValueList<int>* days = recurDays[daysInMonth - 28]; 1816 QValueList<int>* days = recurDays[daysInMonth - 28];
1811 if (recurrence->recurs == rMonthlyPos) 1817 if (recurrence->recurs == rMonthlyPos)
1812 recurrence->getMonthlyPosDays(*days, daysInMonth, startOfMonth.dayOfWeek()); 1818 recurrence->getMonthlyPosDays(*days, daysInMonth, startOfMonth.dayOfWeek());
1813 else if (days->isEmpty()) 1819 else if (days->isEmpty())
1814 recurrence->getMonthlyDayDays(*days, daysInMonth); 1820 recurrence->getMonthlyDayDays(*days, daysInMonth);
1815 return days; 1821 return days;
1816 } 1822 }
1817 int yearMonth() const { return year*12 + month; } 1823 int yearMonth() const { return year*12 + month; }
1818 void addMonths(int diff) { month += diff; year += month / 12; month %= 12; } 1824 void addMonths(int diff) { month += diff; year += month / 12; month %= 12; }
1819 QDate date() const { return QDate(year, month + 1, day); } 1825 QDate date() const { return QDate(year, month + 1, day); }
1820}; 1826};
1821 1827
1822int Recurrence::monthlyCalc(PeriodFunc func, QDate &enddate) const 1828int Recurrence::monthlyCalc(PeriodFunc func, QDate &enddate) const
1823{ 1829{
1824 if (recurs == rMonthlyPos && rMonthPositions.isEmpty() 1830 if (recurs == rMonthlyPos && rMonthPositions.isEmpty()
1825 || recurs == rMonthlyDay && rMonthDays.isEmpty()) 1831 || recurs == rMonthlyDay && rMonthDays.isEmpty())
1826 return 0; 1832 return 0;
1827 1833
1828 MonthlyData data(this, mRecurStart.date()); 1834 MonthlyData data(this, mRecurStart.date());
1829 switch (func) { 1835 switch (func) {
1830 case END_DATE_AND_COUNT: 1836 case END_DATE_AND_COUNT:
1831 return monthlyCalcEndDate(enddate, data); 1837 return monthlyCalcEndDate(enddate, data);
1832 case COUNT_TO_DATE: 1838 case COUNT_TO_DATE:
1833 return monthlyCalcToDate(enddate, data); 1839 return monthlyCalcToDate(enddate, data);
1834 case NEXT_AFTER_DATE: 1840 case NEXT_AFTER_DATE:
1835 return monthlyCalcNextAfter(enddate, data); 1841 return monthlyCalcNextAfter(enddate, data);
1836 } 1842 }
1837 return 0; 1843 return 0;
1838} 1844}
1839 1845
1840int Recurrence::monthlyCalcEndDate(QDate &enddate, MonthlyData &data) const 1846int Recurrence::monthlyCalcEndDate(QDate &enddate, MonthlyData &data) const
1841{ 1847{
1842 uint countTogo = rDuration + mRecurExDatesCount; 1848 uint countTogo = rDuration + mRecurExDatesCount;
1843 int countGone = 0; 1849 int countGone = 0;
1844 QValueList<int>::ConstIterator it; 1850 QValueList<int>::ConstIterator it;
1845 const QValueList<int>* days = data.dayList(); 1851 const QValueList<int>* days = data.dayList();
1846 1852
1847 if (data.day > 1) { 1853 if (data.day > 1) {
1848 // Check what remains of the start month 1854 // Check what remains of the start month
1849 for (it = days->begin(); it != days->end(); ++it) { 1855 for (it = days->begin(); it != days->end(); ++it) {
1850 if (*it >= data.day) { 1856 if (*it >= data.day) {
1851 ++countGone; 1857 ++countGone;
1852 if (--countTogo == 0) { 1858 if (--countTogo == 0) {
1853 data.day = *it; 1859 data.day = *it;
1854 break; 1860 break;
1855 } 1861 }
1856 } 1862 }
1857 } 1863 }
1858 if (countTogo) { 1864 if (countTogo) {
1859 data.day = 1; 1865 data.day = 1;
1860 data.addMonths(rFreq); 1866 data.addMonths(rFreq);
1861 } 1867 }
1862 } 1868 }
1863 if (countTogo) { 1869 if (countTogo) {
1864 if (data.varies) { 1870 if (data.varies) {
1865 // The number of recurrence days varies from month to month, 1871 // The number of recurrence days varies from month to month,
1866 // so we need to check month by month. 1872 // so we need to check month by month.
1867 for ( ; ; ) { 1873 for ( ; ; ) {
1868 days = data.dayList(); 1874 days = data.dayList();
1869 uint n = days->count(); // number of recurrence days in this month 1875 uint n = days->count(); // number of recurrence days in this month
1870 if (n >= countTogo) 1876 if (n >= countTogo)
1871 break; 1877 break;
1872 countTogo -= n; 1878 countTogo -= n;
1873 countGone += n; 1879 countGone += n;
1874 data.addMonths(rFreq); 1880 data.addMonths(rFreq);
1875 } 1881 }
1876 } else { 1882 } else {
1877 // The number of recurrences is the same every month, 1883 // The number of recurrences is the same every month,
1878 // so skip the month-by-month check. 1884 // so skip the month-by-month check.
1879 // Skip the remaining whole months, but leave at least 1885 // Skip the remaining whole months, but leave at least
1880 // 1 recurrence remaining, in order to get its date. 1886 // 1 recurrence remaining, in order to get its date.
1881 int daysPerMonth = days->count(); 1887 int daysPerMonth = days->count();
1882 int wholeMonths = (countTogo - 1) / daysPerMonth; 1888 int wholeMonths = (countTogo - 1) / daysPerMonth;
1883 data.addMonths(wholeMonths * rFreq); 1889 data.addMonths(wholeMonths * rFreq);
1884 countGone += wholeMonths * daysPerMonth; 1890 countGone += wholeMonths * daysPerMonth;
1885 countTogo -= wholeMonths * daysPerMonth; 1891 countTogo -= wholeMonths * daysPerMonth;
1886 } 1892 }
1887 if (countTogo) { 1893 if (countTogo) {
1888 // Check the last month in the recurrence 1894 // Check the last month in the recurrence
1889 for (it = days->begin(); it != days->end(); ++it) { 1895 for (it = days->begin(); it != days->end(); ++it) {
1890 ++countGone; 1896 ++countGone;
1891 if (--countTogo == 0) { 1897 if (--countTogo == 0) {
1892 data.day = *it; 1898 data.day = *it;
1893 break; 1899 break;
1894 } 1900 }
1895 } 1901 }
1896 } 1902 }
1897 } 1903 }
1898 enddate = data.date(); 1904 enddate = data.date();
1899 return countGone; 1905 return countGone;
1900} 1906}
1901 1907
1902int Recurrence::monthlyCalcToDate(const QDate &enddate, MonthlyData &data) const 1908int Recurrence::monthlyCalcToDate(const QDate &enddate, MonthlyData &data) const
1903{ 1909{
1904 int countGone = 0; 1910 int countGone = 0;
1905 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 1911 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
1906 int endYear = enddate.year(); 1912 int endYear = enddate.year();
1907 int endMonth = enddate.month() - 1; // zero-based 1913 int endMonth = enddate.month() - 1; // zero-based
1908 int endDay = enddate.day(); 1914 int endDay = enddate.day();
1909 int endYearMonth = endYear*12 + endMonth; 1915 int endYearMonth = endYear*12 + endMonth;
1910 QValueList<int>::ConstIterator it; 1916 QValueList<int>::ConstIterator it;
1911 const QValueList<int>* days = data.dayList(); 1917 const QValueList<int>* days = data.dayList();
1912 1918
1913 if (data.day > 1) { 1919 if (data.day > 1) {
1914 // Check what remains of the start month 1920 // Check what remains of the start month
1915 for (it = days->begin(); it != days->end(); ++it) { 1921 for (it = days->begin(); it != days->end(); ++it) {
1916 if (*it >= data.day) { 1922 if (*it >= data.day) {
1917 if (data.yearMonth() == endYearMonth && *it > endDay) 1923 if (data.yearMonth() == endYearMonth && *it > endDay)
1918 return countGone; 1924 return countGone;
1919 if (++countGone >= countMax) 1925 if (++countGone >= countMax)
1920 return countMax; 1926 return countMax;
1921 } 1927 }
1922 } 1928 }
1923 data.day = 1; 1929 data.day = 1;
1924 data.addMonths(rFreq); 1930 data.addMonths(rFreq);
1925 } 1931 }
1926 1932
1927 if (data.varies) { 1933 if (data.varies) {
1928 // The number of recurrence days varies from month to month, 1934 // The number of recurrence days varies from month to month,
1929 // so we need to check month by month. 1935 // so we need to check month by month.
1930 while (data.yearMonth() < endYearMonth) { 1936 while (data.yearMonth() < endYearMonth) {
1931 countGone += data.dayList()->count(); 1937 countGone += data.dayList()->count();
1932 if (countGone >= countMax) 1938 if (countGone >= countMax)
1933 return countMax; 1939 return countMax;
1934 data.addMonths(rFreq); 1940 data.addMonths(rFreq);
1935 } 1941 }
1936 days = data.dayList(); 1942 days = data.dayList();
1937 } else { 1943 } else {
1938 // The number of recurrences is the same every month, 1944 // The number of recurrences is the same every month,
1939 // so skip the month-by-month check. 1945 // so skip the month-by-month check.
1940 // Skip the remaining whole months. 1946 // Skip the remaining whole months.
1941 int daysPerMonth = days->count(); 1947 int daysPerMonth = days->count();
1942 int wholeMonths = endYearMonth - data.yearMonth(); 1948 int wholeMonths = endYearMonth - data.yearMonth();
1943 countGone += (wholeMonths / rFreq) * daysPerMonth; 1949 countGone += (wholeMonths / rFreq) * daysPerMonth;
1944 if (countGone >= countMax) 1950 if (countGone >= countMax)
1945 return countMax; 1951 return countMax;
1946 if (wholeMonths % rFreq) 1952 if (wholeMonths % rFreq)
1947 return countGone; // end year isn't a recurrence year 1953 return countGone; // end year isn't a recurrence year
1948 data.year = endYear; 1954 data.year = endYear;
1949 data.month = endMonth; 1955 data.month = endMonth;
1950 } 1956 }
1951 1957
1952 // Check the last month in the recurrence 1958 // Check the last month in the recurrence
1953 for (it = days->begin(); it != days->end(); ++it) { 1959 for (it = days->begin(); it != days->end(); ++it) {
1954 if (*it > endDay) 1960 if (*it > endDay)
1955 return countGone; 1961 return countGone;
1956 if (++countGone >= countMax) 1962 if (++countGone >= countMax)
1957 return countMax; 1963 return countMax;
1958 } 1964 }
1959 return countGone; 1965 return countGone;
1960} 1966}
1961 1967
1962int Recurrence::monthlyCalcNextAfter(QDate &enddate, MonthlyData &data) const 1968int Recurrence::monthlyCalcNextAfter(QDate &enddate, MonthlyData &data) const
1963{ 1969{
1964 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 1970 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
1965 int countGone = 0; 1971 int countGone = 0;
1966 int endYear = enddate.year(); 1972 int endYear = enddate.year();
1967 int endDay = enddate.day(); 1973 int endDay = enddate.day();
1968 int endYearMonth = endYear*12 + enddate.month() - 1; 1974 int endYearMonth = endYear*12 + enddate.month() - 1;
1969 QValueList<int>::ConstIterator it; 1975 QValueList<int>::ConstIterator it;
1970 const QValueList<int>* days = data.dayList(); 1976 const QValueList<int>* days = data.dayList();
1971 1977
1972 if (data.day > 1) { 1978 if (data.day > 1) {
1973 // Check what remains of the start month 1979 // Check what remains of the start month
1974 for (it = days->begin(); it != days->end(); ++it) { 1980 for (it = days->begin(); it != days->end(); ++it) {
1975 if (*it >= data.day) { 1981 if (*it >= data.day) {
1976 ++countGone; 1982 ++countGone;
1977 if (data.yearMonth() == endYearMonth && *it > endDay) { 1983 if (data.yearMonth() == endYearMonth && *it > endDay) {
1978 data.day = *it; 1984 data.day = *it;
1979 goto ex; 1985 goto ex;
1980 } 1986 }
1981 if (--countTogo == 0) 1987 if (--countTogo == 0)
1982 return 0; 1988 return 0;
1983 } 1989 }
1984 } 1990 }
1985 data.day = 1; 1991 data.day = 1;
1986 data.addMonths(rFreq); 1992 data.addMonths(rFreq);
1987 } 1993 }
1988 1994
1989 if (data.varies) { 1995 if (data.varies) {
1990 // The number of recurrence days varies from month to month, 1996 // The number of recurrence days varies from month to month,
1991 // so we need to check month by month. 1997 // so we need to check month by month.
1992 while (data.yearMonth() <= endYearMonth) { 1998 while (data.yearMonth() <= endYearMonth) {
1993 days = data.dayList(); 1999 days = data.dayList();
1994 uint n = days->count(); // number of recurrence days in this month 2000 uint n = days->count(); // number of recurrence days in this month
1995 if (data.yearMonth() == endYearMonth && days->last() > endDay) 2001 if (data.yearMonth() == endYearMonth && days->last() > endDay)
1996 break; 2002 break;
1997 if (n >= countTogo) 2003 if (n >= countTogo)
1998 return 0; 2004 return 0;
1999 countGone += n; 2005 countGone += n;
2000 countTogo -= n; 2006 countTogo -= n;
2001 data.addMonths(rFreq); 2007 data.addMonths(rFreq);
2002 } 2008 }
2003 days = data.dayList(); 2009 days = data.dayList();
2004 } else { 2010 } else {
2005 // The number of recurrences is the same every month, 2011 // The number of recurrences is the same every month,
2006 // so skip the month-by-month check. 2012 // so skip the month-by-month check.
2007 // Skip the remaining whole months to at least end year/month. 2013 // Skip the remaining whole months to at least end year/month.
2008 int daysPerMonth = days->count(); 2014 int daysPerMonth = days->count();
2009 int elapsed = endYearMonth - data.yearMonth(); 2015 int elapsed = endYearMonth - data.yearMonth();
2010 int recurMonths = (elapsed + rFreq - 1) / rFreq; 2016 int recurMonths = (elapsed + rFreq - 1) / rFreq;
2011 if (elapsed % rFreq == 0 && days->last() <= endDay) 2017 if (elapsed % rFreq == 0 && days->last() <= endDay)
2012 ++recurMonths; // required month is after endYearMonth 2018 ++recurMonths; // required month is after endYearMonth
2013 if (recurMonths) { 2019 if (recurMonths) {
2014 int n = recurMonths * daysPerMonth; 2020 int n = recurMonths * daysPerMonth;
2015 if (static_cast<uint>(n) > countTogo) 2021 if (static_cast<uint>(n) > countTogo)
2016 return 0; // reached end of recurrence 2022 return 0; // reached end of recurrence
2017 countTogo -= n; 2023 countTogo -= n;
2018 countGone += n; 2024 countGone += n;
2019 data.addMonths(recurMonths * rFreq); 2025 data.addMonths(recurMonths * rFreq);
2020 } 2026 }
2021 } 2027 }
2022 2028
2023 // Check the last month in the recurrence 2029 // Check the last month in the recurrence
2024 for (it = days->begin(); it != days->end(); ++it) { 2030 for (it = days->begin(); it != days->end(); ++it) {
2025 ++countGone; 2031 ++countGone;
2026 if (data.yearMonth() > endYearMonth || *it > endDay) { 2032 if (data.yearMonth() > endYearMonth || *it > endDay) {
2027 data.day = *it; 2033 data.day = *it;
2028 break; 2034 break;
2029 } 2035 }
2030 if (--countTogo == 0) 2036 if (--countTogo == 0)
2031 return 0; 2037 return 0;
2032 } 2038 }
2033ex: 2039ex:
2034 enddate = data.date(); 2040 enddate = data.date();
2035 return countGone; 2041 return countGone;
2036} 2042}
2037 2043
2038 2044
2039/* Find count and, depending on 'func', the end date of an annual recurrence by date. 2045/* Find count and, depending on 'func', the end date of an annual recurrence by date.
2040 * Reply = total number of occurrences up to 'enddate', or 0 if error. 2046 * Reply = total number of occurrences up to 'enddate', or 0 if error.
2041 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 2047 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
2042 * recurrence end date. 2048 * recurrence end date.
2043 */ 2049 */
2044struct Recurrence::YearlyMonthData { 2050struct Recurrence::YearlyMonthData {
2045 const Recurrence *recurrence; 2051 const Recurrence *recurrence;
2046 int year; // current year 2052 int year; // current year
2047 int month; // current month 1..12 2053 int month; // current month 1..12
2048 int day; // current day of month 1..31 2054 int day; // current day of month 1..31
2049 bool leapyear; // true if February 29th recurs and current year is a leap year 2055 bool leapyear; // true if February 29th recurs and current year is a leap year
2050 bool feb29; // true if February 29th recurs 2056 bool feb29; // true if February 29th recurs
2051 private: 2057 private:
2052 QValueList<int> months; // recurring months in non-leap years 1..12 2058 QValueList<int> months; // recurring months in non-leap years 1..12
2053 QValueList<int> leapMonths; // recurring months in leap years 1..12 2059 QValueList<int> leapMonths; // recurring months in leap years 1..12
2054 public: 2060 public:
2055 YearlyMonthData(const Recurrence* r, const QDate &date) 2061 YearlyMonthData(const Recurrence* r, const QDate &date)
2056 : recurrence(r), year(date.year()), month(date.month()), day(date.day()) 2062 : recurrence(r), year(date.year()), month(date.month()), day(date.day())
2057 { feb29 = recurrence->getYearlyMonthMonths(day, months, leapMonths); 2063 { feb29 = recurrence->getYearlyMonthMonths(day, months, leapMonths);
2058 leapyear = feb29 && QDate::leapYear(year); 2064 leapyear = feb29 && QDate::leapYear(year);
2059 } 2065 }
2060 const QValueList<int>* monthList() const 2066 const QValueList<int>* monthList() const
2061 { return leapyear ? &leapMonths : &months; } 2067 { return leapyear ? &leapMonths : &months; }
2062 const QValueList<int>* leapMonthList() const { return &leapMonths; } 2068 const QValueList<int>* leapMonthList() const { return &leapMonths; }
2063 QDate date() const { return QDate(year, month, day); } 2069 QDate date() const { return QDate(year, month, day); }
2064}; 2070};
2065 2071
2066int Recurrence::yearlyMonthCalc(PeriodFunc func, QDate &enddate) const 2072int Recurrence::yearlyMonthCalc(PeriodFunc func, QDate &enddate) const
2067{ 2073{
2068 if (rYearNums.isEmpty()) 2074 if (rYearNums.isEmpty())
2069 return 0; 2075 return 0;
2070 YearlyMonthData data(this, mRecurStart.date()); 2076 YearlyMonthData data(this, mRecurStart.date());
2071 switch (func) { 2077 switch (func) {
2072 case END_DATE_AND_COUNT: 2078 case END_DATE_AND_COUNT:
2073 return yearlyMonthCalcEndDate(enddate, data); 2079 return yearlyMonthCalcEndDate(enddate, data);
2074 case COUNT_TO_DATE: 2080 case COUNT_TO_DATE:
2075 return yearlyMonthCalcToDate(enddate, data); 2081 return yearlyMonthCalcToDate(enddate, data);
2076 case NEXT_AFTER_DATE: 2082 case NEXT_AFTER_DATE:
2077 return yearlyMonthCalcNextAfter(enddate, data); 2083 return yearlyMonthCalcNextAfter(enddate, data);
2078 } 2084 }
2079 return 0; 2085 return 0;
2080} 2086}
2081 2087
2082// Find total count and end date of an annual recurrence by date. 2088// Find total count and end date of an annual recurrence by date.
2083// Reply = total number of occurrences. 2089// Reply = total number of occurrences.
2084int Recurrence::yearlyMonthCalcEndDate(QDate &enddate, YearlyMonthData &data) const 2090int Recurrence::yearlyMonthCalcEndDate(QDate &enddate, YearlyMonthData &data) const
2085{ 2091{
2086 uint countTogo = rDuration + mRecurExDatesCount; 2092 uint countTogo = rDuration + mRecurExDatesCount;
2087 int countGone = 0; 2093 int countGone = 0;
2088 QValueList<int>::ConstIterator it; 2094 QValueList<int>::ConstIterator it;
2089 const QValueList<int>* mons = data.monthList(); // get recurring months for this year 2095 const QValueList<int>* mons = data.monthList(); // get recurring months for this year
2090 2096
2091 if (data.month > 1) { 2097 if (data.month > 1) {
2092 // Check what remains of the start year 2098 // Check what remains of the start year
2093 for (it = mons->begin(); it != mons->end(); ++it) { 2099 for (it = mons->begin(); it != mons->end(); ++it) {
2094 if (*it >= data.month) { 2100 if (*it >= data.month) {
2095 ++countGone; 2101 ++countGone;
2096 if (--countTogo == 0) { 2102 if (--countTogo == 0) {
2097 data.month = *it; 2103 data.month = *it;
2098 if (data.month == 2 && data.feb29 && !data.leapyear) { 2104 if (data.month == 2 && data.feb29 && !data.leapyear) {
2099 // The recurrence should end on February 29th, but it's a non-leap year 2105 // The recurrence should end on February 29th, but it's a non-leap year
2100 switch (mFeb29YearlyType) { 2106 switch (mFeb29YearlyType) {
2101 case rFeb28: 2107 case rFeb28:
2102 data.day = 28; 2108 data.day = 28;
2103 break; 2109 break;
2104 case rMar1: 2110 case rMar1:
2105 data.month = 3; 2111 data.month = 3;
2106 data.day = 1; 2112 data.day = 1;
2107 break; 2113 break;
2108 case rFeb29: 2114 case rFeb29:
2109 break; 2115 break;
2110 } 2116 }
2111 } 2117 }
2112 break; 2118 break;
2113 } 2119 }
2114 } 2120 }
2115 } 2121 }
2116 if (countTogo) { 2122 if (countTogo) {
2117 data.month = 1; 2123 data.month = 1;
2118 data.year += rFreq; 2124 data.year += rFreq;
2119 } 2125 }
2120 } 2126 }
2121 if (countTogo) { 2127 if (countTogo) {
2122 if (data.feb29 && mFeb29YearlyType == rFeb29) { 2128 if (data.feb29 && mFeb29YearlyType == rFeb29) {
2123 // The number of recurrences is different on leap years, 2129 // The number of recurrences is different on leap years,
2124 // so check year-by-year. 2130 // so check year-by-year.
2125 for ( ; ; ) { 2131 for ( ; ; ) {
2126 mons = data.monthList(); 2132 mons = data.monthList();
2127 uint n = mons->count(); 2133 uint n = mons->count();
2128 if (n >= countTogo) 2134 if (n >= countTogo)
2129 break; 2135 break;
2130 countTogo -= n; 2136 countTogo -= n;
2131 countGone += n; 2137 countGone += n;
2132 data.year += rFreq; 2138 data.year += rFreq;
2133 } 2139 }
2134 } else { 2140 } else {
2135 // The number of recurrences is the same every year, 2141 // The number of recurrences is the same every year,
2136 // so skip the year-by-year check. 2142 // so skip the year-by-year check.
2137 // Skip the remaining whole years, but leave at least 2143 // Skip the remaining whole years, but leave at least
2138 // 1 recurrence remaining, in order to get its date. 2144 // 1 recurrence remaining, in order to get its date.
2139 int monthsPerYear = mons->count(); 2145 int monthsPerYear = mons->count();
2140 int wholeYears = (countTogo - 1) / monthsPerYear; 2146 int wholeYears = (countTogo - 1) / monthsPerYear;
2141 data.year += wholeYears * rFreq; 2147 data.year += wholeYears * rFreq;
2142 countGone += wholeYears * monthsPerYear; 2148 countGone += wholeYears * monthsPerYear;
2143 countTogo -= wholeYears * monthsPerYear; 2149 countTogo -= wholeYears * monthsPerYear;
2144 } 2150 }
2145 if (countTogo) { 2151 if (countTogo) {
2146 // Check the last year in the recurrence 2152 // Check the last year in the recurrence
2147 for (it = mons->begin(); it != mons->end(); ++it) { 2153 for (it = mons->begin(); it != mons->end(); ++it) {
2148 ++countGone; 2154 ++countGone;
2149 if (--countTogo == 0) { 2155 if (--countTogo == 0) {
2150 data.month = *it; 2156 data.month = *it;
2151 if (data.month == 2 && data.feb29 && !QDate::leapYear(data.year)) { 2157 if (data.month == 2 && data.feb29 && !QDate::leapYear(data.year)) {
2152 // The recurrence should end on February 29th, but it's a non-leap year 2158 // The recurrence should end on February 29th, but it's a non-leap year
2153 switch (mFeb29YearlyType) { 2159 switch (mFeb29YearlyType) {
2154 case rFeb28: 2160 case rFeb28:
2155 data.day = 28; 2161 data.day = 28;
2156 break; 2162 break;
2157 case rMar1: 2163 case rMar1:
2158 data.month = 3; 2164 data.month = 3;
2159 data.day = 1; 2165 data.day = 1;
2160 break; 2166 break;
2161 case rFeb29: 2167 case rFeb29:
2162 break; 2168 break;
2163 } 2169 }
2164 } 2170 }
2165 break; 2171 break;
2166 } 2172 }
2167 } 2173 }
2168 } 2174 }
2169 } 2175 }
2170 enddate = data.date(); 2176 enddate = data.date();
2171 return countGone; 2177 return countGone;
2172} 2178}
2173 2179
2174// Find count of an annual recurrence by date. 2180// Find count of an annual recurrence by date.
2175// Reply = total number of occurrences up to 'enddate'. 2181// Reply = total number of occurrences up to 'enddate'.
2176int Recurrence::yearlyMonthCalcToDate(const QDate &enddate, YearlyMonthData &data) const 2182int Recurrence::yearlyMonthCalcToDate(const QDate &enddate, YearlyMonthData &data) const
2177{ 2183{
2178 int countGone = 0; 2184 int countGone = 0;
2179 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 2185 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
2180 int endYear = enddate.year(); 2186 int endYear = enddate.year();
2181 int endMonth = enddate.month(); 2187 int endMonth = enddate.month();
2182 int endDay = enddate.day(); 2188 int endDay = enddate.day();
2183 if (endDay < data.day) { 2189 if (endDay < data.day) {
2184 /* The end day of the month is earlier than the recurrence day of the month. 2190 /* The end day of the month is earlier than the recurrence day of the month.
2185 * If Feb 29th recurs and: 2191 * If Feb 29th recurs and:
2186 * 1) it recurs on Feb 28th in non-leap years, don't adjust the end month 2192 * 1) it recurs on Feb 28th in non-leap years, don't adjust the end month
2187 * if enddate is Feb 28th on a non-leap year. 2193 * if enddate is Feb 28th on a non-leap year.
2188 * 2) it recurs on Mar 1st in non-leap years, allow the end month to be 2194 * 2) it recurs on Mar 1st in non-leap years, allow the end month to be
2189 * adjusted to February, to simplify calculations. 2195 * adjusted to February, to simplify calculations.
2190 */ 2196 */
2191 if (data.feb29 && !QDate::leapYear(endYear) 2197 if (data.feb29 && !QDate::leapYear(endYear)
2192 && mFeb29YearlyType == rFeb28 && endDay == 28 && endMonth == 2) { 2198 && mFeb29YearlyType == rFeb28 && endDay == 28 && endMonth == 2) {
2193 } 2199 }
2194 else if (--endMonth == 0) { 2200 else if (--endMonth == 0) {
2195 endMonth = 12; 2201 endMonth = 12;
2196 --endYear; 2202 --endYear;
2197 } 2203 }
2198 } 2204 }
2199 QValueList<int>::ConstIterator it; 2205 QValueList<int>::ConstIterator it;
2200 const QValueList<int>* mons = data.monthList(); 2206 const QValueList<int>* mons = data.monthList();
2201 2207
2202 if (data.month > 1) { 2208 if (data.month > 1) {
2203 // Check what remains of the start year 2209 // Check what remains of the start year
2204 for (it = mons->begin(); it != mons->end(); ++it) { 2210 for (it = mons->begin(); it != mons->end(); ++it) {
2205 if (*it >= data.month) { 2211 if (*it >= data.month) {
2206 if (data.year == endYear && *it > endMonth) 2212 if (data.year == endYear && *it > endMonth)
2207 return countGone; 2213 return countGone;
2208 if (++countGone >= countMax) 2214 if (++countGone >= countMax)
2209 return countMax; 2215 return countMax;
2210 } 2216 }
2211 } 2217 }
2212 data.month = 1; 2218 data.month = 1;
2213 data.year += rFreq; 2219 data.year += rFreq;
2214 } 2220 }
2215 if (data.feb29 && mFeb29YearlyType == rFeb29) { 2221 if (data.feb29 && mFeb29YearlyType == rFeb29) {
2216 // The number of recurrences is different on leap years, 2222 // The number of recurrences is different on leap years,
2217 // so check year-by-year. 2223 // so check year-by-year.
2218 while (data.year < endYear) { 2224 while (data.year < endYear) {
2219 countGone += data.monthList()->count(); 2225 countGone += data.monthList()->count();
2220 if (countGone >= countMax) 2226 if (countGone >= countMax)
2221 return countMax; 2227 return countMax;
2222 data.year += rFreq; 2228 data.year += rFreq;
2223 } 2229 }
2224 mons = data.monthList(); 2230 mons = data.monthList();
2225 } else { 2231 } else {
2226 // The number of recurrences is the same every year, 2232 // The number of recurrences is the same every year,
2227 // so skip the year-by-year check. 2233 // so skip the year-by-year check.
2228 // Skip the remaining whole years. 2234 // Skip the remaining whole years.
2229 int monthsPerYear = mons->count(); 2235 int monthsPerYear = mons->count();
2230 int wholeYears = endYear - data.year; 2236 int wholeYears = endYear - data.year;
2231 countGone += (wholeYears / rFreq) * monthsPerYear; 2237 countGone += (wholeYears / rFreq) * monthsPerYear;
2232 if (countGone >= countMax) 2238 if (countGone >= countMax)
2233 return countMax; 2239 return countMax;
2234 if (wholeYears % rFreq) 2240 if (wholeYears % rFreq)
2235 return countGone; // end year isn't a recurrence year 2241 return countGone; // end year isn't a recurrence year
2236 data.year = endYear; 2242 data.year = endYear;
2237 } 2243 }
2238 2244
2239 // Check the last year in the recurrence 2245 // Check the last year in the recurrence
2240 for (it = mons->begin(); it != mons->end(); ++it) { 2246 for (it = mons->begin(); it != mons->end(); ++it) {
2241 if (*it > endMonth) 2247 if (*it > endMonth)
2242 return countGone; 2248 return countGone;
2243 if (++countGone >= countMax) 2249 if (++countGone >= countMax)
2244 return countMax; 2250 return countMax;
2245 } 2251 }
2246 return countGone; 2252 return countGone;
2247} 2253}
2248 2254
2249// Find count and date of first recurrence after 'enddate' of an annual recurrence by date. 2255// Find count and date of first recurrence after 'enddate' of an annual recurrence by date.
2250// Reply = total number of occurrences up to 'enddate'. 2256// Reply = total number of occurrences up to 'enddate'.
2251int Recurrence::yearlyMonthCalcNextAfter(QDate &enddate, YearlyMonthData &data) const 2257int Recurrence::yearlyMonthCalcNextAfter(QDate &enddate, YearlyMonthData &data) const
2252{ 2258{
2253 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 2259 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
2254 int countGone = 0; 2260 int countGone = 0;
2255 int endYear = enddate.year(); 2261 int endYear = enddate.year();
2256 int endMonth = enddate.month(); 2262 int endMonth = enddate.month();
2257 int endDay = enddate.day(); 2263 int endDay = enddate.day();
2258 bool mar1TooEarly = false; 2264 bool mar1TooEarly = false;
2259 bool feb28ok = false; 2265 bool feb28ok = false;
2260 if (endDay < data.day) { 2266 if (endDay < data.day) {
2261 if (data.feb29 && mFeb29YearlyType == rMar1 && endMonth == 3) 2267 if (data.feb29 && mFeb29YearlyType == rMar1 && endMonth == 3)
2262 mar1TooEarly = true; 2268 mar1TooEarly = true;
2263 if (data.feb29 && mFeb29YearlyType == rFeb28 && endMonth == 2 && endDay == 28) 2269 if (data.feb29 && mFeb29YearlyType == rFeb28 && endMonth == 2 && endDay == 28)
2264 feb28ok = true; 2270 feb28ok = true;
2265 else if (--endMonth == 0) { 2271 else if (--endMonth == 0) {
2266 endMonth = 12; 2272 endMonth = 12;
2267 --endYear; 2273 --endYear;
2268 } 2274 }
2269 } 2275 }
2270 QValueList<int>::ConstIterator it; 2276 QValueList<int>::ConstIterator it;
2271 const QValueList<int>* mons = data.monthList(); 2277 const QValueList<int>* mons = data.monthList();
2272 2278
2273 if (data.month > 1) { 2279 if (data.month > 1) {
2274 // Check what remains of the start year 2280 // Check what remains of the start year
2275 for (it = mons->begin(); it != mons->end(); ++it) { 2281 for (it = mons->begin(); it != mons->end(); ++it) {
2276 if (*it >= data.month) { 2282 if (*it >= data.month) {
2277 ++countGone; 2283 ++countGone;
2278 if (data.year == endYear 2284 if (data.year == endYear
2279 && ( *it > endMonth && (*it > 3 || !mar1TooEarly) 2285 && ( *it > endMonth && (*it > 3 || !mar1TooEarly)
2280 || *it == 2 && feb28ok && data.leapyear)) { 2286 || *it == 2 && feb28ok && data.leapyear)) {
2281 if (*it == 2 && data.feb29 && !data.leapyear) { 2287 if (*it == 2 && data.feb29 && !data.leapyear) {
2282 // The next recurrence should be on February 29th, but it's a non-leap year 2288 // The next recurrence should be on February 29th, but it's a non-leap year
2283 switch (mFeb29YearlyType) { 2289 switch (mFeb29YearlyType) {
2284 case rFeb28: 2290 case rFeb28:
2285 data.month = 2; 2291 data.month = 2;
2286 data.day = 28; 2292 data.day = 28;
2287 break; 2293 break;
2288 case rMar1: 2294 case rMar1:
2289 data.month = 3; 2295 data.month = 3;
2290 data.day = 1; 2296 data.day = 1;
2291 break; 2297 break;
2292 case rFeb29: // impossible in this context! 2298 case rFeb29: // impossible in this context!
2293 break; 2299 break;
2294 } 2300 }
2295 } 2301 }
2296 else 2302 else
2297 data.month = *it; 2303 data.month = *it;
2298 goto ex; 2304 goto ex;
2299 } 2305 }
2300 if (--countTogo == 0) 2306 if (--countTogo == 0)
2301 return 0; 2307 return 0;
2302 } 2308 }
2303 } 2309 }
2304 data.month = 1; 2310 data.month = 1;
2305 data.year += rFreq; 2311 data.year += rFreq;
2306 } 2312 }
2307 2313
2308 if (data.feb29 && mFeb29YearlyType == rFeb29) { 2314 if (data.feb29 && mFeb29YearlyType == rFeb29) {
2309 // The number of recurrences is different on leap years, 2315 // The number of recurrences is different on leap years,
2310 // so check year-by-year. 2316 // so check year-by-year.
2311 while (data.year <= endYear) { 2317 while (data.year <= endYear) {
2312 mons = data.monthList(); 2318 mons = data.monthList();
2313 if (data.year == endYear && mons->last() > endMonth) 2319 if (data.year == endYear && mons->last() > endMonth)
2314 break; 2320 break;
2315 uint n = mons->count(); 2321 uint n = mons->count();
2316 if (n >= countTogo) 2322 if (n >= countTogo)
2317 break; 2323 break;
2318 countTogo -= n; 2324 countTogo -= n;
2319 countGone += n; 2325 countGone += n;
2320 data.year += rFreq; 2326 data.year += rFreq;
2321 } 2327 }
2322 mons = data.monthList(); 2328 mons = data.monthList();
2323 } else { 2329 } else {
2324 // The number of recurrences is the same every year, 2330 // The number of recurrences is the same every year,
2325 // so skip the year-by-year check. 2331 // so skip the year-by-year check.
2326 // Skip the remaining whole years to at least endYear. 2332 // Skip the remaining whole years to at least endYear.
2327 int monthsPerYear = mons->count(); 2333 int monthsPerYear = mons->count();
2328 int recurYears = (endYear - data.year + rFreq - 1) / rFreq; 2334 int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
2329 if ((endYear - data.year)%rFreq == 0 2335 if ((endYear - data.year)%rFreq == 0
2330 && mons->last() <= endMonth) 2336 && mons->last() <= endMonth)
2331 ++recurYears; // required year is after endYear 2337 ++recurYears; // required year is after endYear
2332 if (recurYears) { 2338 if (recurYears) {
2333 int n = recurYears * monthsPerYear; 2339 int n = recurYears * monthsPerYear;
2334 if (static_cast<uint>(n) > countTogo) 2340 if (static_cast<uint>(n) > countTogo)
2335 return 0; // reached end of recurrence 2341 return 0; // reached end of recurrence
2336 countTogo -= n; 2342 countTogo -= n;
2337 countGone += n; 2343 countGone += n;
2338 data.year += recurYears * rFreq; 2344 data.year += recurYears * rFreq;
2339 } 2345 }
2340 } 2346 }
2341 2347
2342 // Check the last year in the recurrence 2348 // Check the last year in the recurrence
2343 for (it = mons->begin(); it != mons->end(); ++it) { 2349 for (it = mons->begin(); it != mons->end(); ++it) {
2344 ++countGone; 2350 ++countGone;
2345 if (data.year > endYear 2351 if (data.year > endYear
2346 || ( *it > endMonth && (*it > 3 || !mar1TooEarly) 2352 || ( *it > endMonth && (*it > 3 || !mar1TooEarly)
2347 || *it == 2 && feb28ok && QDate::leapYear(data.year))) { 2353 || *it == 2 && feb28ok && QDate::leapYear(data.year))) {
2348 if (*it == 2 && data.feb29 && !QDate::leapYear(data.year)) { 2354 if (*it == 2 && data.feb29 && !QDate::leapYear(data.year)) {
2349 // The next recurrence should be on February 29th, but it's a non-leap year 2355 // The next recurrence should be on February 29th, but it's a non-leap year
2350 switch (mFeb29YearlyType) { 2356 switch (mFeb29YearlyType) {
2351 case rFeb28: 2357 case rFeb28:
2352 data.month = 2; 2358 data.month = 2;
2353 data.day = 28; 2359 data.day = 28;
2354 break; 2360 break;
2355 case rMar1: 2361 case rMar1:
2356 data.month = 3; 2362 data.month = 3;
2357 data.day = 1; 2363 data.day = 1;
2358 break; 2364 break;
2359 case rFeb29: // impossible in this context! 2365 case rFeb29: // impossible in this context!
2360 break; 2366 break;
2361 } 2367 }
2362 } 2368 }
2363 else 2369 else
2364 data.month = *it; 2370 data.month = *it;
2365 break; 2371 break;
2366 } 2372 }
2367 if (--countTogo == 0) 2373 if (--countTogo == 0)
2368 return 0; 2374 return 0;
2369 } 2375 }
2370ex: 2376ex:
2371 enddate = data.date(); 2377 enddate = data.date();
2372 return countGone; 2378 return countGone;
2373} 2379}
2374 2380
2375 2381
2376/* Find count and, depending on 'func', the end date of an annual recurrence by date. 2382/* Find count and, depending on 'func', the end date of an annual recurrence by date.
2377 * Reply = total number of occurrences up to 'enddate', or 0 if error. 2383 * Reply = total number of occurrences up to 'enddate', or 0 if error.
2378 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 2384 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
2379 * recurrence end date. 2385 * recurrence end date.
2380 */ 2386 */
2381struct Recurrence::YearlyPosData { 2387struct Recurrence::YearlyPosData {
2382 const Recurrence *recurrence; 2388 const Recurrence *recurrence;
2383 int year; // current year 2389 int year; // current year
2384 int month; // current month 1..12 2390 int month; // current month 1..12
2385 int day; // current day of month 1..31 2391 int day; // current day of month 1..31
2386 int daysPerMonth; // number of days which recur each month, or -1 if variable 2392 int daysPerMonth; // number of days which recur each month, or -1 if variable
2387 int count; // number of days which recur each year, or -1 if variable 2393 int count; // number of days which recur each year, or -1 if variable
2388 bool varies; // true if number of days varies from year to year 2394 bool varies; // true if number of days varies from year to year
2389 private: 2395 private:
2390 mutable QValueList<int> days; 2396 mutable QValueList<int> days;
2391 public: 2397 public:
2392 YearlyPosData(const Recurrence* r, const QDate &date) 2398 YearlyPosData(const Recurrence* r, const QDate &date)
2393 : recurrence(r), year(date.year()), month(date.month()), day(date.day()), count(-1) 2399 : recurrence(r), year(date.year()), month(date.month()), day(date.day()), count(-1)
2394 { if ((daysPerMonth = r->countMonthlyPosDays()) > 0) 2400 { if ((daysPerMonth = r->countMonthlyPosDays()) > 0)
2395 count = daysPerMonth * r->rYearNums.count(); 2401 count = daysPerMonth * r->rYearNums.count();
2396 varies = (daysPerMonth < 0); 2402 varies = (daysPerMonth < 0);
2397 } 2403 }
2398 const QValueList<int>* dayList() const { 2404 const QValueList<int>* dayList() const {
2399 QDate startOfMonth(year, month, 1); 2405 QDate startOfMonth(year, month, 1);
2400 recurrence->getMonthlyPosDays(days, startOfMonth.daysInMonth(), startOfMonth.dayOfWeek()); 2406 recurrence->getMonthlyPosDays(days, startOfMonth.daysInMonth(), startOfMonth.dayOfWeek());
2401 return &days; 2407 return &days;
2402 } 2408 }
2403 int yearMonth() const { return year*12 + month - 1; } 2409 int yearMonth() const { return year*12 + month - 1; }
2404 void addMonths(int diff) { month += diff - 1; year += month / 12; month = month % 12 + 1; } 2410 void addMonths(int diff) { month += diff - 1; year += month / 12; month = month % 12 + 1; }
2405 QDate date() const { return QDate(year, month, day); } 2411 QDate date() const { return QDate(year, month, day); }
2406}; 2412};
2407 2413
2408int Recurrence::yearlyPosCalc(PeriodFunc func, QDate &enddate) const 2414int Recurrence::yearlyPosCalc(PeriodFunc func, QDate &enddate) const
2409{ 2415{
2410 if (rYearNums.isEmpty() || rMonthPositions.isEmpty()) 2416 if (rYearNums.isEmpty() || rMonthPositions.isEmpty())
2411 return 0; 2417 return 0;
2412 YearlyPosData data(this, mRecurStart.date()); 2418 YearlyPosData data(this, mRecurStart.date());
2413 switch (func) { 2419 switch (func) {
2414 case END_DATE_AND_COUNT: 2420 case END_DATE_AND_COUNT:
2415 return yearlyPosCalcEndDate(enddate, data); 2421 return yearlyPosCalcEndDate(enddate, data);
2416 case COUNT_TO_DATE: 2422 case COUNT_TO_DATE:
2417 return yearlyPosCalcToDate(enddate, data); 2423 return yearlyPosCalcToDate(enddate, data);
2418 case NEXT_AFTER_DATE: 2424 case NEXT_AFTER_DATE:
2419 return yearlyPosCalcNextAfter(enddate, data); 2425 return yearlyPosCalcNextAfter(enddate, data);
2420 } 2426 }
2421 return 0; 2427 return 0;
2422} 2428}
2423 2429
2424int Recurrence::yearlyPosCalcEndDate(QDate &enddate, YearlyPosData &data) const 2430int Recurrence::yearlyPosCalcEndDate(QDate &enddate, YearlyPosData &data) const
2425{ 2431{
2426 uint countTogo = rDuration + mRecurExDatesCount; 2432 uint countTogo = rDuration + mRecurExDatesCount;
2427 int countGone = 0; 2433 int countGone = 0;
2428 QValueList<int>::ConstIterator id; 2434 QValueList<int>::ConstIterator id;
2429 const QValueList<int>* days; 2435 const QValueList<int>* days;
2430 2436
2431 if (data.month > 1 || data.day > 1) { 2437 if (data.month > 1 || data.day > 1) {
2432 // Check what remains of the start year 2438 // Check what remains of the start year
2433 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2439 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2434 if (*im.current() >= data.month) { 2440 if (*im.current() >= data.month) {
2435 // Check what remains of the start month 2441 // Check what remains of the start month
2436 if (data.day > 1 || data.varies 2442 if (data.day > 1 || data.varies
2437 || static_cast<uint>(data.daysPerMonth) >= countTogo) { 2443 || static_cast<uint>(data.daysPerMonth) >= countTogo) {
2438 data.month = *im.current(); 2444 data.month = *im.current();
2439 days = data.dayList(); 2445 days = data.dayList();
2440 for (id = days->begin(); id != days->end(); ++id) { 2446 for (id = days->begin(); id != days->end(); ++id) {
2441 if (*id >= data.day) { 2447 if (*id >= data.day) {
2442 ++countGone; 2448 ++countGone;
2443 if (--countTogo == 0) { 2449 if (--countTogo == 0) {
2444 data.month = *im.current(); 2450 data.month = *im.current();
2445 data.day = *id; 2451 data.day = *id;
2446 goto ex; 2452 goto ex;
2447 } 2453 }
2448 } 2454 }
2449 } 2455 }
2450 data.day = 1; 2456 data.day = 1;
2451 } else { 2457 } else {
2452 // The number of days per month is constant, so skip 2458 // The number of days per month is constant, so skip
2453 // the whole month. 2459 // the whole month.
2454 countTogo -= data.daysPerMonth; 2460 countTogo -= data.daysPerMonth;
2455 countGone += data.daysPerMonth; 2461 countGone += data.daysPerMonth;
2456 } 2462 }
2457 } 2463 }
2458 } 2464 }
2459 data.month = 1; 2465 data.month = 1;
2460 data.year += rFreq; 2466 data.year += rFreq;
2461 } 2467 }
2462 2468
2463 if (data.varies) { 2469 if (data.varies) {
2464 // The number of recurrences varies from year to year. 2470 // The number of recurrences varies from year to year.
2465 for ( ; ; ) { 2471 for ( ; ; ) {
2466 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2472 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2467 data.month = *im.current(); 2473 data.month = *im.current();
2468 days = data.dayList(); 2474 days = data.dayList();
2469 int n = days->count(); 2475 int n = days->count();
2470 if (static_cast<uint>(n) >= countTogo) { 2476 if (static_cast<uint>(n) >= countTogo) {
2471 // Check the last month in the recurrence 2477 // Check the last month in the recurrence
2472 for (id = days->begin(); id != days->end(); ++id) { 2478 for (id = days->begin(); id != days->end(); ++id) {
2473 ++countGone; 2479 ++countGone;
2474 if (--countTogo == 0) { 2480 if (--countTogo == 0) {
2475 data.day = *id; 2481 data.day = *id;
2476 goto ex; 2482 goto ex;
2477 } 2483 }
2478 } 2484 }
2479 } 2485 }
2480 countTogo -= n; 2486 countTogo -= n;
2481 countGone += n; 2487 countGone += n;
2482 } 2488 }
2483 data.year += rFreq; 2489 data.year += rFreq;
2484 } 2490 }
2485 } else { 2491 } else {
2486 // The number of recurrences is the same every year, 2492 // The number of recurrences is the same every year,
2487 // so skip the year-by-year check. 2493 // so skip the year-by-year check.
2488 // Skip the remaining whole years, but leave at least 2494 // Skip the remaining whole years, but leave at least
2489 // 1 recurrence remaining, in order to get its date. 2495 // 1 recurrence remaining, in order to get its date.
2490 int wholeYears = (countTogo - 1) / data.count; 2496 int wholeYears = (countTogo - 1) / data.count;
2491 data.year += wholeYears * rFreq; 2497 data.year += wholeYears * rFreq;
2492 countGone += wholeYears * data.count; 2498 countGone += wholeYears * data.count;
2493 countTogo -= wholeYears * data.count; 2499 countTogo -= wholeYears * data.count;
2494 2500
2495 // Check the last year in the recurrence. 2501 // Check the last year in the recurrence.
2496 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2502 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2497 if (static_cast<uint>(data.daysPerMonth) >= countTogo) { 2503 if (static_cast<uint>(data.daysPerMonth) >= countTogo) {
2498 // Check the last month in the recurrence 2504 // Check the last month in the recurrence
2499 data.month = *im.current(); 2505 data.month = *im.current();
2500 days = data.dayList(); 2506 days = data.dayList();
2501 for (id = days->begin(); id != days->end(); ++id) { 2507 for (id = days->begin(); id != days->end(); ++id) {
2502 ++countGone; 2508 ++countGone;
2503 if (--countTogo == 0) { 2509 if (--countTogo == 0) {
2504 data.day = *id; 2510 data.day = *id;
2505 goto ex; 2511 goto ex;
2506 } 2512 }
2507 } 2513 }
2508 } 2514 }
2509 countTogo -= data.daysPerMonth; 2515 countTogo -= data.daysPerMonth;
2510 countGone += data.daysPerMonth; 2516 countGone += data.daysPerMonth;
2511 } 2517 }
2512 data.year += rFreq; 2518 data.year += rFreq;
2513 } 2519 }
2514ex: 2520ex:
2515 enddate = data.date(); 2521 enddate = data.date();
2516 return countGone; 2522 return countGone;
2517} 2523}
2518 2524
2519int Recurrence::yearlyPosCalcToDate(const QDate &enddate, YearlyPosData &data) const 2525int Recurrence::yearlyPosCalcToDate(const QDate &enddate, YearlyPosData &data) const
2520{ 2526{
2521 int countGone = 0; 2527 int countGone = 0;
2522 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 2528 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
2523 int endYear = enddate.year(); 2529 int endYear = enddate.year();
2524 int endMonth = enddate.month(); 2530 int endMonth = enddate.month();
2525 int endDay = enddate.day(); 2531 int endDay = enddate.day();
2526 if (endDay < data.day && --endMonth == 0) { 2532 if (endDay < data.day && --endMonth == 0) {
2527 endMonth = 12; 2533 endMonth = 12;
2528 --endYear; 2534 --endYear;
2529 } 2535 }
2530 int endYearMonth = endYear*12 + endMonth; 2536 int endYearMonth = endYear*12 + endMonth;
2531 QValueList<int>::ConstIterator id; 2537 QValueList<int>::ConstIterator id;
2532 const QValueList<int>* days; 2538 const QValueList<int>* days;
2533 2539
2534 if (data.month > 1 || data.day > 1) { 2540 if (data.month > 1 || data.day > 1) {
2535 // Check what remains of the start year 2541 // Check what remains of the start year
2536 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2542 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2537 if (*im.current() >= data.month) { 2543 if (*im.current() >= data.month) {
2538 data.month = *im.current(); 2544 data.month = *im.current();
2539 if (data.yearMonth() > endYearMonth) 2545 if (data.yearMonth() > endYearMonth)
2540 return countGone; 2546 return countGone;
2541 // Check what remains of the start month 2547 // Check what remains of the start month
2542 bool lastMonth = (data.yearMonth() == endYearMonth); 2548 bool lastMonth = (data.yearMonth() == endYearMonth);
2543 if (lastMonth || data.day > 1 || data.varies) { 2549 if (lastMonth || data.day > 1 || data.varies) {
2544 days = data.dayList(); 2550 days = data.dayList();
2545 if (lastMonth || data.day > 1) { 2551 if (lastMonth || data.day > 1) {
2546 for (id = days->begin(); id != days->end(); ++id) { 2552 for (id = days->begin(); id != days->end(); ++id) {
2547 if (*id >= data.day) { 2553 if (*id >= data.day) {
2548 if (lastMonth && *id > endDay) 2554 if (lastMonth && *id > endDay)
2549 return countGone; 2555 return countGone;
2550 if (++countGone >= countMax) 2556 if (++countGone >= countMax)
2551 return countMax; 2557 return countMax;
2552 } 2558 }
2553 } 2559 }
2554 } else { 2560 } else {
2555 countGone += days->count(); 2561 countGone += days->count();
2556 if (countGone >= countMax) 2562 if (countGone >= countMax)
2557 return countMax; 2563 return countMax;
2558 } 2564 }
2559 data.day = 1; 2565 data.day = 1;
2560 } else { 2566 } else {
2561 // The number of days per month is constant, so skip 2567 // The number of days per month is constant, so skip
2562 // the whole month. 2568 // the whole month.
2563 countGone += data.daysPerMonth; 2569 countGone += data.daysPerMonth;
2564 if (countGone >= countMax) 2570 if (countGone >= countMax)
2565 return countMax; 2571 return countMax;
2566 } 2572 }
2567 } 2573 }
2568 } 2574 }
2569 data.month = 1; 2575 data.month = 1;
2570 data.year += rFreq; 2576 data.year += rFreq;
2571 } 2577 }
2572 2578
2573 if (data.varies) { 2579 if (data.varies) {
2574 // The number of recurrences varies from year to year. 2580 // The number of recurrences varies from year to year.
2575 for ( ; ; ) { 2581 for ( ; ; ) {
2576 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2582 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2577 data.month = *im.current(); 2583 data.month = *im.current();
2578 days = data.dayList(); 2584 days = data.dayList();
2579 if (data.yearMonth() >= endYearMonth) { 2585 if (data.yearMonth() >= endYearMonth) {
2580 if (data.yearMonth() > endYearMonth) 2586 if (data.yearMonth() > endYearMonth)
2581 return countGone; 2587 return countGone;
2582 // Check the last month in the recurrence 2588 // Check the last month in the recurrence
2583 for (id = days->begin(); id != days->end(); ++id) { 2589 for (id = days->begin(); id != days->end(); ++id) {
2584 if (*id > endDay) 2590 if (*id > endDay)
2585 return countGone; 2591 return countGone;
2586 if (++countGone >= countMax) 2592 if (++countGone >= countMax)
2587 return countMax; 2593 return countMax;
2588 } 2594 }
2589 } else { 2595 } else {
2590 countGone += days->count(); 2596 countGone += days->count();
2591 if (countGone >= countMax) 2597 if (countGone >= countMax)
2592 return countMax; 2598 return countMax;
2593 } 2599 }
2594 } 2600 }
2595 data.year += rFreq; 2601 data.year += rFreq;
2596 } 2602 }
2597 } else { 2603 } else {
2598 // The number of recurrences is the same every year, 2604 // The number of recurrences is the same every year,
2599 // so skip the year-by-year check. 2605 // so skip the year-by-year check.
2600 // Skip the remaining whole years, but leave at least 2606 // Skip the remaining whole years, but leave at least
2601 // 1 recurrence remaining, in order to get its date. 2607 // 1 recurrence remaining, in order to get its date.
2602 int wholeYears = endYear - data.year; 2608 int wholeYears = endYear - data.year;
2603 countGone += (wholeYears / rFreq) * data.count; 2609 countGone += (wholeYears / rFreq) * data.count;
2604 if (countGone >= countMax) 2610 if (countGone >= countMax)
2605 return countMax; 2611 return countMax;
2606 if (wholeYears % rFreq) 2612 if (wholeYears % rFreq)
2607 return countGone; // end year isn't a recurrence year 2613 return countGone; // end year isn't a recurrence year
2608 data.year = endYear; 2614 data.year = endYear;
2609 2615
2610 // Check the last year in the recurrence. 2616 // Check the last year in the recurrence.
2611 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2617 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2612 data.month = *im.current(); 2618 data.month = *im.current();
2613 if (data.month >= endMonth) { 2619 if (data.month >= endMonth) {
2614 if (data.month > endMonth) 2620 if (data.month > endMonth)
2615 return countGone; 2621 return countGone;
2616 // Check the last month in the recurrence 2622 // Check the last month in the recurrence
2617 days = data.dayList(); 2623 days = data.dayList();
2618 for (id = days->begin(); id != days->end(); ++id) { 2624 for (id = days->begin(); id != days->end(); ++id) {
2619 if (*id > endDay) 2625 if (*id > endDay)
2620 return countGone; 2626 return countGone;
2621 if (++countGone >= countMax) 2627 if (++countGone >= countMax)
2622 return countMax; 2628 return countMax;
2623 } 2629 }
2624 } else { 2630 } else {
2625 countGone += data.daysPerMonth; 2631 countGone += data.daysPerMonth;
2626 if (countGone >= countMax) 2632 if (countGone >= countMax)
2627 return countMax; 2633 return countMax;
2628 } 2634 }
2629 } 2635 }
2630 } 2636 }
2631 return countGone; 2637 return countGone;
2632} 2638}
2633 2639
2634int Recurrence::yearlyPosCalcNextAfter(QDate &enddate, YearlyPosData &data) const 2640int Recurrence::yearlyPosCalcNextAfter(QDate &enddate, YearlyPosData &data) const
2635{ 2641{
2636 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 2642 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
2637 int countGone = 0; 2643 int countGone = 0;
2638 int endYear = enddate.year(); 2644 int endYear = enddate.year();
2639 int endMonth = enddate.month(); 2645 int endMonth = enddate.month();
2640 int endDay = enddate.day(); 2646 int endDay = enddate.day();
2641 if (endDay < data.day && --endMonth == 0) { 2647 if (endDay < data.day && --endMonth == 0) {
2642 endMonth = 12; 2648 endMonth = 12;
2643 --endYear; 2649 --endYear;
2644 } 2650 }
2645 int endYearMonth = endYear*12 + endMonth; 2651 int endYearMonth = endYear*12 + endMonth;
2646 QValueList<int>::ConstIterator id; 2652 QValueList<int>::ConstIterator id;
2647 const QValueList<int>* days; 2653 const QValueList<int>* days;
2648 2654
2649 if (data.varies) { 2655 if (data.varies) {
2650 // The number of recurrences varies from year to year. 2656 // The number of recurrences varies from year to year.
2651 for ( ; ; ) { 2657 for ( ; ; ) {
2652 // Check the next year 2658 // Check the next year
2653 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2659 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2654 if (*im.current() >= data.month) { 2660 if (*im.current() >= data.month) {
2655 // Check the next month 2661 // Check the next month
2656 data.month = *im.current(); 2662 data.month = *im.current();
2657 int ended = data.yearMonth() - endYearMonth; 2663 int ended = data.yearMonth() - endYearMonth;
2658 days = data.dayList(); 2664 days = data.dayList();
2659 if (ended >= 0 || data.day > 1) { 2665 if (ended >= 0 || data.day > 1) {
2660 // This is the start or end month, so check each day 2666 // This is the start or end month, so check each day
2661 for (id = days->begin(); id != days->end(); ++id) { 2667 for (id = days->begin(); id != days->end(); ++id) {
2662 if (*id >= data.day) { 2668 if (*id >= data.day) {
2663 ++countGone; 2669 ++countGone;
2664 if (ended > 0 || (ended == 0 && *id > endDay)) { 2670 if (ended > 0 || (ended == 0 && *id > endDay)) {
2665 data.day = *id; 2671 data.day = *id;
2666 goto ex; 2672 goto ex;
2667 } 2673 }
2668 if (--countTogo == 0) 2674 if (--countTogo == 0)
2669 return 0; 2675 return 0;
2670 } 2676 }
2671 } 2677 }
2672 } else { 2678 } else {
2673 // Skip the whole month 2679 // Skip the whole month
2674 uint n = days->count(); 2680 uint n = days->count();
2675 if (n >= countTogo) 2681 if (n >= countTogo)
2676 return 0; 2682 return 0;
2677 countGone += n; 2683 countGone += n;
2678 } 2684 }
2679 data.day = 1; // we've checked the start month now 2685 data.day = 1; // we've checked the start month now
2680 } 2686 }
2681 } 2687 }
2682 data.month = 1; // we've checked the start year now 2688 data.month = 1; // we've checked the start year now
2683 data.year += rFreq; 2689 data.year += rFreq;
2684 } 2690 }
2685 } else { 2691 } else {
2686 // The number of recurrences is the same every year. 2692 // The number of recurrences is the same every year.
2687 if (data.month > 1 || data.day > 1) { 2693 if (data.month > 1 || data.day > 1) {
2688 // Check what remains of the start year 2694 // Check what remains of the start year
2689 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2695 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2690 if (*im.current() >= data.month) { 2696 if (*im.current() >= data.month) {
2691 // Check what remains of the start month 2697 // Check what remains of the start month
2692 data.month = *im.current(); 2698 data.month = *im.current();
2693 int ended = data.yearMonth() - endYearMonth; 2699 int ended = data.yearMonth() - endYearMonth;
2694 if (ended >= 0 || data.day > 1) { 2700 if (ended >= 0 || data.day > 1) {
2695 // This is the start or end month, so check each day 2701 // This is the start or end month, so check each day
2696 days = data.dayList(); 2702 days = data.dayList();
2697 for (id = days->begin(); id != days->end(); ++id) { 2703 for (id = days->begin(); id != days->end(); ++id) {
2698 if (*id >= data.day) { 2704 if (*id >= data.day) {
2699 ++countGone; 2705 ++countGone;
2700 if (ended > 0 || (ended == 0 && *id > endDay)) { 2706 if (ended > 0 || (ended == 0 && *id > endDay)) {
2701 data.day = *id; 2707 data.day = *id;
2702 goto ex; 2708 goto ex;
2703 } 2709 }
2704 if (--countTogo == 0) 2710 if (--countTogo == 0)
2705 return 0; 2711 return 0;
2706 } 2712 }
2707 } 2713 }
2708 data.day = 1; // we've checked the start month now 2714 data.day = 1; // we've checked the start month now
2709 } else { 2715 } else {
2710 // Skip the whole month. 2716 // Skip the whole month.
2711 if (static_cast<uint>(data.daysPerMonth) >= countTogo) 2717 if (static_cast<uint>(data.daysPerMonth) >= countTogo)
2712 return 0; 2718 return 0;
2713 countGone += data.daysPerMonth; 2719 countGone += data.daysPerMonth;
2714 } 2720 }
2715 } 2721 }
2716 } 2722 }
2717 data.year += rFreq; 2723 data.year += rFreq;
2718 } 2724 }
2719 // Skip the remaining whole years to at least endYear. 2725 // Skip the remaining whole years to at least endYear.
2720 int recurYears = (endYear - data.year + rFreq - 1) / rFreq; 2726 int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
2721 if ((endYear - data.year)%rFreq == 0 2727 if ((endYear - data.year)%rFreq == 0
2722 && *rYearNums.getLast() <= endMonth) 2728 && *rYearNums.getLast() <= endMonth)
2723 ++recurYears; // required year is after endYear 2729 ++recurYears; // required year is after endYear
2724 if (recurYears) { 2730 if (recurYears) {
2725 int n = recurYears * data.count; 2731 int n = recurYears * data.count;
2726 if (static_cast<uint>(n) > countTogo) 2732 if (static_cast<uint>(n) > countTogo)
2727 return 0; // reached end of recurrence 2733 return 0; // reached end of recurrence
2728 countTogo -= n; 2734 countTogo -= n;
2729 countGone += n; 2735 countGone += n;
2730 data.year += recurYears * rFreq; 2736 data.year += recurYears * rFreq;
2731 } 2737 }
2732 2738
2733 // Check the last year in the recurrence 2739 // Check the last year in the recurrence
2734 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) { 2740 for (QPtrListIterator<int> im(rYearNums); im.current(); ++im) {
2735 data.month = *im.current(); 2741 data.month = *im.current();
2736 int ended = data.yearMonth() - endYearMonth; 2742 int ended = data.yearMonth() - endYearMonth;
2737 if (ended >= 0) { 2743 if (ended >= 0) {
2738 // This is the end month, so check each day 2744 // This is the end month, so check each day
2739 days = data.dayList(); 2745 days = data.dayList();
2740 for (id = days->begin(); id != days->end(); ++id) { 2746 for (id = days->begin(); id != days->end(); ++id) {
2741 ++countGone; 2747 ++countGone;
2742 if (ended > 0 || (ended == 0 && *id > endDay)) { 2748 if (ended > 0 || (ended == 0 && *id > endDay)) {
2743 data.day = *id; 2749 data.day = *id;
2744 goto ex; 2750 goto ex;
2745 } 2751 }
2746 if (--countTogo == 0) 2752 if (--countTogo == 0)
2747 return 0; 2753 return 0;
2748 } 2754 }
2749 } else { 2755 } else {
2750 // Skip the whole month. 2756 // Skip the whole month.
2751 if (static_cast<uint>(data.daysPerMonth) >= countTogo) 2757 if (static_cast<uint>(data.daysPerMonth) >= countTogo)
2752 return 0; 2758 return 0;
2753 countGone += data.daysPerMonth; 2759 countGone += data.daysPerMonth;
2754 } 2760 }
2755 } 2761 }
2756 } 2762 }
2757ex: 2763ex:
2758 enddate = data.date(); 2764 enddate = data.date();
2759 return countGone; 2765 return countGone;
2760} 2766}
2761 2767
2762 2768
2763/* Find count and, depending on 'func', the end date of an annual recurrence by day. 2769/* Find count and, depending on 'func', the end date of an annual recurrence by day.
2764 * Reply = total number of occurrences up to 'enddate', or 0 if error. 2770 * Reply = total number of occurrences up to 'enddate', or 0 if error.
2765 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the 2771 * If 'func' = END_DATE_AND_COUNT or NEXT_AFTER_DATE, 'enddate' is updated to the
2766 * recurrence end date. 2772 * recurrence end date.
2767 */ 2773 */
2768struct Recurrence::YearlyDayData { 2774struct Recurrence::YearlyDayData {
2769 int year; // current year 2775 int year; // current year
2770 int day; // current day of year 1..366 2776 int day; // current day of year 1..366
2771 bool varies; // true if day 366 recurs 2777 bool varies; // true if day 366 recurs
2772 private: 2778 private:
2773 int daycount; 2779 int daycount;
2774 public: 2780 public:
2775 YearlyDayData(const Recurrence* r, const QDate &date) 2781 YearlyDayData(const Recurrence* r, const QDate &date)
2776 : year(date.year()), day(date.dayOfYear()), varies(*r->rYearNums.getLast() == 366), 2782 : year(date.year()), day(date.dayOfYear()), varies(*r->rYearNums.getLast() == 366),
2777 daycount(r->rYearNums.count()) { } 2783 daycount(r->rYearNums.count()) { }
2778 bool leapYear() const { return QDate::leapYear(year); } 2784 bool leapYear() const { return QDate::leapYear(year); }
2779 int dayCount() const { return daycount - (varies && !QDate::leapYear(year) ? 1 : 0); } 2785 int dayCount() const { return daycount - (varies && !QDate::leapYear(year) ? 1 : 0); }
2780 bool isMaxDayCount() const { return !varies || QDate::leapYear(year); } 2786 bool isMaxDayCount() const { return !varies || QDate::leapYear(year); }
2781 QDate date() const { return QDate(year, 1, 1).addDays(day - 1); } 2787 QDate date() const { return QDate(year, 1, 1).addDays(day - 1); }
2782}; 2788};
2783 2789
2784int Recurrence::yearlyDayCalc(PeriodFunc func, QDate &enddate) const 2790int Recurrence::yearlyDayCalc(PeriodFunc func, QDate &enddate) const
2785{ 2791{
2786 if (rYearNums.isEmpty()) 2792 if (rYearNums.isEmpty())
2787 return 0; 2793 return 0;
2788 YearlyDayData data(this, mRecurStart.date()); 2794 YearlyDayData data(this, mRecurStart.date());
2789 switch (func) { 2795 switch (func) {
2790 case END_DATE_AND_COUNT: 2796 case END_DATE_AND_COUNT:
2791 return yearlyDayCalcEndDate(enddate, data); 2797 return yearlyDayCalcEndDate(enddate, data);
2792 case COUNT_TO_DATE: 2798 case COUNT_TO_DATE:
2793 return yearlyDayCalcToDate(enddate, data); 2799 return yearlyDayCalcToDate(enddate, data);
2794 case NEXT_AFTER_DATE: 2800 case NEXT_AFTER_DATE:
2795 return yearlyDayCalcNextAfter(enddate, data); 2801 return yearlyDayCalcNextAfter(enddate, data);
2796 } 2802 }
2797 return 0; 2803 return 0;
2798} 2804}
2799 2805
2800int Recurrence::yearlyDayCalcEndDate(QDate &enddate, YearlyDayData &data) const 2806int Recurrence::yearlyDayCalcEndDate(QDate &enddate, YearlyDayData &data) const
2801{ 2807{
2802 uint countTogo = rDuration + mRecurExDatesCount; 2808 uint countTogo = rDuration + mRecurExDatesCount;
2803 int countGone = 0; 2809 int countGone = 0;
2804 2810
2805 if (data.day > 1) { 2811 if (data.day > 1) {
2806 // Check what remains of the start year 2812 // Check what remains of the start year
2807 bool leapOK = data.isMaxDayCount(); 2813 bool leapOK = data.isMaxDayCount();
2808 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2814 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2809 int d = *it.current(); 2815 int d = *it.current();
2810 if (d >= data.day && (leapOK || d < 366)) { 2816 if (d >= data.day && (leapOK || d < 366)) {
2811 ++countGone; 2817 ++countGone;
2812 if (--countTogo == 0) { 2818 if (--countTogo == 0) {
2813 data.day = d; 2819 data.day = d;
2814 goto ex; 2820 goto ex;
2815 } 2821 }
2816 } 2822 }
2817 } 2823 }
2818 data.day = 1; 2824 data.day = 1;
2819 data.year += rFreq; 2825 data.year += rFreq;
2820 } 2826 }
2821 2827
2822 if (data.varies) { 2828 if (data.varies) {
2823 // The number of recurrences is different in leap years, 2829 // The number of recurrences is different in leap years,
2824 // so check year-by-year. 2830 // so check year-by-year.
2825 for ( ; ; ) { 2831 for ( ; ; ) {
2826 uint n = data.dayCount(); 2832 uint n = data.dayCount();
2827 if (n >= countTogo) 2833 if (n >= countTogo)
2828 break; 2834 break;
2829 countTogo -= n; 2835 countTogo -= n;
2830 countGone += n; 2836 countGone += n;
2831 data.year += rFreq; 2837 data.year += rFreq;
2832 } 2838 }
2833 } else { 2839 } else {
2834 // The number of recurrences is the same every year, 2840 // The number of recurrences is the same every year,
2835 // so skip the year-by-year check. 2841 // so skip the year-by-year check.
2836 // Skip the remaining whole years, but leave at least 2842 // Skip the remaining whole years, but leave at least
2837 // 1 recurrence remaining, in order to get its date. 2843 // 1 recurrence remaining, in order to get its date.
2838 int daysPerYear = rYearNums.count(); 2844 int daysPerYear = rYearNums.count();
2839 int wholeYears = (countTogo - 1) / daysPerYear; 2845 int wholeYears = (countTogo - 1) / daysPerYear;
2840 data.year += wholeYears * rFreq; 2846 data.year += wholeYears * rFreq;
2841 countGone += wholeYears * daysPerYear; 2847 countGone += wholeYears * daysPerYear;
2842 countTogo -= wholeYears * daysPerYear; 2848 countTogo -= wholeYears * daysPerYear;
2843 } 2849 }
2844 if (countTogo) { 2850 if (countTogo) {
2845 // Check the last year in the recurrence 2851 // Check the last year in the recurrence
2846 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2852 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2847 ++countGone; 2853 ++countGone;
2848 if (--countTogo == 0) { 2854 if (--countTogo == 0) {
2849 data.day = *it.current(); 2855 data.day = *it.current();
2850 break; 2856 break;
2851 } 2857 }
2852 } 2858 }
2853 } 2859 }
2854ex: 2860ex:
2855 enddate = data.date(); 2861 enddate = data.date();
2856 return countGone; 2862 return countGone;
2857} 2863}
2858 2864
2859int Recurrence::yearlyDayCalcToDate(const QDate &enddate, YearlyDayData &data) const 2865int Recurrence::yearlyDayCalcToDate(const QDate &enddate, YearlyDayData &data) const
2860{ 2866{
2861 int countGone = 0; 2867 int countGone = 0;
2862 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX; 2868 int countMax = (rDuration > 0) ? rDuration + mRecurExDatesCount : INT_MAX;
2863 int endYear = enddate.year(); 2869 int endYear = enddate.year();
2864 int endDay = enddate.dayOfYear(); 2870 int endDay = enddate.dayOfYear();
2865 2871
2866 if (data.day > 1) { 2872 if (data.day > 1) {
2867 // Check what remains of the start year 2873 // Check what remains of the start year
2868 bool leapOK = data.isMaxDayCount(); 2874 bool leapOK = data.isMaxDayCount();
2869 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2875 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2870 int d = *it.current(); 2876 int d = *it.current();
2871 if (d >= data.day && (leapOK || d < 366)) { 2877 if (d >= data.day && (leapOK || d < 366)) {
2872 if (data.year == endYear && d > endDay) 2878 if (data.year == endYear && d > endDay)
2873 return countGone; 2879 return countGone;
2874 if (++countGone >= countMax) 2880 if (++countGone >= countMax)
2875 return countMax; 2881 return countMax;
2876 } 2882 }
2877 } 2883 }
2878 data.day = 1; 2884 data.day = 1;
2879 data.year += rFreq; 2885 data.year += rFreq;
2880 } 2886 }
2881 2887
2882 if (data.varies) { 2888 if (data.varies) {
2883 // The number of recurrences is different in leap years, 2889 // The number of recurrences is different in leap years,
2884 // so check year-by-year. 2890 // so check year-by-year.
2885 while (data.year < endYear) { 2891 while (data.year < endYear) {
2886 uint n = data.dayCount(); 2892 uint n = data.dayCount();
2887 countGone += n; 2893 countGone += n;
2888 if (countGone >= countMax) 2894 if (countGone >= countMax)
2889 return countMax; 2895 return countMax;
2890 data.year += rFreq; 2896 data.year += rFreq;
2891 } 2897 }
2892 if (data.year > endYear) 2898 if (data.year > endYear)
2893 return countGone; 2899 return countGone;
2894 } else { 2900 } else {
2895 // The number of recurrences is the same every year. 2901 // The number of recurrences is the same every year.
2896 // Skip the remaining whole years. 2902 // Skip the remaining whole years.
2897 int wholeYears = endYear - data.year; 2903 int wholeYears = endYear - data.year;
2898 countGone += (wholeYears / rFreq) * rYearNums.count(); 2904 countGone += (wholeYears / rFreq) * rYearNums.count();
2899 if (countGone >= countMax) 2905 if (countGone >= countMax)
2900 return countMax; 2906 return countMax;
2901 if (wholeYears % rFreq) 2907 if (wholeYears % rFreq)
2902 return countGone; // end year isn't a recurrence year 2908 return countGone; // end year isn't a recurrence year
2903 data.year = endYear; 2909 data.year = endYear;
2904 } 2910 }
2905 2911
2906 if (data.year <= endYear) { 2912 if (data.year <= endYear) {
2907 // Check the last year in the recurrence 2913 // Check the last year in the recurrence
2908 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2914 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2909 if (*it.current() > endDay) 2915 if (*it.current() > endDay)
2910 return countGone; 2916 return countGone;
2911 if (++countGone >= countMax) 2917 if (++countGone >= countMax)
2912 return countMax; 2918 return countMax;
2913 } 2919 }
2914 } 2920 }
2915 return countGone; 2921 return countGone;
2916} 2922}
2917 2923
2918int Recurrence::yearlyDayCalcNextAfter(QDate &enddate, YearlyDayData &data) const 2924int Recurrence::yearlyDayCalcNextAfter(QDate &enddate, YearlyDayData &data) const
2919{ 2925{
2920 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX; 2926 uint countTogo = (rDuration > 0) ? rDuration + mRecurExDatesCount : UINT_MAX;
2921 int countGone = 0; 2927 int countGone = 0;
2922 int endYear = enddate.year(); 2928 int endYear = enddate.year();
2923 int endDay = enddate.dayOfYear(); 2929 int endDay = enddate.dayOfYear();
2924 2930
2925 if (data.day > 1) { 2931 if (data.day > 1) {
2926 // Check what remains of the start year 2932 // Check what remains of the start year
2927 bool leapOK = data.isMaxDayCount(); 2933 bool leapOK = data.isMaxDayCount();
2928 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2934 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2929 int d = *it.current(); 2935 int d = *it.current();
2930 if (d >= data.day && (leapOK || d < 366)) { 2936 if (d >= data.day && (leapOK || d < 366)) {
2931 ++countGone; 2937 ++countGone;
2932 if (data.year == endYear && d > endDay) { 2938 if (data.year == endYear && d > endDay) {
2933 data.day = d; 2939 data.day = d;
2934 goto ex; 2940 goto ex;
2935 } 2941 }
2936 if (--countTogo == 0) 2942 if (--countTogo == 0)
2937 return 0; 2943 return 0;
2938 } 2944 }
2939 } 2945 }
2940 data.day = 1; 2946 data.day = 1;
2941 data.year += rFreq; 2947 data.year += rFreq;
2942 } 2948 }
2943 2949
2944 if (data.varies) { 2950 if (data.varies) {
2945 // The number of recurrences is different in leap years, 2951 // The number of recurrences is different in leap years,
2946 // so check year-by-year. 2952 // so check year-by-year.
2947 while (data.year <= endYear) { 2953 while (data.year <= endYear) {
2948 uint n = data.dayCount(); 2954 uint n = data.dayCount();
2949 if (data.year == endYear && *rYearNums.getLast() > endDay) 2955 if (data.year == endYear && *rYearNums.getLast() > endDay)
2950 break; 2956 break;
2951 if (n >= countTogo) 2957 if (n >= countTogo)
2952 break; 2958 break;
2953 countTogo -= n; 2959 countTogo -= n;
2954 countGone += n; 2960 countGone += n;
2955 data.year += rFreq; 2961 data.year += rFreq;
2956 } 2962 }
2957 } else { 2963 } else {
2958 // The number of recurrences is the same every year, 2964 // The number of recurrences is the same every year,
2959 // so skip the year-by-year check. 2965 // so skip the year-by-year check.
2960 // Skip the remaining whole years to at least endYear. 2966 // Skip the remaining whole years to at least endYear.
2961 int daysPerYear = rYearNums.count(); 2967 int daysPerYear = rYearNums.count();
2962 int recurYears = (endYear - data.year + rFreq - 1) / rFreq; 2968 int recurYears = (endYear - data.year + rFreq - 1) / rFreq;
2963 if ((endYear - data.year)%rFreq == 0 2969 if ((endYear - data.year)%rFreq == 0
2964 && *rYearNums.getLast() <= endDay) 2970 && *rYearNums.getLast() <= endDay)
2965 ++recurYears; // required year is after endYear 2971 ++recurYears; // required year is after endYear
2966 if (recurYears) { 2972 if (recurYears) {
2967 int n = recurYears * daysPerYear; 2973 int n = recurYears * daysPerYear;
2968 if (static_cast<uint>(n) > countTogo) 2974 if (static_cast<uint>(n) > countTogo)
2969 return 0; // reached end of recurrence 2975 return 0; // reached end of recurrence
2970 countTogo -= n; 2976 countTogo -= n;
2971 countGone += n; 2977 countGone += n;
2972 data.year += recurYears * rFreq; 2978 data.year += recurYears * rFreq;
2973 } 2979 }
2974 } 2980 }
2975 2981
2976 // Check the last year in the recurrence 2982 // Check the last year in the recurrence
2977 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 2983 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
2978 ++countGone; 2984 ++countGone;
2979 int d = *it.current(); 2985 int d = *it.current();
2980 if (data.year > endYear || d > endDay) { 2986 if (data.year > endYear || d > endDay) {
2981 data.day = d; 2987 data.day = d;
2982 break; 2988 break;
2983 } 2989 }
2984 if (--countTogo == 0) 2990 if (--countTogo == 0)
2985 return 0; 2991 return 0;
2986 } 2992 }
2987ex: 2993ex:
2988 enddate = data.date(); 2994 enddate = data.date();
2989 return countGone; 2995 return countGone;
2990} 2996}
2991 2997
2992// Get the days in this month which recur, in numerical order. 2998// Get the days in this month which recur, in numerical order.
2993// Parameters: daysInMonth = number of days in this month 2999// Parameters: daysInMonth = number of days in this month
2994// startDayOfWeek = day of week for first day of month. 3000// startDayOfWeek = day of week for first day of month.
2995void Recurrence::getMonthlyPosDays(QValueList<int> &list, int daysInMonth, int startDayOfWeek) const 3001void Recurrence::getMonthlyPosDays(QValueList<int> &list, int daysInMonth, int startDayOfWeek) const
2996{ 3002{
2997 list.clear(); 3003 list.clear();
2998 int endDayOfWeek = (startDayOfWeek + daysInMonth - 2) % 7 + 1; 3004 int endDayOfWeek = (startDayOfWeek + daysInMonth - 2) % 7 + 1;
2999 // Go through the list, compiling a bit list of actual day numbers 3005 // Go through the list, compiling a bit list of actual day numbers
3000 Q_UINT32 days = 0; 3006 Q_UINT32 days = 0;
3001 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) { 3007 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) {
3002 int weeknum = pos.current()->rPos - 1; // get 0-based week number 3008 int weeknum = pos.current()->rPos - 1; // get 0-based week number
3003 QBitArray &rdays = pos.current()->rDays; 3009 QBitArray &rdays = pos.current()->rDays;
3004 if (pos.current()->negative) { 3010 if (pos.current()->negative) {
3005 // nth days before the end of the month 3011 // nth days before the end of the month
3006 for (uint i = 1; i <= 7; ++i) { 3012 for (uint i = 1; i <= 7; ++i) {
3007 if (rdays.testBit(i - 1)) { 3013 if (rdays.testBit(i - 1)) {
3008 int day = daysInMonth - weeknum*7 - (endDayOfWeek - i + 7) % 7; 3014 int day = daysInMonth - weeknum*7 - (endDayOfWeek - i + 7) % 7;
3009 if (day > 0) 3015 if (day > 0)
3010 days |= 1 << (day - 1); 3016 days |= 1 << (day - 1);
3011 } 3017 }
3012 } 3018 }
3013 } else { 3019 } else {
3014 // nth days after the start of the month 3020 // nth days after the start of the month
3015 for (uint i = 1; i <= 7; ++i) { 3021 for (uint i = 1; i <= 7; ++i) {
3016 if (rdays.testBit(i - 1)) { 3022 if (rdays.testBit(i - 1)) {
3017 int day = 1 + weeknum*7 + (i - startDayOfWeek + 7) % 7; 3023 int day = 1 + weeknum*7 + (i - startDayOfWeek + 7) % 7;
3018 if (day <= daysInMonth) 3024 if (day <= daysInMonth)
3019 days |= 1 << (day - 1); 3025 days |= 1 << (day - 1);
3020 } 3026 }
3021 } 3027 }
3022 } 3028 }
3023 } 3029 }
3024 // Compile the ordered list 3030 // Compile the ordered list
3025 Q_UINT32 mask = 1; 3031 Q_UINT32 mask = 1;
3026 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) { 3032 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) {
3027 if (days & mask) 3033 if (days & mask)
3028 list.append(i + 1); 3034 list.append(i + 1);
3029 } 3035 }
3030} 3036}
3031 3037
3032// Get the number of days in the month which recur. 3038// Get the number of days in the month which recur.
3033// Reply = -1 if the number varies from month to month. 3039// Reply = -1 if the number varies from month to month.
3034int Recurrence::countMonthlyPosDays() const 3040int Recurrence::countMonthlyPosDays() const
3035{ 3041{
3036 int count = 0; 3042 int count = 0;
3037 Q_UINT8 positive[5] = { 0, 0, 0, 0, 0 }; 3043 Q_UINT8 positive[5] = { 0, 0, 0, 0, 0 };
3038 Q_UINT8 negative[4] = { 0, 0, 0, 0 }; 3044 Q_UINT8 negative[4] = { 0, 0, 0, 0 };
3039 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) { 3045 for (QPtrListIterator<rMonthPos> pos(rMonthPositions); pos.current(); ++pos) {
3040 int weeknum = pos.current()->rPos; 3046 int weeknum = pos.current()->rPos;
3041 Q_UINT8* wk; 3047 Q_UINT8* wk;
3042 if (pos.current()->negative) { 3048 if (pos.current()->negative) {
3043 // nth days before the end of the month 3049 // nth days before the end of the month
3044 if (weeknum > 4) 3050 if (weeknum > 4)
3045 return -1; // days in 5th week are often missing 3051 return -1; // days in 5th week are often missing
3046 wk = &negative[4 - weeknum]; 3052 wk = &negative[4 - weeknum];
3047 } else { 3053 } else {
3048 // nth days after the start of the month 3054 // nth days after the start of the month
3049 if (weeknum > 4) 3055 if (weeknum > 4)
3050 return -1; // days in 5th week are often missing 3056 return -1; // days in 5th week are often missing
3051 wk = &positive[weeknum - 1]; 3057 wk = &positive[weeknum - 1];
3052 } 3058 }
3053 QBitArray &rdays = pos.current()->rDays; 3059 QBitArray &rdays = pos.current()->rDays;
3054 for (uint i = 0; i < 7; ++i) { 3060 for (uint i = 0; i < 7; ++i) {
3055 if (rdays.testBit(i)) { 3061 if (rdays.testBit(i)) {
3056 ++count; 3062 ++count;
3057 *wk |= (1 << i); 3063 *wk |= (1 << i);
3058 } 3064 }
3059 } 3065 }
3060 } 3066 }
3061 // Check for any possible days which could be duplicated by 3067 // Check for any possible days which could be duplicated by
3062 // a positive and a negative position. 3068 // a positive and a negative position.
3063 for (int i = 0; i < 4; ++i) { 3069 for (int i = 0; i < 4; ++i) {
3064 if (negative[i] & (positive[i] | positive[i+1])) 3070 if (negative[i] & (positive[i] | positive[i+1]))
3065 return -1; 3071 return -1;
3066 } 3072 }
3067 return count; 3073 return count;
3068} 3074}
3069 3075
3070// Get the days in this month which recur, in numerical order. 3076// Get the days in this month which recur, in numerical order.
3071// Reply = true if day numbers varies from month to month. 3077// Reply = true if day numbers varies from month to month.
3072bool Recurrence::getMonthlyDayDays(QValueList<int> &list, int daysInMonth) const 3078bool Recurrence::getMonthlyDayDays(QValueList<int> &list, int daysInMonth) const
3073{ 3079{
3074 list.clear(); 3080 list.clear();
3075 bool variable = false; 3081 bool variable = false;
3076 Q_UINT32 days = 0; 3082 Q_UINT32 days = 0;
3077 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { 3083 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
3078 int day = *it.current(); 3084 int day = *it.current();
3079 if (day > 0) { 3085 if (day > 0) {
3080 // date in the month 3086 // date in the month
3081 if (day <= daysInMonth) 3087 if (day <= daysInMonth)
3082 days |= 1 << (day - 1); 3088 days |= 1 << (day - 1);
3083 if (day > 28 && day <= 31) 3089 if (day > 28 && day <= 31)
3084 variable = true; // this date does not appear in some months 3090 variable = true; // this date does not appear in some months
3085 } else if (day < 0) { 3091 } else if (day < 0) {
3086 // days before the end of the month 3092 // days before the end of the month
3087 variable = true; // this date varies depending on the month length 3093 variable = true; // this date varies depending on the month length
3088 day = daysInMonth + day; // zero-based day of month 3094 day = daysInMonth + day; // zero-based day of month
3089 if (day >= 0) 3095 if (day >= 0)
3090 days |= 1 << day; 3096 days |= 1 << day;
3091 } 3097 }
3092 } 3098 }
3093 // Compile the ordered list 3099 // Compile the ordered list
3094 Q_UINT32 mask = 1; 3100 Q_UINT32 mask = 1;
3095 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) { 3101 for (int i = 0; i < daysInMonth; mask <<= 1, ++i) {
3096 if (days & mask) 3102 if (days & mask)
3097 list.append(i + 1); 3103 list.append(i + 1);
3098 } 3104 }
3099 return variable; 3105 return variable;
3100} 3106}
3101 3107
3102// Get the months which recur, in numerical order, for both leap years and non-leap years. 3108// Get the months which recur, in numerical order, for both leap years and non-leap years.
3103// N.B. If February 29th recurs on March 1st in non-leap years, February (not March) is 3109// N.B. If February 29th recurs on March 1st in non-leap years, February (not March) is
3104// included in the non-leap year month list. 3110// included in the non-leap year month list.
3105// Reply = true if February 29th also recurs. 3111// Reply = true if February 29th also recurs.
3106bool Recurrence::getYearlyMonthMonths(int day, QValueList<int> &list, QValueList<int> &leaplist) const 3112bool Recurrence::getYearlyMonthMonths(int day, QValueList<int> &list, QValueList<int> &leaplist) const
3107{ 3113{
3108 list.clear(); 3114 list.clear();
3109 leaplist.clear(); 3115 leaplist.clear();
3110 bool feb29 = false; 3116 bool feb29 = false;
3111 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) { 3117 for (QPtrListIterator<int> it(rYearNums); it.current(); ++it) {
3112 int month = *it.current(); 3118 int month = *it.current();
3113 if (month == 2) { 3119 if (month == 2) {
3114 if (day <= 28) { 3120 if (day <= 28) {
3115 list.append(month); // date appears in February 3121 list.append(month); // date appears in February
3116 leaplist.append(month); 3122 leaplist.append(month);
3117 } 3123 }
3118 else if (day == 29) { 3124 else if (day == 29) {
3119 // February 29th 3125 // February 29th
3120 leaplist.append(month); 3126 leaplist.append(month);
3121 switch (mFeb29YearlyType) { 3127 switch (mFeb29YearlyType) {
3122 case rFeb28: 3128 case rFeb28:
3123 case rMar1: 3129 case rMar1:
3124 list.append(2); 3130 list.append(2);
3125 break; 3131 break;
3126 case rFeb29: 3132 case rFeb29:
3127 break; 3133 break;
3128 } 3134 }
3129 feb29 = true; 3135 feb29 = true;
3130 } 3136 }
3131 } 3137 }
3132 else if (day <= 30 || QDate(2000, month, 1).daysInMonth() == 31) { 3138 else if (day <= 30 || QDate(2000, month, 1).daysInMonth() == 31) {
3133 list.append(month); // date appears in every month 3139 list.append(month); // date appears in every month
3134 leaplist.append(month); 3140 leaplist.append(month);
3135 } 3141 }
3136 } 3142 }
3137 return feb29; 3143 return feb29;
3138} 3144}
3139 3145
3140/* From the recurrence day of the week list, get the earliest day in the 3146/* From the recurrence day of the week list, get the earliest day in the
3141 * specified week which is >= the startDay. 3147 * specified week which is >= the startDay.
3142 * Parameters: startDay = 1..7 (Monday..Sunday) 3148 * Parameters: startDay = 1..7 (Monday..Sunday)
3143 * useWeekStart = true to end search at day before next rWeekStart 3149 * useWeekStart = true to end search at day before next rWeekStart
3144 * = false to search for a full 7 days 3150 * = false to search for a full 7 days
3145 * Reply = day of the week (1..7), or 0 if none found. 3151 * Reply = day of the week (1..7), or 0 if none found.
3146 */ 3152 */
3147int Recurrence::getFirstDayInWeek(int startDay, bool useWeekStart) const 3153int Recurrence::getFirstDayInWeek(int startDay, bool useWeekStart) const
3148{ 3154{
3149 int last = ((useWeekStart ? rWeekStart : startDay) + 5)%7; 3155 int last = ((useWeekStart ? rWeekStart : startDay) + 5)%7;
3150 for (int i = startDay - 1; ; i = (i + 1)%7) { 3156 for (int i = startDay - 1; ; i = (i + 1)%7) {
3151 if (rDays.testBit(i)) 3157 if (rDays.testBit(i))
3152 return i + 1; 3158 return i + 1;
3153 if (i == last) 3159 if (i == last)
3154 return 0; 3160 return 0;
3155 } 3161 }
3156} 3162}
3157 3163
3158/* From the recurrence day of the week list, get the latest day in the 3164/* From the recurrence day of the week list, get the latest day in the
3159 * specified week which is <= the endDay. 3165 * specified week which is <= the endDay.
3160 * Parameters: endDay = 1..7 (Monday..Sunday) 3166 * Parameters: endDay = 1..7 (Monday..Sunday)
3161 * useWeekStart = true to end search at rWeekStart 3167 * useWeekStart = true to end search at rWeekStart
3162 * = false to search for a full 7 days 3168 * = false to search for a full 7 days
3163 * Reply = day of the week (1..7), or 0 if none found. 3169 * Reply = day of the week (1..7), or 0 if none found.
3164 */ 3170 */
3165int Recurrence::getLastDayInWeek(int endDay, bool useWeekStart) const 3171int Recurrence::getLastDayInWeek(int endDay, bool useWeekStart) const
3166{ 3172{
3167 int last = useWeekStart ? rWeekStart - 1 : endDay%7; 3173 int last = useWeekStart ? rWeekStart - 1 : endDay%7;
3168 for (int i = endDay - 1; ; i = (i + 6)%7) { 3174 for (int i = endDay - 1; ; i = (i + 6)%7) {
3169 if (rDays.testBit(i)) 3175 if (rDays.testBit(i))
3170 return i + 1; 3176 return i + 1;
3171 if (i == last) 3177 if (i == last)
3172 return 0; 3178 return 0;
3173 } 3179 }
3174} 3180}
3175 3181
3176/* From the recurrence monthly day number list or monthly day of week/week of 3182/* From the recurrence monthly day number list or monthly day of week/week of
3177 * month list, get the earliest day in the specified month which is >= the 3183 * month list, get the earliest day in the specified month which is >= the
3178 * earliestDate. 3184 * earliestDate.
3179 */ 3185 */
3180QDate Recurrence::getFirstDateInMonth(const QDate &earliestDate) const 3186QDate Recurrence::getFirstDateInMonth(const QDate &earliestDate) const
3181{ 3187{
3182 int earliestDay = earliestDate.day(); 3188 int earliestDay = earliestDate.day();
3183 int daysInMonth = earliestDate.daysInMonth(); 3189 int daysInMonth = earliestDate.daysInMonth();
3184 switch (recurs) { 3190 switch (recurs) {
3185 case rMonthlyDay: { 3191 case rMonthlyDay: {
3186 int minday = daysInMonth + 1; 3192 int minday = daysInMonth + 1;
3187 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { 3193 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
3188 int day = *it.current(); 3194 int day = *it.current();
3189 if (day < 0) 3195 if (day < 0)
3190 day = daysInMonth + day + 1; 3196 day = daysInMonth + day + 1;
3191 if (day >= earliestDay && day < minday) 3197 if (day >= earliestDay && day < minday)
3192 minday = day; 3198 minday = day;
3193 } 3199 }
3194 if (minday <= daysInMonth) 3200 if (minday <= daysInMonth)
3195 return earliestDate.addDays(minday - earliestDay); 3201 return earliestDate.addDays(minday - earliestDay);
3196 break; 3202 break;
3197 } 3203 }
3198 case rMonthlyPos: 3204 case rMonthlyPos:
3199 case rYearlyPos: { 3205 case rYearlyPos: {
3200 QDate monthBegin(earliestDate.addDays(1 - earliestDay)); 3206 QDate monthBegin(earliestDate.addDays(1 - earliestDay));
3201 QValueList<int> dayList; 3207 QValueList<int> dayList;
3202 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek()); 3208 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek());
3203 for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) { 3209 for (QValueList<int>::ConstIterator id = dayList.begin(); id != dayList.end(); ++id) {
3204 if (*id >= earliestDay) 3210 if (*id >= earliestDay)
3205 return monthBegin.addDays(*id - 1); 3211 return monthBegin.addDays(*id - 1);
3206 } 3212 }
3207 break; 3213 break;
3208 } 3214 }
3209 } 3215 }
3210 return QDate(); 3216 return QDate();
3211} 3217}
3212 3218
3213/* From the recurrence monthly day number list or monthly day of week/week of 3219/* From the recurrence monthly day number list or monthly day of week/week of
3214 * month list, get the latest day in the specified month which is <= the 3220 * month list, get the latest day in the specified month which is <= the
3215 * latestDate. 3221 * latestDate.
3216 */ 3222 */
3217QDate Recurrence::getLastDateInMonth(const QDate &latestDate) const 3223QDate Recurrence::getLastDateInMonth(const QDate &latestDate) const
3218{ 3224{
3219 int latestDay = latestDate.day(); 3225 int latestDay = latestDate.day();
3220 int daysInMonth = latestDate.daysInMonth(); 3226 int daysInMonth = latestDate.daysInMonth();
3221 switch (recurs) { 3227 switch (recurs) {
3222 case rMonthlyDay: { 3228 case rMonthlyDay: {
3223 int maxday = -1; 3229 int maxday = -1;
3224 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) { 3230 for (QPtrListIterator<int> it(rMonthDays); it.current(); ++it) {
3225 int day = *it.current(); 3231 int day = *it.current();
3226 if (day < 0) 3232 if (day < 0)
3227 day = daysInMonth + day + 1; 3233 day = daysInMonth + day + 1;
3228 if (day <= latestDay && day > maxday) 3234 if (day <= latestDay && day > maxday)
3229 maxday = day; 3235 maxday = day;
3230 } 3236 }
3231 if (maxday > 0) 3237 if (maxday > 0)
3232 return QDate(latestDate.year(), latestDate.month(), maxday); 3238 return QDate(latestDate.year(), latestDate.month(), maxday);
3233 break; 3239 break;
3234 } 3240 }
3235 case rMonthlyPos: 3241 case rMonthlyPos:
3236 case rYearlyPos: { 3242 case rYearlyPos: {
3237 QDate monthBegin(latestDate.addDays(1 - latestDay)); 3243 QDate monthBegin(latestDate.addDays(1 - latestDay));
3238 QValueList<int> dayList; 3244 QValueList<int> dayList;
3239 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek()); 3245 getMonthlyPosDays(dayList, daysInMonth, monthBegin.dayOfWeek());
3240 for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) { 3246 for (QValueList<int>::ConstIterator id = dayList.fromLast(); id != dayList.end(); --id) {
3241 if (*id <= latestDay) 3247 if (*id <= latestDay)
3242 return monthBegin.addDays(*id - 1); 3248 return monthBegin.addDays(*id - 1);
3243 } 3249 }
3244 break; 3250 break;
3245 } 3251 }
3246 } 3252 }
3247 return QDate(); 3253 return QDate();
3248} 3254}
3249 3255
3250/* From the recurrence yearly month list or yearly day list, get the earliest 3256/* From the recurrence yearly month list or yearly day list, get the earliest
3251 * month or day in the specified year which is >= the earliestDate. 3257 * month or day in the specified year which is >= the earliestDate.
3252 * Note that rYearNums is sorted in numerical order. 3258 * Note that rYearNums is sorted in numerical order.
3253 */ 3259 */
3254QDate Recurrence::getFirstDateInYear(const QDate &earliestDate) const 3260QDate Recurrence::getFirstDateInYear(const QDate &earliestDate) const
3255{ 3261{
3256 QPtrListIterator<int> it(rYearNums); 3262 QPtrListIterator<int> it(rYearNums);
3257 switch (recurs) { 3263 switch (recurs) {
3258 case rYearlyMonth: { 3264 case rYearlyMonth: {
3259 int day = recurStart().date().day(); 3265 int day = recurStart().date().day();
3260 int earliestYear = earliestDate.year(); 3266 int earliestYear = earliestDate.year();
3261 int earliestMonth = earliestDate.month(); 3267 int earliestMonth = earliestDate.month();
3262 int earliestDay = earliestDate.day(); 3268 int earliestDay = earliestDate.day();
3263 if (earliestDay > day) { 3269 if (earliestDay > day) {
3264 // The earliest date is later in the month than the recurrence date, 3270 // The earliest date is later in the month than the recurrence date,
3265 // so skip to the next month before starting to check 3271 // so skip to the next month before starting to check
3266 if (++earliestMonth > 12) 3272 if (++earliestMonth > 12)
3267 return QDate(); 3273 return QDate();
3268 } 3274 }
3269 for ( ; it.current(); ++it) { 3275 for ( ; it.current(); ++it) {
3270 int month = *it.current(); 3276 int month = *it.current();
3271 if (month >= earliestMonth) { 3277 if (month >= earliestMonth) {
3272 if (day <= 28 || QDate::isValid(earliestYear, month, day)) 3278 if (day <= 28 || QDate::isValid(earliestYear, month, day))
3273 return QDate(earliestYear, month, day); 3279 return QDate(earliestYear, month, day);
3274 if (day == 29 && month == 2) { 3280 if (day == 29 && month == 2) {
3275 // It's a recurrence on February 29th, in a non-leap year 3281 // It's a recurrence on February 29th, in a non-leap year
3276 switch (mFeb29YearlyType) { 3282 switch (mFeb29YearlyType) {
3277 case rMar1: 3283 case rMar1:
3278 return QDate(earliestYear, 3, 1); 3284 return QDate(earliestYear, 3, 1);
3279 case rFeb28: 3285 case rFeb28:
3280 if (earliestDay <= 28) 3286 if (earliestDay <= 28)
3281 return QDate(earliestYear, 2, 28); 3287 return QDate(earliestYear, 2, 28);
3282 break; 3288 break;
3283 case rFeb29: 3289 case rFeb29:
3284 break; 3290 break;