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