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