25 #include "recurrence.h"
26 #include "recurrencerule.h"
32 #include <QtCore/QList>
33 #include <QtCore/QBitArray>
40 class KCal::Recurrence::Private
44 : mCachedType( rMax ),
46 mRecurReadOnly( false )
48 mExRules.setAutoDelete(
true );
49 mRRules.setAutoDelete(
true );
52 Private(
const Private &p )
53 : mRDateTimes( p.mRDateTimes ),
55 mExDateTimes( p.mExDateTimes ),
56 mExDates( p.mExDates ),
57 mStartDateTime( p.mStartDateTime ),
58 mCachedType( p.mCachedType ),
60 mRecurReadOnly( p.mRecurReadOnly )
62 mExRules.setAutoDelete(
true );
63 mRRules.setAutoDelete(
true );
66 bool operator==(
const Private &p )
const;
74 KDateTime mStartDateTime;
75 QList<RecurrenceObserver*> mObservers;
78 mutable ushort mCachedType;
84 bool Recurrence::Private::operator==(
const Recurrence::Private &p )
const
86 if ( mStartDateTime != p.mStartDateTime ||
87 mAllDay != p.mAllDay ||
88 mRecurReadOnly != p.mRecurReadOnly ||
89 mExDates != p.mExDates ||
90 mExDateTimes != p.mExDateTimes ||
91 mRDates != p.mRDates ||
92 mRDateTimes != p.mRDateTimes ) {
99 int end = mRRules.count();
100 if ( end != p.mRRules.count() ) {
103 for ( i = 0; i < end; ++i ) {
104 if ( *mRRules[i] != *p.mRRules[i] ) {
108 end = mExRules.count();
109 if ( end != p.mExRules.count() ) {
112 for ( i = 0; i < end; ++i ) {
113 if ( *mExRules[i] != *p.mExRules[i] ) {
131 for ( i = 0, end = r.d->mRRules.count(); i < end; ++i ) {
133 d->mRRules.append( rule );
136 for ( i = 0, end = r.d->mExRules.count(); i < end; ++i ) {
138 d->mExRules.append( rule );
155 if ( &other ==
this )
164 if ( !d->mObservers.contains( observer ) ) {
165 d->mObservers.append( observer );
171 if ( d->mObservers.contains( observer ) ) {
172 d->mObservers.removeAll( observer );
178 return d->mStartDateTime;
188 if ( d->mRecurReadOnly || allDay == d->mAllDay ) {
193 for (
int i = 0, end = d->mRRules.count(); i < end; ++i ) {
194 d->mRRules[i]->setAllDay( allDay );
196 for (
int i = 0, end = d->mExRules.count(); i < end; ++i ) {
197 d->mExRules[i]->setAllDay( allDay );
204 if ( d->mRRules.isEmpty() ) {
205 if ( !create || d->mRecurReadOnly ) {
213 return d->mRRules[0];
219 return d->mRRules.isEmpty() ? 0 : d->mRRules[0];
222 void Recurrence::updated()
225 d->mCachedType = rMax;
226 for (
int i = 0, end = d->mObservers.count(); i < end; ++i ) {
227 if ( d->mObservers[i] ) {
228 d->mObservers[i]->recurrenceUpdated(
this );
235 return !d->mRRules.isEmpty() || !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty();
240 if ( d->mCachedType == rMax ) {
243 return d->mCachedType;
254 if ( !rrule->bySetPos().isEmpty() ||
255 !rrule->bySeconds().isEmpty() ||
256 !rrule->byWeekNumbers().isEmpty() ) {
262 if ( !rrule->byMinutes().isEmpty() || !rrule->byHours().isEmpty() ) {
271 if ( ( !rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly ) ||
272 ( !rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly ) ) {
275 if ( !rrule->byDays().isEmpty() ) {
276 if ( type != RecurrenceRule::rYearly &&
277 type != RecurrenceRule::rMonthly &&
278 type != RecurrenceRule::rWeekly ) {
284 case RecurrenceRule::rNone:
286 case RecurrenceRule::rMinutely:
288 case RecurrenceRule::rHourly:
290 case RecurrenceRule::rDaily:
292 case RecurrenceRule::rWeekly:
294 case RecurrenceRule::rMonthly:
296 if ( rrule->byDays().isEmpty() ) {
298 }
else if ( rrule->byMonthDays().isEmpty() ) {
304 case RecurrenceRule::rYearly:
310 if ( !rrule->byDays().isEmpty() ) {
312 if ( rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty() ) {
317 }
else if ( !rrule->byYearDays().isEmpty() ) {
319 if ( rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty() ) {
329 default:
return rOther;
337 if ( KDateTime( qd, QTime( 23, 59, 59 ), timeSpec ) < d->mStartDateTime ) {
342 if ( d->mExDates.containsSorted( qd ) ) {
351 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
352 if ( d->mExRules[i]->recursOn( qd, timeSpec ) ) {
358 if ( d->mRDates.containsSorted( qd ) ) {
364 for ( i = 0, end = d->mRDateTimes.count(); i < end && !
recurs; ++i ) {
365 recurs = ( d->mRDateTimes[i].toTimeSpec( timeSpec ).date() == qd );
367 for ( i = 0, end = d->mRRules.count(); i < end && !
recurs; ++i ) {
368 recurs = d->mRRules[i]->recursOn( qd, timeSpec );
377 for ( i = 0, end = d->mExDateTimes.count(); i < end && !exon; ++i ) {
378 exon = ( d->mExDateTimes[i].toTimeSpec( timeSpec ).date() == qd );
381 for ( i = 0, end = d->mExRules.count(); i < end && !exon; ++i ) {
382 exon = d->mExRules[i]->recursOn( qd, timeSpec );
395 return !timesForDay.isEmpty();
402 KDateTime dtrecur = dt.toTimeSpec( d->mStartDateTime.timeSpec() );
405 if ( d->mExDateTimes.containsSorted( dtrecur ) ||
406 d->mExDates.containsSorted( dtrecur.date() ) ) {
410 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
411 if ( d->mExRules[i]->recursAt( dtrecur ) ) {
417 if (
startDateTime() == dtrecur || d->mRDateTimes.containsSorted( dtrecur ) ) {
420 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
421 if ( d->mRRules[i]->recursAt( dtrecur ) ) {
436 if ( !d->mRDates.isEmpty() ) {
437 dts << KDateTime( d->mRDates.last(), QTime( 0, 0, 0 ), d->mStartDateTime.timeSpec() );
439 if ( !d->mRDateTimes.isEmpty() ) {
440 dts << d->mRDateTimes.last();
442 for (
int i = 0, end = d->mRRules.count(); i < end; ++i ) {
443 KDateTime rl( d->mRRules[i]->endDt() );
445 if ( !rl.isValid() ) {
451 return dts.isEmpty() ? KDateTime() : dts.last();
460 return end.isValid() ? end.date() : QDate();
465 KDateTime dt( date, d->mStartDateTime.time(), d->mStartDateTime.timeSpec() );
467 dt.setTime( QTime( 23, 59, 59 ) );
474 if ( d->mRecurReadOnly ) {
488 return rrule ? rrule->
duration() : 0;
495 return rrule ? rrule->
durationTo( datetime ) : 0;
500 return durationTo( KDateTime( date, QTime( 23, 59, 59 ), d->mStartDateTime.timeSpec() ) );
505 if ( d->mRecurReadOnly ) {
519 if ( d->mRecurReadOnly ) {
523 d->mStartDateTime = d->mStartDateTime.toTimeSpec( oldSpec );
524 d->mStartDateTime.setTimeSpec( newSpec );
527 for ( i = 0, end = d->mRDateTimes.count(); i < end; ++i ) {
528 d->mRDateTimes[i] = d->mRDateTimes[i].toTimeSpec( oldSpec );
529 d->mRDateTimes[i].setTimeSpec( newSpec );
531 for ( i = 0, end = d->mExDateTimes.count(); i < end; ++i ) {
532 d->mExDateTimes[i] = d->mExDateTimes[i].toTimeSpec( oldSpec );
533 d->mExDateTimes[i].setTimeSpec( newSpec );
535 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
536 d->mRRules[i]->shiftTimes( oldSpec, newSpec );
538 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
539 d->mExRules[i]->shiftTimes( oldSpec, newSpec );
545 if ( d->mRecurReadOnly ) {
554 if ( d->mRecurReadOnly ) {
557 d->mRRules.clearAll();
558 d->mExRules.clearAll();
560 d->mRDateTimes.clear();
562 d->mExDateTimes.clear();
563 d->mCachedType = rMax;
569 d->mRecurReadOnly = readOnly;
574 return d->mRecurReadOnly;
579 return d->mStartDateTime.date();
584 if ( d->mRecurReadOnly ) {
587 d->mStartDateTime = start;
591 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
592 d->mRRules[i]->setStartDt( start );
594 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
595 d->mExRules[i]->setStartDt( start );
610 if ( d->mRecurReadOnly || freq <= 0 ) {
626 return rrule ? rrule->weekStart() : 1;
636 QList<RecurrenceRule::WDayPos> bydays = rrule->byDays();
637 for (
int i = 0; i < bydays.size(); ++i ) {
638 if ( bydays.at(i).pos() == 0 ) {
639 days.setBit( bydays.at( i ).day() - 1 );
653 return rrule->byMonthDays();
663 return rrule ? rrule->byDays() : QList<RecurrenceRule::WDayPos>();
671 return rrule ? rrule->byYearDays() : QList<int>();
682 return rrule ? rrule->byMonths() : QList<int>();
692 if ( d->mRecurReadOnly || freq <= 0 ) {
696 d->mRRules.clearAll();
702 rrule->setRecurrenceType( type );
710 if ( setNewRecurrenceType( RecurrenceRule::rMinutely, _rFreq ) ) {
717 if ( setNewRecurrenceType( RecurrenceRule::rHourly, _rFreq ) ) {
724 if ( setNewRecurrenceType( RecurrenceRule::rDaily, _rFreq ) ) {
731 RecurrenceRule *rrule = setNewRecurrenceType( RecurrenceRule::rWeekly, freq );
735 rrule->setWeekStart( weekStart );
752 if ( setNewRecurrenceType( RecurrenceRule::rMonthly, freq ) ) {
760 if ( d->mRecurReadOnly || pos > 53 || pos < -53 ) {
768 bool changed =
false;
769 QList<RecurrenceRule::WDayPos> positions = rrule->byDays();
771 for (
int i = 0; i < 7; ++i ) {
772 if ( days.testBit(i) ) {
774 if ( !positions.contains( p ) ) {
776 positions.append( p );
781 rrule->setByDays( positions );
789 if ( d->mRecurReadOnly || pos > 53 || pos < -53 ) {
797 QList<RecurrenceRule::WDayPos> positions = rrule->byDays();
800 if ( !positions.contains( p ) ) {
801 positions.append( p );
802 rrule->setByDays( positions );
809 if ( d->mRecurReadOnly || day > 31 || day < -31 ) {
818 QList<int>
monthDays = rrule->byMonthDays();
819 if ( !monthDays.contains( day ) ) {
820 monthDays.append( day );
821 rrule->setByMonthDays( monthDays );
828 if ( setNewRecurrenceType( RecurrenceRule::rYearly, freq ) ) {
841 QList<int>
days = rrule->byYearDays();
842 if ( !days.contains( day ) ) {
844 rrule->setByYearDays( days );
864 if ( d->mRecurReadOnly || month < 1 || month > 12 ) {
873 QList<int> months = rrule->byMonths();
874 if ( !months.contains(month) ) {
876 rrule->setByMonths( months );
888 if ( d->mExDates.containsSorted( date ) ) {
895 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
896 if ( d->mExRules[i]->recursOn( date, timeSpec ) ) {
903 if ( dt.date() == date ) {
907 bool foundDate =
false;
908 for ( i = 0, end = d->mRDateTimes.count(); i < end; ++i ) {
909 dt = d->mRDateTimes[i].toTimeSpec( timeSpec );
910 if ( dt.date() == date ) {
913 }
else if (foundDate)
break;
915 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
916 times += d->mRRules[i]->recurTimesOn( date, timeSpec );
922 for ( i = 0, end = d->mExDateTimes.count(); i < end; ++i ) {
923 dt = d->mExDateTimes[i].toTimeSpec( timeSpec );
924 if ( dt.date() == date ) {
925 extimes << dt.time();
927 }
else if (foundDate)
break;
930 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
931 extimes += d->mExRules[i]->recurTimesOn( date, timeSpec );
937 for ( i = 0, end = extimes.count(); i < end; ++i ) {
950 for ( i = 0, count = d->mRRules.count(); i < count; ++i ) {
951 times += d->mRRules[i]->timesInInterval( start, end );
955 for ( i = 0, count = d->mRDateTimes.count(); i < count; ++i ) {
956 if ( d->mRDateTimes[i] >= start && d->mRDateTimes[i] <= end ) {
957 times += d->mRDateTimes[i];
962 KDateTime kdt( d->mStartDateTime );
963 for ( i = 0, count = d->mRDates.count(); i < count; ++i ) {
964 kdt.setDate( d->mRDates[i] );
965 if ( kdt >= start && kdt <= end ) {
975 if ( ( !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty() ) &&
976 d->mRRules.isEmpty() &&
977 start <= d->mStartDateTime &&
978 end >= d->mStartDateTime ) {
979 times += d->mStartDateTime;
986 int enddt = times.count();
987 for ( i = 0, count = d->mExDates.count(); i < count && idt < enddt; ++i ) {
988 while ( idt < enddt && times[idt].date() < d->mExDates[i] ) ++idt;
989 while ( idt < enddt && times[idt].date() == d->mExDates[i] ) {
995 for ( i = 0, count = d->mExRules.count(); i < count; ++i ) {
996 extimes += d->mExRules[i]->timesInInterval( start, end );
998 extimes += d->mExDateTimes;
1002 for ( i = 0, count = extimes.count(); i < count; ++i ) {
1014 KDateTime nextDT = preDateTime;
1021 while ( loop < 1000 ) {
1041 int i = d->mRDateTimes.findGT( nextDT );
1043 dates << d->mRDateTimes[i];
1047 for ( i = 0, end = d->mRDates.count(); i < end; ++i ) {
1048 kdt.setDate( d->mRDates[i] );
1049 if ( kdt > nextDT ) {
1056 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
1057 KDateTime dt = d->mRRules[i]->getNextDate( nextDT );
1058 if ( dt.isValid() ) {
1065 if ( dates.isEmpty() ) {
1068 nextDT = dates.first();
1071 if ( !d->mExDates.containsSorted( nextDT.date() ) &&
1072 !d->mExDateTimes.containsSorted( nextDT ) ) {
1073 bool allowed =
true;
1074 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
1075 allowed = allowed && !( d->mExRules[i]->recursAt( nextDT ) );
1089 KDateTime prevDT = afterDateTime;
1094 while ( loop < 1000 ) {
1111 int i = d->mRDateTimes.findLT( prevDT );
1113 dates << d->mRDateTimes[i];
1117 for ( i = d->mRDates.count(); --i >= 0; ) {
1118 kdt.setDate( d->mRDates[i] );
1119 if ( kdt < prevDT ) {
1127 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
1128 KDateTime dt = d->mRRules[i]->getPreviousDate( prevDT );
1129 if ( dt.isValid() ) {
1136 if ( dates.isEmpty() ) {
1139 prevDT = dates.last();
1142 if ( !d->mExDates.containsSorted( prevDT.date() ) &&
1143 !d->mExDateTimes.containsSorted( prevDT ) ) {
1144 bool allowed =
true;
1145 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
1146 allowed = allowed && !( d->mExRules[i]->recursAt( prevDT ) );
1167 if ( d->mRecurReadOnly || !rrule ) {
1172 d->mRRules.append( rrule );
1179 if (d->mRecurReadOnly) {
1183 d->mRRules.removeAll( rrule );
1190 if (d->mRecurReadOnly) {
1194 d->mRRules.removeAll( rrule );
1206 if ( d->mRecurReadOnly || !exrule ) {
1211 d->mExRules.append( exrule );
1218 if ( d->mRecurReadOnly ) {
1222 d->mExRules.removeAll( exrule );
1229 if ( d->mRecurReadOnly ) {
1233 d->mExRules.removeAll( exrule );
1240 return d->mRDateTimes;
1243 void Recurrence::setRDateTimes(
const DateTimeList &rdates )
1245 if ( d->mRecurReadOnly ) {
1249 d->mRDateTimes = rdates;
1254 void Recurrence::addRDateTime(
const KDateTime &rdate )
1256 if ( d->mRecurReadOnly ) {
1260 d->mRDateTimes.insertSorted( rdate );
1264 DateList Recurrence::rDates()
const
1269 void Recurrence::setRDates(
const DateList &rdates )
1271 if ( d->mRecurReadOnly ) {
1275 d->mRDates = rdates;
1280 void Recurrence::addRDate(
const QDate &rdate )
1282 if ( d->mRecurReadOnly ) {
1286 d->mRDates.insertSorted( rdate );
1292 return d->mExDateTimes;
1295 void Recurrence::setExDateTimes(
const DateTimeList &exdates )
1297 if ( d->mRecurReadOnly ) {
1301 d->mExDateTimes = exdates;
1305 void Recurrence::addExDateTime(
const KDateTime &exdate )
1307 if ( d->mRecurReadOnly ) {
1311 d->mExDateTimes.insertSorted( exdate );
1315 DateList Recurrence::exDates()
const
1320 void Recurrence::setExDates(
const DateList &exdates )
1322 if ( d->mRecurReadOnly ) {
1326 d->mExDates = exdates;
1331 void Recurrence::addExDate(
const QDate &exdate )
1333 if ( d->mRecurReadOnly ) {
1337 d->mExDates.insertSorted( exdate );
1353 int count = d->mRRules.count();
1354 kDebug() <<
" -)" << count <<
"RRULEs:";
1355 for ( i = 0; i < count; ++i ) {
1356 kDebug() <<
" -) RecurrenceRule: ";
1357 d->mRRules[i]->dump();
1359 count = d->mExRules.count();
1360 kDebug() <<
" -)" << count <<
"EXRULEs:";
1361 for ( i = 0; i < count; ++i ) {
1362 kDebug() <<
" -) ExceptionRule :";
1363 d->mExRules[i]->dump();
1366 count = d->mRDates.count();
1367 kDebug() << endl <<
" -)" << count <<
"Recurrence Dates:";
1368 for ( i = 0; i < count; ++i ) {
1369 kDebug() <<
" " << d->mRDates[i];
1371 count = d->mRDateTimes.count();
1372 kDebug() << endl <<
" -)" << count <<
"Recurrence Date/Times:";
1373 for ( i = 0; i < count; ++i ) {
1374 kDebug() <<
" " << d->mRDateTimes[i].dateTime();
1376 count = d->mExDates.count();
1377 kDebug() << endl <<
" -)" << count <<
"Exceptions Dates:";
1378 for ( i = 0; i < count; ++i ) {
1379 kDebug() <<
" " << d->mExDates[i];
1381 count = d->mExDateTimes.count();
1382 kDebug() << endl <<
" -)" << count <<
"Exception Date/Times:";
1383 for ( i = 0; i < count; ++i ) {
1384 kDebug() <<
" " << d->mExDateTimes[i].dateTime();