summaryrefslogtreecommitdiff
path: root/libopie2/opiepim/core/opimrecurrence.cpp
Unidiff
Diffstat (limited to 'libopie2/opiepim/core/opimrecurrence.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--libopie2/opiepim/core/opimrecurrence.cpp691
1 files changed, 691 insertions, 0 deletions
diff --git a/libopie2/opiepim/core/opimrecurrence.cpp b/libopie2/opiepim/core/opimrecurrence.cpp
new file mode 100644
index 0000000..98bd647
--- a/dev/null
+++ b/libopie2/opiepim/core/opimrecurrence.cpp
@@ -0,0 +1,691 @@
1/*
2 This file is part of the Opie Project
3 Copyright (C) The Main Author <main-author@whereever.org>
4 =. Copyright (C) The Opie Team <opie-devel@handhelds.org>
5 .=l.
6 .>+-=
7 _;:, .> :=|. This program is free software; you can
8.> <`_, > . <= redistribute it and/or modify it under
9:`=1 )Y*s>-.-- : the terms of the GNU Library General Public
10.="- .-=="i, .._ License as published by the Free Software
11 - . .-<_> .<> Foundation; either version 2 of the License,
12 ._= =} : or (at your option) any later version.
13 .%`+i> _;_.
14 .i_,=:_. -<s. This program is distributed in the hope that
15 + . -:. = it will be useful, but WITHOUT ANY WARRANTY;
16 : .. .:, . . . without even the implied warranty of
17 =_ + =;=|` MERCHANTABILITY or FITNESS FOR A
18 _.=:. : :=>`: PARTICULAR PURPOSE. See the GNU
19..}^=.= = ; Library General Public License for more
20++= -. .` .: details.
21 : = ...= . :.=-
22 -. .:....=;==+<; You should have received a copy of the GNU
23 -_. . . )=. = Library General Public License along with
24 -- :-=` this library; see the file COPYING.LIB.
25 If not, write to the Free Software Foundation,
26 Inc., 59 Temple Place - Suite 330,
27 Boston, MA 02111-1307, USA.
28*/
29
30#include "opimrecurrence.h"
31
32/* OPIE */
33#include <opie2/opimtimezone.h>
34#include <qpe/timeconversion.h>
35
36/* QT */
37#include <qshared.h>
38
39/* STD */
40#include <time.h>
41
42namespace Opie {
43
44struct OPimRecurrence::Data : public QShared {
45 Data() : QShared() {
46 type = OPimRecurrence::NoRepeat;
47 freq = -1;
48 days = 0;
49 pos = 0;
50 create = QDateTime::currentDateTime();
51 hasEnd = FALSE;
52 end = QDate::currentDate();
53 }
54 char days; // Q_UINT8 for 8 seven days;)
55 OPimRecurrence::RepeatType type;
56 int freq;
57 int pos;
58 bool hasEnd : 1;
59 QDate end;
60 QDateTime create;
61 int rep;
62 QString app;
63 ExceptionList list;
64 QDate start;
65};
66
67
68OPimRecurrence::OPimRecurrence() {
69 data = new Data;
70}
71
72OPimRecurrence::OPimRecurrence( const QMap<int, QString>& map )
73{
74 OPimRecurrence();
75 fromMap( map );
76}
77
78
79OPimRecurrence::OPimRecurrence( const OPimRecurrence& rec)
80 : data( rec.data )
81{
82 data->ref();
83}
84
85
86OPimRecurrence::~OPimRecurrence() {
87 if ( data->deref() ) {
88 delete data;
89 data = 0l;
90 }
91}
92
93
94void OPimRecurrence::deref() {
95 if ( data->deref() ) {
96 delete data;
97 data = 0l;
98 }
99}
100
101
102bool OPimRecurrence::operator==( const OPimRecurrence& )const {
103 return false;
104}
105
106
107OPimRecurrence &OPimRecurrence::operator=( const OPimRecurrence& re) {
108 if ( *this == re ) return *this;
109
110 re.data->ref();
111 deref();
112 data = re.data;
113
114 return *this;
115}
116
117
118bool OPimRecurrence::doesRecur()const {
119 return !( type() == NoRepeat );
120}
121
122
123/*
124 * we try to be smart here
125 *
126 */
127bool OPimRecurrence::doesRecur( const QDate& date ) {
128 /* the day before the recurrance */
129 QDate da = date.addDays(-1);
130
131 QDate recur;
132 if (!nextOcurrence( da, recur ) )
133 return false;
134
135 return (recur == date);
136}
137
138
139// FIXME unuglify!
140// GPL from Datebookdb.cpp
141// FIXME exception list!
142bool OPimRecurrence::nextOcurrence( const QDate& from, QDate& next ) {
143 bool stillLooking;
144 stillLooking = p_nextOccurrence( from, next );
145 while ( stillLooking && data->list.contains(next) )
146 stillLooking = p_nextOccurrence( next.addDays(1), next );
147
148 return stillLooking;
149}
150
151
152bool OPimRecurrence::p_nextOccurrence( const QDate& from, QDate& next ) {
153
154 // easy checks, first are we too far in the future or too far in the past?
155 QDate tmpDate;
156 int freq = frequency();
157 int diff, diff2, a;
158 int iday, imonth, iyear;
159 int dayOfWeek = 0;
160 int firstOfWeek = 0;
161 int weekOfMonth;
162
163
164 if (hasEndDate() && endDate() < from)
165 return FALSE;
166
167 if (start() >= from ) {
168 next = start();
169 return TRUE;
170 }
171
172 switch ( type() ) {
173 case Weekly:
174 /* weekly is just daily by 7 */
175 /* first convert the repeatPattern.Days() mask to the next
176 day of week valid after from */
177 dayOfWeek = from.dayOfWeek();
178 dayOfWeek--; /* we want 0-6, doco for above specs 1-7 */
179
180 /* this is done in case freq > 1 and from in week not
181 for this round */
182 // firstOfWeek = 0; this is already done at decl.
183 while(!((1 << firstOfWeek) & days() ))
184 firstOfWeek++;
185
186 /* there is at least one 'day', or there would be no event */
187 while(!((1 << (dayOfWeek % 7)) & days() ))
188 dayOfWeek++;
189
190 dayOfWeek = dayOfWeek % 7; /* the actual day of week */
191 dayOfWeek -= start().dayOfWeek() -1;
192
193 firstOfWeek = firstOfWeek % 7; /* the actual first of week */
194 firstOfWeek -= start().dayOfWeek() -1;
195
196 // dayOfWeek may be negitive now
197 // day of week is number of days to add to start day
198
199 freq *= 7;
200 // FALL-THROUGH !!!!!
201 case Daily:
202 // the add is for the possible fall through from weekly */
203 if(start().addDays(dayOfWeek) > from) {
204 /* first week exception */
205 next = QDate(start().addDays(dayOfWeek) );
206 if ((next > endDate())
207 && hasEndDate() )
208 return FALSE;
209 return TRUE;
210 }
211 /* if from is middle of a non-week */
212
213 diff = start().addDays(dayOfWeek).daysTo(from) % freq;
214 diff2 = start().addDays(firstOfWeek).daysTo(from) % freq;
215
216 if(diff != 0)
217 diff = freq - diff;
218 if(diff2 != 0)
219 diff2 = freq - diff2;
220 diff = QMIN(diff, diff2);
221
222 next = QDate(from.addDays(diff));
223 if ( (next > endDate())
224 && hasEndDate() )
225 return FALSE;
226 return TRUE;
227 case MonthlyDay:
228 iday = from.day();
229 iyear = from.year();
230 imonth = from.month();
231 /* find equivelent day of month for this month */
232 dayOfWeek = start().dayOfWeek();
233 weekOfMonth = (start().day() - 1) / 7;
234
235 /* work out when the next valid month is */
236 a = from.year() - start().year();
237 a *= 12;
238 a = a + (imonth - start().month());
239 /* a is e.start()monthsFrom(from); */
240 if(a % freq) {
241 a = freq - (a % freq);
242 imonth = from.month() + a;
243 if (imonth > 12) {
244 imonth--;
245 iyear += imonth / 12;
246 imonth = imonth % 12;
247 imonth++;
248 }
249 }
250 /* imonth is now the first month after or on
251 from that matches the frequency given */
252
253 /* find for this month */
254 tmpDate = QDate( iyear, imonth, 1 );
255
256 iday = 1;
257 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
258 iday += 7 * weekOfMonth;
259 while (iday > tmpDate.daysInMonth()) {
260 imonth += freq;
261 if (imonth > 12) {
262 imonth--;
263 iyear += imonth / 12;
264 imonth = imonth % 12;
265 imonth++;
266 }
267 tmpDate = QDate( iyear, imonth, 1 );
268 /* these loops could go for a while, check end case now */
269 if ((tmpDate > endDate()) && hasEndDate() )
270 return FALSE;
271 iday = 1;
272 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
273 iday += 7 * weekOfMonth;
274 }
275 tmpDate = QDate(iyear, imonth, iday);
276
277 if (tmpDate >= from) {
278 next = tmpDate;
279 if ((next > endDate() ) && hasEndDate() )
280 return FALSE;
281 return TRUE;
282 }
283
284 /* need to find the next iteration */
285 do {
286 imonth += freq;
287 if (imonth > 12) {
288 imonth--;
289 iyear += imonth / 12;
290 imonth = imonth % 12;
291 imonth++;
292 }
293 tmpDate = QDate( iyear, imonth, 1 );
294 /* these loops could go for a while, check end case now */
295 if ((tmpDate > endDate()) && hasEndDate() )
296 return FALSE;
297 iday = 1;
298 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
299 iday += 7 * weekOfMonth;
300 } while (iday > tmpDate.daysInMonth());
301 tmpDate = QDate(iyear, imonth, iday);
302
303 next = tmpDate;
304 if ((next > endDate()) && hasEndDate() )
305 return FALSE;
306 return TRUE;
307 case MonthlyDate:
308 iday = start().day();
309 iyear = from.year();
310 imonth = from.month();
311
312 a = from.year() - start().year();
313 a *= 12;
314 a = a + (imonth - start().month());
315 /* a is e.start()monthsFrom(from); */
316 if(a % freq) {
317 a = freq - (a % freq);
318 imonth = from.month() + a;
319 if (imonth > 12) {
320 imonth--;
321 iyear += imonth / 12;
322 imonth = imonth % 12;
323 imonth++;
324 }
325 }
326 /* imonth is now the first month after or on
327 from that matches the frequencey given */
328
329 /* this could go for a while, worse case, 4*12 iterations, probably */
330 while(!QDate::isValid(iyear, imonth, iday) ) {
331 imonth += freq;
332 if (imonth > 12) {
333 imonth--;
334 iyear += imonth / 12;
335 imonth = imonth % 12;
336 imonth++;
337 }
338 /* these loops could go for a while, check end case now */
339 if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() )
340 return FALSE;
341 }
342
343 if(QDate(iyear, imonth, iday) >= from) {
344 /* done */
345 next = QDate(iyear, imonth, iday);
346 if ((next > endDate()) && hasEndDate() )
347 return FALSE;
348 return TRUE;
349 }
350
351 /* ok, need to cycle */
352 imonth += freq;
353 imonth--;
354 iyear += imonth / 12;
355 imonth = imonth % 12;
356 imonth++;
357
358 while(!QDate::isValid(iyear, imonth, iday) ) {
359 imonth += freq;
360 imonth--;
361 iyear += imonth / 12;
362 imonth = imonth % 12;
363 imonth++;
364 if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() )
365 return FALSE;
366 }
367
368 next = QDate(iyear, imonth, iday);
369 if ((next > endDate()) && hasEndDate() )
370 return FALSE;
371 return TRUE;
372 case Yearly:
373 iday = start().day();
374 imonth = start().month();
375 iyear = from.year(); // after all, we want to start in this year
376
377 diff = 1;
378 if(imonth == 2 && iday > 28) {
379 /* leap year, and it counts, calculate actual frequency */
380 if(freq % 4)
381 if (freq % 2)
382 freq = freq * 4;
383 else
384 freq = freq * 2;
385 /* else divides by 4 already, leave freq alone */
386 diff = 4;
387 }
388
389 a = from.year() - start().year();
390 if(a % freq) {
391 a = freq - (a % freq);
392 iyear = iyear + a;
393 }
394
395 /* under the assumption we won't hit one of the special not-leap years twice */
396 if(!QDate::isValid(iyear, imonth, iday)) {
397 /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
398 iyear += freq;
399 }
400
401 if(QDate(iyear, imonth, iday) >= from) {
402 next = QDate(iyear, imonth, iday);
403
404 if ((next > endDate()) && hasEndDate() )
405 return FALSE;
406 return TRUE;
407 }
408 /* iyear == from.year(), need to advance again */
409 iyear += freq;
410 /* under the assumption we won't hit one of the special not-leap years twice */
411 if(!QDate::isValid(iyear, imonth, iday)) {
412 /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
413 iyear += freq;
414 }
415
416 next = QDate(iyear, imonth, iday);
417 if ((next > endDate()) && hasEndDate() )
418 return FALSE;
419 return TRUE;
420 default:
421 return FALSE;
422 }
423}
424
425
426OPimRecurrence::RepeatType OPimRecurrence::type()const{
427 return data->type;
428}
429
430
431int OPimRecurrence::frequency()const {
432 return data->freq;
433}
434
435
436int OPimRecurrence::position()const {
437 return data->pos;
438}
439
440
441char OPimRecurrence::days() const{
442 return data->days;
443}
444
445
446bool OPimRecurrence::hasEndDate()const {
447 return data->hasEnd;
448}
449
450
451QDate OPimRecurrence::endDate()const {
452 return data->end;
453}
454
455
456QDate OPimRecurrence::start()const{
457 return data->start;
458}
459
460
461QDateTime OPimRecurrence::createdDateTime()const {
462 return data->create;
463}
464
465
466int OPimRecurrence::repetition()const {
467 return data->rep;
468}
469
470
471QString OPimRecurrence::service()const {
472 return data->app;
473}
474
475
476OPimRecurrence::ExceptionList& OPimRecurrence::exceptions() {
477 return data->list;
478}
479
480
481void OPimRecurrence::setType( const RepeatType& z) {
482 checkOrModify();
483 data->type = z;
484}
485
486
487void OPimRecurrence::setFrequency( int freq ) {
488 checkOrModify();
489 data->freq = freq;
490}
491
492
493void OPimRecurrence::setPosition( int pos ) {
494 checkOrModify();
495 data->pos = pos;
496}
497
498
499void OPimRecurrence::setDays( char c ) {
500 checkOrModify();
501 data->days = c;
502}
503
504
505void OPimRecurrence::setEndDate( const QDate& dt) {
506 checkOrModify();
507 data->end = dt;
508}
509
510
511void OPimRecurrence::setCreatedDateTime( const QDateTime& t) {
512 checkOrModify();
513 data->create = t;
514}
515
516
517void OPimRecurrence::setHasEndDate( bool b) {
518 checkOrModify();
519 data->hasEnd = b;
520}
521
522
523void OPimRecurrence::setRepitition( int rep ) {
524 checkOrModify();
525 data->rep = rep;
526}
527
528
529void OPimRecurrence::setService( const QString& app ) {
530 checkOrModify();
531 data->app = app;
532}
533
534
535void OPimRecurrence::setStart( const QDate& dt ) {
536 checkOrModify();
537 data->start = dt;
538}
539
540
541void OPimRecurrence::checkOrModify() {
542 if ( data->count != 1 ) {
543 data->deref();
544 Data* d2 = new Data;
545 d2->days = data->days;
546 d2->type = data->type;
547 d2->freq = data->freq;
548 d2->pos = data->pos;
549 d2->hasEnd = data->hasEnd;
550 d2->end = data->end;
551 d2->create = data->create;
552 d2->rep = data->rep;
553 d2->app = data->app;
554 d2->list = data->list;
555 d2->start = data->start;
556 data = d2;
557 }
558}
559
560
561QString OPimRecurrence::toString()const {
562 QString buf;
563 QMap<int, QString> recMap = toMap();
564
565 buf += " rtype=\"";
566 buf += recMap[OPimRecurrence::RType];
567 buf += "\"";
568 if (data->days > 0 )
569 buf += " rweekdays=\"" + recMap[OPimRecurrence::RWeekdays] + "\"";
570 if ( data->pos != 0 )
571 buf += " rposition=\"" + recMap[OPimRecurrence::RPosition] + "\"";
572
573 buf += " rfreq=\"" + recMap[OPimRecurrence::RFreq] + "\"";
574 buf += " rhasenddate=\"" + recMap[OPimRecurrence::RHasEndDate]+ "\"";
575 if ( data->hasEnd )
576 buf += " enddt=\""
577 + recMap[OPimRecurrence::EndDate]
578 + "\"";
579 buf += " created=\"" + recMap[OPimRecurrence::Created] + "\"";
580
581 if ( data->list.isEmpty() ) return buf;
582 buf += " exceptions=\"";
583 buf += recMap[OPimRecurrence::Exceptions];
584 buf += "\" ";
585
586 return buf;
587}
588
589QString OPimRecurrence::rTypeString() const
590{
591 QString retString;
592 switch ( data->type ) {
593 case OPimRecurrence::Daily:
594 retString = "Daily";
595 break;
596 case OPimRecurrence::Weekly:
597 retString = "Weekly";
598 break;
599 case OPimRecurrence::MonthlyDay:
600 retString = "MonthlyDay";
601 break;
602 case OPimRecurrence::MonthlyDate:
603 retString = "MonthlyDate";
604 break;
605 case OPimRecurrence::Yearly:
606 retString = "Yearly";
607 break;
608 default:
609 retString = "NoRepeat";
610 break;
611
612 }
613
614 return retString;
615}
616
617QMap<QString, OPimRecurrence::RepeatType> OPimRecurrence::rTypeValueConvertMap() const
618{
619 QMap<QString, RepeatType> convertMap;
620
621 convertMap.insert( QString( "Daily" ), OPimRecurrence::Daily );
622 convertMap.insert( QString( "Weekly" ), OPimRecurrence::Weekly );
623 convertMap.insert( QString( "MonthlyDay" ), OPimRecurrence::MonthlyDay );
624 convertMap.insert( QString( "MonthlyDate" ), OPimRecurrence::MonthlyDate );
625 convertMap.insert( QString( "Yearly" ), OPimRecurrence::Yearly );
626 convertMap.insert( QString( "NoRepeat" ), OPimRecurrence::NoRepeat );
627
628 return convertMap;
629}
630
631
632QMap<int, QString> OPimRecurrence::toMap() const
633{
634 QMap<int, QString> retMap;
635
636 retMap.insert( OPimRecurrence::RType, rTypeString() );
637 retMap.insert( OPimRecurrence::RWeekdays, QString::number( static_cast<int>( data->days ) ) );
638 retMap.insert( OPimRecurrence::RPosition, QString::number(data->pos ) );
639 retMap.insert( OPimRecurrence::RFreq, QString::number( data->freq ) );
640 retMap.insert( OPimRecurrence::RHasEndDate, QString::number( static_cast<int>( data->hasEnd ) ) );
641 if( data -> hasEnd )
642 retMap.insert( OPimRecurrence::EndDate, QString::number( OPimTimeZone::utc().fromUTCDateTime( QDateTime( data->end, QTime(12,0,0) ) ) ) );
643 retMap.insert( OPimRecurrence::Created, QString::number( OPimTimeZone::utc().fromUTCDateTime( data->create ) ) );
644
645 if ( data->list.isEmpty() ) return retMap;
646
647 // save exceptions list here!!
648 ExceptionList::ConstIterator it;
649 ExceptionList list = data->list;
650 QString exceptBuf;
651 QDate date;
652 for ( it = list.begin(); it != list.end(); ++it ) {
653 date = (*it);
654 if ( it != list.begin() ) exceptBuf += " ";
655
656 exceptBuf += QCString().sprintf("%04d%02d%02d", date.year(), date.month(), date.day() );
657 }
658
659 retMap.insert( OPimRecurrence::Exceptions, exceptBuf );
660
661 return retMap;
662}
663
664void OPimRecurrence::fromMap( const QMap<int, QString>& map )
665{
666 QMap<QString, RepeatType> repTypeMap = rTypeValueConvertMap();
667
668 data -> type = repTypeMap[ map [OPimRecurrence::RType] ];
669 data -> days = (char) map[ OPimRecurrence::RWeekdays ].toInt();
670 data -> pos = map[ OPimRecurrence::RPosition ].toInt();
671 data -> freq = map[ OPimRecurrence::RFreq ].toInt();
672 data -> hasEnd= map[ OPimRecurrence::RHasEndDate ].toInt() ? true : false;
673 OPimTimeZone utc = OPimTimeZone::utc();
674 if ( data -> hasEnd ){
675 data -> end = utc.fromUTCDateTime( (time_t) map[ OPimRecurrence::EndDate ].toLong() ).date();
676 }
677 data -> create = utc.fromUTCDateTime( (time_t) map[ OPimRecurrence::Created ].toLong() ).date();
678
679#if 0
680 // FIXME: Exceptions currently not supported...
681 // Convert the list of exceptions from QString into ExceptionList
682 data -> list.clear();
683 QString exceptStr = map[ OPimRecurrence::Exceptions ];
684 QStringList exceptList = QStringList::split( " ", exceptStr );
685 ...
686#endif
687
688
689}
690
691}