39 #include "icaltimezones.h"
46 #include <icaltimezone.h>
51 using namespace KCalCore;
58 class KCalCore::Calendar::Private
65 mObserversEnabled(true),
67 batchAddingInProgress(false),
68 mDeletionTracking(true)
71 mFilter = mDefaultFilter;
72 mFilter->setEnabled(
false);
75 mOwner->setName(
"Unknown Name");
76 mOwner->setEmail(
"unknown@nowhere");
83 if (mFilter != mDefaultFilter) {
86 delete mDefaultFilter;
88 KDateTime::Spec timeZoneIdSpec(
const QString &timeZoneId,
bool view);
95 KDateTime::Spec mTimeSpec;
96 mutable KDateTime::Spec mViewTimeSpec;
99 bool mObserversEnabled;
100 QList<CalendarObserver*> mObservers;
106 QMultiHash<QString, Incidence::Ptr> mOrphans;
107 QMultiHash<QString, Incidence::Ptr> mOrphanUids;
110 QMultiHash<QString, Incidence::Ptr >mNotebookIncidences;
111 QHash<QString, QString>mUidToNotebook;
112 QHash<QString, bool>mNotebooks;
113 QHash<Incidence::Ptr, bool>mIncidenceVisibility;
114 QString mDefaultNotebook;
115 QMap<QString, Incidence::List > mIncidenceRelations;
116 bool batchAddingInProgress;
117 bool mDeletionTracking;
123 template <
typename K,
typename V>
124 QVector<V> values(
const QMultiHash<K,V> &c)
128 for (
typename QMultiHash<K,V>::const_iterator it = c.begin(), end = c.end(); it != end; ++it) {
129 v.push_back(it.value());
134 template <
typename K,
typename V>
135 QVector<V> values(
const QMultiHash<K,V> &c,
const K &x)
138 typename QMultiHash<K,V>::const_iterator it = c.find(x);
139 while (it != c.end() && it.key() == x) {
140 v.push_back(it.value());
151 class AddVisitor :
public Visitor
154 AddVisitor(T *r) : mResource(r) {}
158 return mResource->addEvent(e);
162 return mResource->addTodo(t);
166 return mResource->addJournal(j);
183 class DeleteVisitor :
public Visitor
186 DeleteVisitor(T *r) : mResource(r) {}
190 mResource->deleteEvent(e);
195 mResource->deleteTodo(t);
200 mResource->deleteJournal(j);
214 : d(new KCalCore::
Calendar::Private)
221 : d(new KCalCore::
Calendar::Private)
259 d->mTimeSpec = d->timeZoneIdSpec(timeZoneId,
false);
260 d->mViewTimeSpec = d->mTimeSpec;
261 d->mBuiltInViewTimeZone = d->mBuiltInTimeZone;
267 KDateTime::Spec Calendar::Private::timeZoneIdSpec(
const QString &timeZoneId,
275 if (timeZoneId == QLatin1String(
"UTC")) {
276 return KDateTime::UTC;
281 tz = tzsrc.
parse(icaltimezone_get_builtin_timezone(timeZoneId.toLatin1()));
283 mBuiltInViewTimeZone = tz;
285 mBuiltInTimeZone = tz;
291 return KDateTime::ClockTime;
298 KTimeZone tz = d->mTimeSpec.timeZone();
299 return tz.isValid() ? tz.name() : QString();
310 d->mViewTimeSpec = d->timeZoneIdSpec(timeZoneId,
true);
315 return d->mViewTimeSpec;
320 KTimeZone tz = d->mViewTimeSpec.timeZone();
321 return tz.isValid() ? tz.name() : QString();
326 return d->mTimeZones;
335 if (d->mTimeZones && (d->mTimeZones != zones)) {
336 delete d->mTimeZones;
339 d->mTimeZones = zones;
348 for (i = 0, end = ev.count(); i < end; ++i) {
349 ev[i]->shiftTimes(oldSpec, newSpec);
353 for (i = 0, end = to.count(); i < end; ++i) {
354 to[i]->shiftTimes(oldSpec, newSpec);
358 for (i = 0, end = jo.count(); i < end; ++i) {
359 jo[i]->shiftTimes(oldSpec, newSpec);
368 d->mFilter = d->mDefaultFilter;
381 QStringList cats, thisCats;
384 for (Incidence::List::ConstIterator i = rawInc.constBegin();
385 i != rawInc.constEnd(); ++i) {
386 thisCats = (*i)->categories();
387 for (QStringList::ConstIterator si = thisCats.constBegin();
388 si != thisCats.constEnd(); ++si) {
389 if (!cats.contains(*si)) {
437 Incidence::List::const_iterator it;
438 for (it = vals.constBegin(); it != vals.constEnd(); ++it) {
439 if (((incidence->dtStart() == (*it)->dtStart()) ||
440 (!incidence->dtStart().isValid() && !(*it)->dtStart().isValid())) &&
441 (incidence->summary() == (*it)->summary())) {
453 if (d->mNotebooks.contains(notebook)) {
456 d->mNotebooks.insert(notebook, isVisible);
463 if (!d->mNotebooks.contains(notebook)) {
466 d->mNotebooks.insert(notebook, isVisible);
473 if (!d->mNotebooks.contains(notebook)) {
476 return d->mNotebooks.remove(notebook);
482 if (!d->mNotebooks.contains(notebook)) {
492 return d->mDefaultNotebook;
497 return d->mNotebooks.contains(notebook);
502 if (d->mIncidenceVisibility.contains(incidence)) {
503 return d->mIncidenceVisibility[
incidence];
505 const QString nuid =
notebook(incidence);
507 if (d->mNotebooks.contains(nuid)) {
508 rv = d->mNotebooks.value(nuid);
519 d->mNotebookIncidences.clear();
520 d->mUidToNotebook.clear();
521 d->mIncidenceVisibility.clear();
530 if (!notebook.isEmpty() &&
531 !
incidence(inc->uid(), inc->recurrenceId())) {
532 kWarning() <<
"cannot set notebook until incidence has been added";
536 if (d->mUidToNotebook.contains(inc->uid())) {
537 QString old = d->mUidToNotebook.value(inc->uid());
538 if (!old.isEmpty() && notebook != old) {
539 if (inc->hasRecurrenceId()) {
540 kWarning() <<
"cannot set notebook for child incidences";
545 Incidence::List::Iterator it;
546 for (it = list.begin(); it != list.end(); ++it) {
547 d->mNotebookIncidences.remove(old, *it);
548 d->mNotebookIncidences.insert(notebook, *it);
552 d->mNotebookIncidences.remove(old, inc);
555 if (!notebook.isEmpty()) {
556 d->mUidToNotebook.insert(inc->uid(),
notebook);
557 d->mNotebookIncidences.insert(notebook, inc);
558 kDebug() <<
"setting notebook" << notebook <<
"for" << inc->uid();
568 return d->mUidToNotebook.value(incidence->uid());
576 return d->mUidToNotebook.value(uid);
581 return d->mNotebookIncidences.uniqueKeys();
586 if (notebook.isEmpty()) {
587 return values(d->mNotebookIncidences);
589 return values(d->mNotebookIncidences, notebook);
599 if (eventList.isEmpty()) {
607 eventListSorted = eventList;
614 qSort(eventListSorted.begin(), eventListSorted.end(), Events::startDateLessThan);
616 qSort(eventListSorted.begin(), eventListSorted.end(), Events::startDateMoreThan);
622 qSort(eventListSorted.begin(), eventListSorted.end(), Events::endDateLessThan);
624 qSort(eventListSorted.begin(), eventListSorted.end(), Events::endDateMoreThan);
630 qSort(eventListSorted.begin(), eventListSorted.end(), Events::summaryLessThan);
632 qSort(eventListSorted.begin(), eventListSorted.end(), Events::summaryMoreThan);
637 return eventListSorted;
642 const KDateTime::Spec &timeSpec,
647 d->mFilter->apply(&el);
654 d->mFilter->apply(&el);
659 const KDateTime::Spec &timeSpec,
660 bool inclusive)
const
663 d->mFilter->apply(&el);
671 d->mFilter->apply(&el);
681 AddVisitor<Calendar> v(
this);
682 return incidence->accept(v, incidence);
692 DeleteVisitor<Calendar> v(
this);
693 const bool result = incidence->accept(v, incidence);
702 const KDateTime &recurrenceId,
705 Q_ASSERT(recurrenceId.isValid());
706 if (!incidence || !incidence->recurs() || !recurrenceId.isValid()) {
711 newInc->setCreated(KDateTime::currentUtcDateTime());
712 newInc->setRevision(0);
714 newInc->clearRecurrence();
716 newInc->setRecurrenceId(recurrenceId);
717 newInc->setThisAndFuture(thisAndFuture);
718 newInc->setDtStart(recurrenceId);
724 if (incidence->dtStart().isDateOnly()) {
725 int offset = incidence->dtStart().daysTo(recurrenceId);
726 end = end.addDays(offset);
728 qint64 offset = incidence->dtStart().secsTo_long(recurrenceId);
729 end = end.addSecs(offset);
741 const KDateTime::Spec &spec,
744 if (!incidence || !incidence->recurs()) {
764 int doneduration = recur->
durationTo(date.addDays(-1));
765 if (doneduration >= duration) {
766 kDebug() <<
"The dissociated event already occurred more often"
767 <<
"than it was supposed to ever occur. ERROR!";
777 KDateTime start(ev->dtStart());
778 int daysTo = start.toTimeSpec(spec).date().daysTo(date);
780 ev->setDtEnd(ev->dtEnd().addDays(daysTo));
783 bool haveOffset =
false;
785 if (td->hasDueDate()) {
786 KDateTime due(td->dtDue());
787 daysTo = due.toTimeSpec(spec).date().daysTo(date);
788 td->
setDtDue(due.addDays(daysTo),
true);
791 if (td->hasStartDate()) {
792 KDateTime start(td->dtStart());
794 daysTo = start.toTimeSpec(spec).date().daysTo(date);
796 td->setDtStart(start.addDays(daysTo));
800 recur = incidence->recurrence();
803 recur->addExDate(date);
814 const KDateTime &recurrenceId)
const
821 i =
todo(uid, recurrenceId);
826 i =
journal(uid, recurrenceId);
850 Incidence::List::const_iterator it = incidences.begin();
851 for (; it != incidences.end(); ++it) {
852 if ((*it)->schedulingID() == sid) {
862 Incidence::List::const_iterator it = incidences.begin();
863 for (; it != incidences.end(); ++it) {
864 if ((*it)->schedulingID() == uid) {
878 if (todoList.isEmpty()) {
889 todoListSorted = todoList;
896 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::startDateLessThan);
898 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::startDateMoreThan);
904 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::dueDateLessThan);
906 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::dueDateMoreThan);
912 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::priorityLessThan);
914 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::priorityMoreThan);
920 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::percentLessThan);
922 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::percentMoreThan);
928 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::summaryLessThan);
930 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::summaryMoreThan);
936 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::createdLessThan);
938 qSort(todoListSorted.begin(), todoListSorted.end(), Todos::createdMoreThan);
943 return todoListSorted;
950 d->mFilter->apply(&tl);
957 d->mFilter->apply(&el);
962 const KDateTime::Spec ×pec,
bool inclusive)
const
965 d->mFilter->apply(&tl);
974 if (journalList.isEmpty()) {
986 qSort(journalListSorted.begin(), journalListSorted.end(), Journals::dateLessThan);
988 qSort(journalListSorted.begin(), journalListSorted.end(), Journals::dateMoreThan);
994 qSort(journalListSorted.begin(), journalListSorted.end(), Journals::summaryLessThan);
996 qSort(journalListSorted.begin(), journalListSorted.end(), Journals::summaryMoreThan);
1001 return journalListSorted;
1008 d->mFilter->apply(&jl);
1015 d->mFilter->apply(&el);
1023 if (!forincidence) {
1027 const QString uid = forincidence->uid();
1031 d->mOrphans.remove(uid);
1032 for (
int i = 0, end = l.count(); i < end; ++i) {
1033 d->mIncidenceRelations[uid].append(l[i]);
1034 d->mOrphanUids.remove(l[i]->uid());
1038 if (forincidence->relatedTo().isEmpty() && !forincidence->relatedTo().isEmpty()) {
1047 forincidence->setRelatedTo(QString());
1048 kWarning() <<
"hierarchy loop beetween " << forincidence->uid() <<
" and " << parent->uid();
1050 d->mIncidenceRelations[parent->uid()].append(forincidence);
1057 d->mOrphans.insert(forincidence->relatedTo(), forincidence);
1058 d->mOrphanUids.insert(forincidence->uid(), forincidence);
1067 kDebug() <<
"Warning: incidence is 0";
1071 const QString uid = incidence->uid();
1074 if (!d->mOrphanUids.contains(i->uid())) {
1075 d->mOrphans.insert(uid, i);
1076 d->mOrphanUids.insert(i->uid(), i);
1077 i->setRelatedTo(uid);
1081 const QString parentUid = incidence->relatedTo();
1084 if (!parentUid.isEmpty()) {
1085 d->mIncidenceRelations[parentUid].erase(
1086 std::remove(d->mIncidenceRelations[parentUid].begin(),
1087 d->mIncidenceRelations[parentUid].end(),
incidence),
1088 d->mIncidenceRelations[parentUid].end());
1092 if (d->mOrphanUids.remove(uid)) {
1102 QStringList relatedToUids;
1106 relatedToUids << incidence->relatedTo();
1107 for (QMultiHash<QString, Incidence::Ptr>::Iterator it = d->mOrphans.begin();
1108 it != d->mOrphans.end(); ++it) {
1109 if (it.value()->uid() == uid) {
1110 relatedToUids << it.key();
1115 for (QStringList::const_iterator uidit = relatedToUids.constBegin();
1116 uidit != relatedToUids.constEnd(); ++uidit) {
1119 QList<Incidence::Ptr> l = d->mOrphans.values(*uidit);
1120 d->mOrphans.remove(*uidit);
1122 if (i != incidence) {
1127 for (Incidence::List::Iterator incit = tempList.begin();
1128 incit != tempList.end(); ++incit) {
1129 d->mOrphans.insert(*uidit, *incit);
1150 if (!incidence || incidence->relatedTo().isEmpty()) {
1152 }
else if (incidence->relatedTo() == ancestor->uid()) {
1161 return d->mIncidenceRelations[uid];
1176 Q_UNUSED(incidence);
1181 Q_UNUSED(incidence);
1186 Q_UNUSED(incidence);
1192 Q_UNUSED(incidence);
1201 if (!d->mObservers.contains(observer)) {
1202 d->mObservers.append(observer);
1204 d->mNewObserver =
true;
1213 d->mObservers.removeAll(observer);
1224 if (modified != d->mModified || d->mNewObserver) {
1225 d->mNewObserver =
false;
1229 d->mModified = modified;
1235 return d->mModified;
1257 inc->setLastModified(KDateTime::currentUtcDateTime());
1278 if (!d->mObserversEnabled) {
1293 if (!d->mObserversEnabled) {
1308 if (!d->mObserversEnabled) {
1323 if (!d->mObserversEnabled) {
1344 return d->mProductId;
1355 for (i = 0, end = events.count(); i < end; ++i) {
1356 incidences.append(events[i]);
1359 for (i = 0, end = todos.count(); i < end; ++i) {
1360 incidences.append(todos[i]);
1363 for (i = 0, end = journals.count(); i < end; ++i) {
1364 incidences.append(journals[i]);
1372 Q_UNUSED(incidence);
1378 Q_UNUSED(incidence);
1384 d->mObserversEnabled = enabled;
1388 const KDateTime &from,
const KDateTime &to)
const
1390 KDateTime preTime = from.addSecs(-1);
1393 for (
int i = 0, iend = alarmlist.count(); i < iend; ++i) {
1394 if (alarmlist[i]->enabled()) {
1395 KDateTime dt = alarmlist[i]->nextRepetition(preTime);
1396 if (dt.isValid() && dt <= to) {
1397 kDebug() << incidence->summary() <<
"':" << dt.toString();
1398 alarms.append(alarmlist[i]);
1406 const KDateTime &from,
1407 const KDateTime &to)
const
1410 bool endOffsetValid =
false;
1415 for (
int i = 0, iend = alarmlist.count(); i < iend; ++i) {
1420 dt = a->nextRepetition(from.addSecs(-1));
1421 if (!dt.isValid() || dt > to) {
1429 if (a->hasStartOffset()) {
1430 offset = a->startOffset();
1431 }
else if (a->hasEndOffset()) {
1432 offset = a->endOffset();
1433 if (!endOffsetValid) {
1434 endOffset =
Duration(incidence->dtStart(),
1436 endOffsetValid =
true;
1441 KDateTime alarmStart =
1443 incidence->dtStart());
1445 if (alarmStart > to) {
1448 KDateTime baseStart = incidence->dtStart();
1449 if (from > alarmStart) {
1451 baseStart = (-offset).end((-endOffset).end(alarmStart));
1456 dt = incidence->recurrence()->getNextDateTime(baseStart.addSecs(-1));
1457 if (!dt.isValid() ||
1458 (dt = endOffset.
end(offset.
end(dt))) > to)
1461 if (!a->repeatCount()) {
1468 Duration alarmDuration = a->duration();
1469 for (KDateTime base = baseStart;
1470 (dt = incidence->recurrence()->getPreviousDateTime(base)).isValid();
1472 if (a->duration().end(dt) < base) {
1478 int snooze = a->snoozeTime().
value();
1479 if (a->snoozeTime().isDaily()) {
1481 int toFrom = toFromDuration.
asDays();
1482 if (a->snoozeTime().end(from) <= to ||
1483 (toFromDuration.
isDaily() && toFrom % snooze == 0) ||
1484 (toFrom / snooze + 1) * snooze <= toFrom + period.
asDays()) {
1488 dt = offset.
end(dt).addDays(((toFrom - 1) / snooze + 1) * snooze);
1493 int toFrom = dt.secsTo(base);
1495 toFrom % snooze == 0 ||
1496 (toFrom / snooze + 1) * snooze <= toFrom + period.
asSeconds())
1501 dt = offset.
end(dt).addSecs(((toFrom - 1) / snooze + 1) * snooze);
1512 kDebug() << incidence->summary() <<
"':" << dt.toString();
1520 d->batchAddingInProgress =
true;
1525 d->batchAddingInProgress =
false;
1530 return d->batchAddingInProgress;
1535 d->mDeletionTracking = enable;
1540 return d->mDeletionTracking;