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;
73 bool Recurrence::Private::operator==(
const Recurrence::Private &p )
const
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 ) {
917 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
918 times += d->mRRules[i]->recurTimesOn( date, timeSpec );
924 for ( i = 0, end = d->mExDateTimes.count(); i < end; ++i ) {
925 dt = d->mExDateTimes[i].toTimeSpec( timeSpec );
926 if ( dt.date() == date ) {
927 extimes << dt.time();
929 }
else if ( foundDate ) {
934 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
935 extimes += d->mExRules[i]->recurTimesOn( date, timeSpec );
941 for ( i = 0, end = extimes.count(); i < end; ++i ) {
954 for ( i = 0, count = d->mRRules.count(); i < count; ++i ) {
955 times += d->mRRules[i]->timesInInterval( start, end );
959 for ( i = 0, count = d->mRDateTimes.count(); i < count; ++i ) {
960 if ( d->mRDateTimes[i] >= start && d->mRDateTimes[i] <= end ) {
961 times += d->mRDateTimes[i];
966 KDateTime kdt( d->mStartDateTime );
967 for ( i = 0, count = d->mRDates.count(); i < count; ++i ) {
968 kdt.setDate( d->mRDates[i] );
969 if ( kdt >= start && kdt <= end ) {
979 if ( ( !d->mRDates.isEmpty() || !d->mRDateTimes.isEmpty() ) &&
980 d->mRRules.isEmpty() &&
981 start <= d->mStartDateTime &&
982 end >= d->mStartDateTime ) {
983 times += d->mStartDateTime;
990 int enddt = times.count();
991 for ( i = 0, count = d->mExDates.count(); i < count && idt < enddt; ++i ) {
992 while ( idt < enddt && times[idt].date() < d->mExDates[i] ) {
995 while ( idt < enddt && times[idt].date() == d->mExDates[i] ) {
996 times.removeAt( idt );
1001 for ( i = 0, count = d->mExRules.count(); i < count; ++i ) {
1002 extimes += d->mExRules[i]->timesInInterval( start, end );
1004 extimes += d->mExDateTimes;
1008 for ( i = 0, count = extimes.count(); i < count; ++i ) {
1020 KDateTime nextDT = preDateTime;
1027 while ( loop < 1000 ) {
1047 int i = d->mRDateTimes.findGT( nextDT );
1049 dates << d->mRDateTimes[i];
1053 for ( i = 0, end = d->mRDates.count(); i < end; ++i ) {
1054 kdt.setDate( d->mRDates[i] );
1055 if ( kdt > nextDT ) {
1062 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
1063 KDateTime dt = d->mRRules[i]->getNextDate( nextDT );
1064 if ( dt.isValid() ) {
1071 if ( dates.isEmpty() ) {
1074 nextDT = dates.first();
1077 if ( !d->mExDates.containsSorted( nextDT.date() ) &&
1078 !d->mExDateTimes.containsSorted( nextDT ) ) {
1079 bool allowed =
true;
1080 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
1081 allowed = allowed && !( d->mExRules[i]->recursAt( nextDT ) );
1095 KDateTime prevDT = afterDateTime;
1100 while ( loop < 1000 ) {
1117 int i = d->mRDateTimes.findLT( prevDT );
1119 dates << d->mRDateTimes[i];
1123 for ( i = d->mRDates.count(); --i >= 0; ) {
1124 kdt.setDate( d->mRDates[i] );
1125 if ( kdt < prevDT ) {
1133 for ( i = 0, end = d->mRRules.count(); i < end; ++i ) {
1134 KDateTime dt = d->mRRules[i]->getPreviousDate( prevDT );
1135 if ( dt.isValid() ) {
1142 if ( dates.isEmpty() ) {
1145 prevDT = dates.last();
1148 if ( !d->mExDates.containsSorted( prevDT.date() ) &&
1149 !d->mExDateTimes.containsSorted( prevDT ) ) {
1150 bool allowed =
true;
1151 for ( i = 0, end = d->mExRules.count(); i < end; ++i ) {
1152 allowed = allowed && !( d->mExRules[i]->recursAt( prevDT ) );
1166 RecurrenceRule::List Recurrence::rRules()
const
1173 if ( d->mRecurReadOnly || !rrule ) {
1178 d->mRRules.append( rrule );
1185 if (d->mRecurReadOnly) {
1189 d->mRRules.removeAll( rrule );
1196 if (d->mRecurReadOnly) {
1200 d->mRRules.removeAll( rrule );
1205 RecurrenceRule::List Recurrence::exRules()
const
1212 if ( d->mRecurReadOnly || !exrule ) {
1217 d->mExRules.append( exrule );
1224 if ( d->mRecurReadOnly ) {
1228 d->mExRules.removeAll( exrule );
1235 if ( d->mRecurReadOnly ) {
1239 d->mExRules.removeAll( exrule );
1246 return d->mRDateTimes;
1249 void Recurrence::setRDateTimes(
const DateTimeList &rdates )
1251 if ( d->mRecurReadOnly ) {
1255 d->mRDateTimes = rdates;
1260 void Recurrence::addRDateTime(
const KDateTime &rdate )
1262 if ( d->mRecurReadOnly ) {
1266 d->mRDateTimes.insertSorted( rdate );
1270 DateList Recurrence::rDates()
const
1275 void Recurrence::setRDates(
const DateList &rdates )
1277 if ( d->mRecurReadOnly ) {
1281 d->mRDates = rdates;
1286 void Recurrence::addRDate(
const QDate &rdate )
1288 if ( d->mRecurReadOnly ) {
1292 d->mRDates.insertSorted( rdate );
1298 return d->mExDateTimes;
1301 void Recurrence::setExDateTimes(
const DateTimeList &exdates )
1303 if ( d->mRecurReadOnly ) {
1307 d->mExDateTimes = exdates;
1311 void Recurrence::addExDateTime(
const KDateTime &exdate )
1313 if ( d->mRecurReadOnly ) {
1317 d->mExDateTimes.insertSorted( exdate );
1321 DateList Recurrence::exDates()
const
1326 void Recurrence::setExDates(
const DateList &exdates )
1328 if ( d->mRecurReadOnly ) {
1332 d->mExDates = exdates;
1337 void Recurrence::addExDate(
const QDate &exdate )
1339 if ( d->mRecurReadOnly ) {
1343 d->mExDates.insertSorted( exdate );
1359 int count = d->mRRules.count();
1360 kDebug() <<
" -)" << count <<
"RRULEs:";
1361 for ( i = 0; i < count; ++i ) {
1362 kDebug() <<
" -) RecurrenceRule: ";
1363 d->mRRules[i]->dump();
1365 count = d->mExRules.count();
1366 kDebug() <<
" -)" << count <<
"EXRULEs:";
1367 for ( i = 0; i < count; ++i ) {
1368 kDebug() <<
" -) ExceptionRule :";
1369 d->mExRules[i]->dump();
1372 count = d->mRDates.count();
1373 kDebug() << endl <<
" -)" << count <<
"Recurrence Dates:";
1374 for ( i = 0; i < count; ++i ) {
1375 kDebug() <<
" " << d->mRDates[i];
1377 count = d->mRDateTimes.count();
1378 kDebug() << endl <<
" -)" << count <<
"Recurrence Date/Times:";
1379 for ( i = 0; i < count; ++i ) {
1380 kDebug() <<
" " << d->mRDateTimes[i].dateTime();
1382 count = d->mExDates.count();
1383 kDebug() << endl <<
" -)" << count <<
"Exceptions Dates:";
1384 for ( i = 0; i < count; ++i ) {
1385 kDebug() <<
" " << d->mExDates[i];
1387 count = d->mExDateTimes.count();
1388 kDebug() << endl <<
" -)" << count <<
"Exception Date/Times:";
1389 for ( i = 0; i < count; ++i ) {
1390 kDebug() <<
" " << d->mExDateTimes[i].dateTime();
1394 Recurrence::RecurrenceObserver::~RecurrenceObserver()