24 #include "recurrence.h"
28 #include <QtCore/QBitArray>
30 using namespace KCalCore;
33 class KCalCore::Recurrence::Private
37 : mCachedType( rMax ),
39 mRecurReadOnly( false )
43 Private(
const Private &p )
44 : mRDateTimes( p.mRDateTimes ),
46 mExDateTimes( p.mExDateTimes ),
47 mExDates( p.mExDates ),
48 mStartDateTime( p.mStartDateTime ),
49 mCachedType( p.mCachedType ),
51 mRecurReadOnly( p.mRecurReadOnly )
55 bool operator==(
const Private &p )
const;
57 RecurrenceRule::List mExRules;
58 RecurrenceRule::List mRRules;
63 KDateTime mStartDateTime;
64 QList<RecurrenceObserver*> mObservers;
67 mutable ushort mCachedType;
75 kDebug() << mStartDateTime << p.mStartDateTime;
77 if ( ( mStartDateTime != p.mStartDateTime &&
78 ( mStartDateTime.isValid() || p.mStartDateTime.isValid() ) ) ||
79 mAllDay != p.mAllDay ||
80 mRecurReadOnly != p.mRecurReadOnly ||
81 mExDates != p.mExDates ||
82 mExDateTimes != p.mExDateTimes ||
83 mRDates != p.mRDates ||
84 mRDateTimes != p.mRDateTimes ) {
91 int end = mRRules.count();
92 if ( end != p.mRRules.count() ) {
95 for ( i = 0; i < end; ++i ) {
96 if ( *mRRules[i] != *p.mRRules[i] ) {
100 end = mExRules.count();
101 if ( end != p.mExRules.count() ) {
104 for ( i = 0; i < end; ++i ) {
105 if ( *mExRules[i] != *p.mExRules[i] ) {
120 d( new KCalCore::
Recurrence::Private( *r.d ) )
123 for ( i = 0, end = r.d->mRRules.count(); i < end; ++i ) {
125 d->mRRules.append( rule );
128 for ( i = 0, end = r.d->mExRules.count(); i < end; ++i ) {
130 d->mExRules.append( rule );
137 qDeleteAll( d->mExRules );
138 qDeleteAll( d->mRRules );
144 return *d == *recurrence.d;
150 if ( &recurrence ==
this ) {
160 if ( !d->mObservers.contains( observer ) ) {
161 d->mObservers.append( observer );
167 if ( d->mObservers.contains( observer ) ) {
168 d->mObservers.removeAll( observer );
174 return d->mStartDateTime;
184 if ( d->mRecurReadOnly || allDay == d->mAllDay ) {
189 for (
int i = 0, end = d->mRRules.count(); i < end; ++i ) {
190 d->mRRules[i]->setAllDay( allDay );
192 for (
int i = 0, end = d->mExRules.count(); i < end; ++i ) {
193 d->mExRules[i]->setAllDay( allDay );
200 if ( d->mRRules.isEmpty() ) {
201 if ( !create || d->mRecurReadOnly ) {
209 return d->mRRules[0];
215 return d->mRRules.isEmpty() ? 0 : d->mRRules[0];
218 void Recurrence::updated()
221 d->mCachedType = rMax;
222 for (
int i = 0, end = d->mObservers.count(); i < end; ++i ) {
223 if ( d->mObservers[i] ) {
224 d->mObservers[i]->recurrenceUpdated(
this );
231 return !d->mRRules.isEmpty() || !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty();
236 if ( d->mCachedType == rMax ) {
239 return d->mCachedType;
250 if ( !rrule->bySetPos().isEmpty() ||
251 !rrule->bySeconds().isEmpty() ||
252 !rrule->byWeekNumbers().isEmpty() ) {
258 if ( !rrule->byMinutes().isEmpty() || !rrule->byHours().isEmpty() ) {
267 if ( ( !rrule->byYearDays().isEmpty() && type != RecurrenceRule::rYearly ) ||
268 ( !rrule->byMonths().isEmpty() && type != RecurrenceRule::rYearly ) ) {
271 if ( !rrule->byDays().isEmpty() ) {
272 if ( type != RecurrenceRule::rYearly &&
273 type != RecurrenceRule::rMonthly &&
274 type != RecurrenceRule::rWeekly ) {
280 case RecurrenceRule::rNone:
282 case RecurrenceRule::rMinutely:
284 case RecurrenceRule::rHourly:
286 case RecurrenceRule::rDaily:
288 case RecurrenceRule::rWeekly:
290 case RecurrenceRule::rMonthly:
292 if ( rrule->byDays().isEmpty() ) {
294 }
else if ( rrule->byMonthDays().isEmpty() ) {
300 case RecurrenceRule::rYearly:
306 if ( !rrule->byDays().isEmpty() ) {
308 if ( rrule->byMonthDays().isEmpty() && rrule->byYearDays().isEmpty() ) {
313 }
else if ( !rrule->byYearDays().isEmpty() ) {
315 if ( rrule->byMonths().isEmpty() && rrule->byMonthDays().isEmpty() ) {
325 default:
return rOther;
333 if ( KDateTime( qd, QTime( 23, 59, 59 ), timeSpec ) < d->mStartDateTime ) {
338 if ( d->mExDates.containsSorted( qd ) ) {
347 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
348 if ( d->mExRules[i]->recursOn( qd, timeSpec ) ) {
354 if ( d->mRDates.containsSorted( qd ) ) {
360 for ( i = 0, end = d->mRDateTimes.count(); i < end && !
recurs; ++i ) {
361 recurs = ( d->mRDateTimes[i].toTimeSpec( timeSpec ).date() == qd );
363 for ( i = 0, end = d->mRRules.count(); i < end && !
recurs; ++i ) {
364 recurs = d->mRRules[i]->recursOn( qd, timeSpec );
373 for ( i = 0, end = d->mExDateTimes.count(); i < end && !exon; ++i ) {
374 exon = ( d->mExDateTimes[i].toTimeSpec( timeSpec ).date() == qd );
377 for ( i = 0, end = d->mExRules.count(); i < end && !exon; ++i ) {
378 exon = d->mExRules[i]->recursOn( qd, timeSpec );
391 return !timesForDay.isEmpty();
398 KDateTime dtrecur = dt.toTimeSpec( d->mStartDateTime.timeSpec() );
401 if ( d->mExDateTimes.containsSorted( dtrecur ) ||
402 d->mExDates.containsSorted( dtrecur.date() ) ) {
406 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
407 if ( d->mExRules[i]->recursAt( dtrecur ) ) {
413 if (
startDateTime() == dtrecur || d->mRDateTimes.containsSorted( dtrecur ) ) {
416 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
417 if ( d->mRRules[i]->recursAt( dtrecur ) ) {
432 if ( !d->mRDates.isEmpty() ) {
433 dts << KDateTime( d->mRDates.last(), QTime( 0, 0, 0 ), d->mStartDateTime.timeSpec() );
435 if ( !d->mRDateTimes.isEmpty() ) {
436 dts << d->mRDateTimes.last();
438 for (
int i = 0, end = d->mRRules.count(); i < end; ++i ) {
439 KDateTime rl( d->mRRules[i]->endDt() );
441 if ( !rl.isValid() ) {
447 return dts.isEmpty() ? KDateTime() : dts.last();
456 return end.isValid() ? end.date() : QDate();
461 KDateTime dt( date, d->mStartDateTime.time(), d->mStartDateTime.timeSpec() );
463 dt.setTime( QTime( 23, 59, 59 ) );
470 if ( d->mRecurReadOnly ) {
484 return rrule ? rrule->
duration() : 0;
491 return rrule ? rrule->
durationTo( datetime ) : 0;
496 return durationTo( KDateTime( date, QTime( 23, 59, 59 ), d->mStartDateTime.timeSpec() ) );
501 if ( d->mRecurReadOnly ) {
515 if ( d->mRecurReadOnly ) {
519 d->mStartDateTime = d->mStartDateTime.toTimeSpec( oldSpec );
520 d->mStartDateTime.setTimeSpec( newSpec );
523 for ( i = 0, end = d->mRDateTimes.count(); i < end; ++i ) {
524 d->mRDateTimes[i] = d->mRDateTimes[i].toTimeSpec( oldSpec );
525 d->mRDateTimes[i].setTimeSpec( newSpec );
527 for ( i = 0, end = d->mExDateTimes.count(); i < end; ++i ) {
528 d->mExDateTimes[i] = d->mExDateTimes[i].toTimeSpec( oldSpec );
529 d->mExDateTimes[i].setTimeSpec( newSpec );
531 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
532 d->mRRules[i]->shiftTimes( oldSpec, newSpec );
534 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
535 d->mExRules[i]->shiftTimes( oldSpec, newSpec );
541 if ( d->mRecurReadOnly ) {
544 qDeleteAll( d->mRRules );
551 if ( d->mRecurReadOnly ) {
554 qDeleteAll( d->mRRules );
556 qDeleteAll( d->mExRules );
559 d->mRDateTimes.clear();
561 d->mExDateTimes.clear();
562 d->mCachedType = rMax;
568 d->mRecurReadOnly = readOnly;
573 return d->mRecurReadOnly;
578 return d->mStartDateTime.date();
583 if ( d->mRecurReadOnly ) {
586 d->mStartDateTime = start;
590 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
591 d->mRRules[i]->setStartDt( start );
593 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
594 d->mExRules[i]->setStartDt( start );
609 if ( d->mRecurReadOnly || freq <= 0 ) {
625 return rrule ? rrule->weekStart() : 1;
635 QList<RecurrenceRule::WDayPos> bydays = rrule->byDays();
636 for (
int i = 0; i < bydays.size(); ++i ) {
637 if ( bydays.at(i).pos() == 0 ) {
638 days.setBit( bydays.at( i ).day() - 1 );
652 return rrule->byMonthDays();
662 return rrule ? rrule->byDays() : QList<RecurrenceRule::WDayPos>();
670 return rrule ? rrule->byYearDays() : QList<int>();
681 return rrule ? rrule->byMonths() : QList<int>();
691 if ( d->mRecurReadOnly || freq <= 0 ) {
695 qDeleteAll( d->mRRules );
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 ) );
1160 RecurrenceRule::List Recurrence::rRules()
const
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 );
1199 RecurrenceRule::List Recurrence::exRules()
const
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();
1388 Recurrence::RecurrenceObserver::~RecurrenceObserver()