File indexing completed on 2024-04-28 15:19:08

0001 /*
0002   This file is part of the kcalcore library.
0003 
0004   SPDX-FileCopyrightText: 1998 Preston Brown <pbrown@kde.org>
0005   SPDX-FileCopyrightText: 2001, 2003, 2004 Cornelius Schumacher <schumacher@kde.org>
0006   SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
0007 
0008   SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 /**
0011   @file
0012   This file is part of the API for handling calendar data and
0013   defines the MemoryCalendar class.
0014 
0015   @brief
0016   This class provides a calendar stored as a local file.
0017 
0018   @author Preston Brown \<pbrown@kde.org\>
0019   @author Cornelius Schumacher \<schumacher@kde.org\>
0020  */
0021 
0022 #include "memorycalendar.h"
0023 #include "calformat.h"
0024 #include "kcalendarcore_debug.h"
0025 
0026 #include <QDate>
0027 
0028 #include <functional>
0029 
0030 using namespace KCalendarCore;
0031 
0032 /**
0033   Private class that helps to provide binary compatibility between releases.
0034   @internal
0035 */
0036 //@cond PRIVATE
0037 class Q_DECL_HIDDEN KCalendarCore::MemoryCalendar::Private
0038 {
0039 private:
0040     static constexpr int incidenceTypeCount = 4;
0041 
0042 public:
0043     Private(MemoryCalendar *qq)
0044         : q(qq)
0045         , mFormat(nullptr)
0046         , mUpdateLastModified(true)
0047     {
0048     }
0049     ~Private()
0050     {
0051     }
0052 
0053     MemoryCalendar *q;
0054     CalFormat *mFormat; // calendar format
0055     QString mIncidenceBeingUpdated; //  Instance identifier of Incidence currently being updated
0056     bool mUpdateLastModified; // Call setLastModified() on incidence modific ations
0057 
0058     /**
0059      * List of all incidences.
0060      * First indexed by incidence->type(), then by incidence->uid();
0061      */
0062     QMultiHash<QString, Incidence::Ptr> mIncidences[incidenceTypeCount];
0063 
0064     /**
0065      * Has all incidences, indexed by identifier.
0066      */
0067     QHash<QString, KCalendarCore::Incidence::Ptr> mIncidencesByIdentifier;
0068 
0069     /**
0070      * List of all deleted incidences.
0071      * First indexed by incidence->type(), then by incidence->uid();
0072      */
0073     QMultiHash<QString, Incidence::Ptr> mDeletedIncidences[incidenceTypeCount];
0074 
0075     /**
0076      * Contains incidences ( to-dos; non-recurring, non-multiday events; journals; )
0077      * indexed by start/due date.
0078      *
0079      * The QMap key is the incidence->type().
0080      * The QMultiHash key is the dtStart/dtDue() converted to calendar's timezone
0081      *
0082      * Note: We had 3 variables, mJournalsForDate, mTodosForDate and mEventsForDate
0083      * but i merged them into one (indexed by type) because it simplifies code using
0084      * it. No need to if else based on type.
0085      */
0086     QMultiHash<QDate, Incidence::Ptr> mIncidencesForDate[incidenceTypeCount];
0087 
0088     void insertIncidence(const Incidence::Ptr &incidence);
0089 
0090     Incidence::Ptr incidence(const QString &uid, IncidenceBase::IncidenceType type, const QDateTime &recurrenceId = {}) const;
0091 
0092     bool deleteIncidence(const QString &uid, IncidenceBase::IncidenceType type, const QDateTime &recurrenceId = {});
0093 
0094     Incidence::Ptr deletedIncidence(const QString &uid, const QDateTime &recurrenceId, IncidenceBase::IncidenceType type) const;
0095 
0096     void deleteAllIncidences(IncidenceBase::IncidenceType type);
0097 
0098     template<typename IncidenceType, typename Key>
0099     void forIncidences(const QMultiHash<Key, Incidence::Ptr> &incidences, const Key &key, std::function<void(const typename IncidenceType::Ptr &)> &&op) const
0100     {
0101         for (auto it = incidences.constFind(key), end = incidences.cend(); it != end && it.key() == key; ++it) {
0102             op(it.value().template staticCast<IncidenceType>());
0103         }
0104     }
0105 
0106     template<typename IncidenceType, typename Key>
0107     void forIncidences(const QMultiHash<Key, Incidence::Ptr> &incidences, std::function<void(const typename IncidenceType::Ptr &)> &&op) const
0108     {
0109         for (const auto &incidence : incidences) {
0110             op(incidence.template staticCast<IncidenceType>());
0111         }
0112     }
0113 
0114     template<typename IncidenceType>
0115     typename IncidenceType::List castIncidenceList(const QMultiHash<QString, Incidence::Ptr> &incidences) const
0116     {
0117         typename IncidenceType::List list;
0118         list.reserve(incidences.size());
0119         std::transform(incidences.cbegin(), incidences.cend(), std::back_inserter(list), [](const Incidence::Ptr &inc) {
0120             return inc.staticCast<IncidenceType>();
0121         });
0122         return list;
0123     }
0124 
0125     template<typename IncidenceType>
0126     typename IncidenceType::List incidenceInstances(IncidenceBase::IncidenceType type, const Incidence::Ptr &incidence) const
0127     {
0128         typename IncidenceType::List list;
0129         forIncidences<IncidenceType, QString>(mIncidences[type], incidence->uid(), [&list](const typename IncidenceType::Ptr &incidence) {
0130             if (incidence->hasRecurrenceId()) {
0131                 list.push_back(incidence);
0132             }
0133         });
0134         return list;
0135     }
0136 
0137     Incidence::Ptr findIncidence(const QMultiHash<QString, Incidence::Ptr> &incidences, const QString &uid, const QDateTime &recurrenceId) const
0138     {
0139         for (auto it = incidences.constFind(uid), end = incidences.cend(); it != end && it.key() == uid; ++it) {
0140             const auto &incidence = it.value();
0141             if (recurrenceId.isNull() && !incidence->hasRecurrenceId()) {
0142                 return incidence;
0143             } else if (!recurrenceId.isNull() && incidence->hasRecurrenceId() && recurrenceId == incidence->recurrenceId()) {
0144                 return incidence;
0145             }
0146         }
0147         return {};
0148     }
0149 };
0150 //@endcond
0151 
0152 MemoryCalendar::MemoryCalendar(const QTimeZone &timeZone)
0153     : Calendar(timeZone)
0154     , d(new KCalendarCore::MemoryCalendar::Private(this))
0155 {
0156 }
0157 
0158 MemoryCalendar::MemoryCalendar(const QByteArray &timeZoneId)
0159     : Calendar(timeZoneId)
0160     , d(new KCalendarCore::MemoryCalendar::Private(this))
0161 {
0162 }
0163 
0164 MemoryCalendar::~MemoryCalendar()
0165 {
0166     close(); // NOLINT false clang-analyzer-optin.cplusplus.VirtualCall
0167     delete d;
0168 }
0169 
0170 void MemoryCalendar::doSetTimeZone(const QTimeZone &timeZone)
0171 {
0172     // Reset date based hashes before storing for the new zone.
0173     for (auto &table : d->mIncidencesForDate) {
0174         table.clear();
0175     }
0176 
0177     for (auto &table : d->mIncidences) {
0178         for (const auto &incidence : table) {
0179             const QDateTime dt = incidence->dateTime(Incidence::RoleCalendarHashing);
0180             if (dt.isValid()) {
0181                 d->mIncidencesForDate[incidence->type()].insert(dt.toTimeZone(timeZone).date(), incidence);
0182             }
0183         }
0184     }
0185 }
0186 
0187 void MemoryCalendar::close()
0188 {
0189     setObserversEnabled(false);
0190 
0191     // Don't call the virtual function deleteEvents() etc, the base class might have
0192     // other ways of deleting the data.
0193     d->deleteAllIncidences(Incidence::TypeEvent);
0194     d->deleteAllIncidences(Incidence::TypeTodo);
0195     d->deleteAllIncidences(Incidence::TypeJournal);
0196 
0197     d->mIncidencesByIdentifier.clear();
0198     for (auto &table : d->mDeletedIncidences) {
0199         table.clear();
0200     }
0201 
0202     clearNotebookAssociations();
0203 
0204     setModified(false);
0205 
0206     setObserversEnabled(true);
0207 }
0208 
0209 bool MemoryCalendar::deleteIncidence(const Incidence::Ptr &incidence)
0210 {
0211     // Handle orphaned children
0212     // relations is an Incidence's property, not a Todo's, so
0213     // we remove relations in deleteIncidence, not in deleteTodo.
0214     removeRelations(incidence);
0215     // Notify while the incidence is still available,
0216     // this is necessary so korganizer still has time to query for exceptions
0217     notifyIncidenceAboutToBeDeleted(incidence);
0218     incidence->unRegisterObserver(this);
0219     const Incidence::IncidenceType type = incidence->type();
0220     const QString &uid = incidence->uid();
0221     bool deleted = d->deleteIncidence(uid, type, incidence->recurrenceId());
0222     if (deleted) {
0223         setModified(true);
0224         if (deletionTracking()) {
0225             d->mDeletedIncidences[type].insert(uid, incidence);
0226         }
0227 
0228         // Delete child-incidences.
0229         if (!incidence->hasRecurrenceId() && incidence->recurs()) {
0230             deleteIncidenceInstances(incidence);
0231         }
0232     } else {
0233         qCWarning(KCALCORE_LOG) << incidence->typeStr() << " not found. uid=" << uid;
0234     }
0235     notifyIncidenceDeleted(incidence);
0236     return deleted;
0237 }
0238 
0239 bool MemoryCalendar::deleteIncidenceInstances(const Incidence::Ptr &incidence)
0240 {
0241     Incidence::List instances;
0242     for (auto it = d->mIncidences[incidence->type()].constFind(incidence->uid()), end = d->mIncidences[incidence->type()].constEnd();
0243          it != end && it.key() == incidence->uid();
0244          ++it) {
0245         if (it.value()->hasRecurrenceId()) {
0246             qCDebug(KCALCORE_LOG) << "deleting child"
0247                                   << ", type=" << int(incidence->type()) << ", uid="
0248                                   << incidence->uid()
0249                                   //                   << ", start=" << i->dtStart()
0250                                   << " from calendar";
0251             // Don't call deleteIncidence() now since it's modifying the
0252             // mIncidences map we're iterating over.
0253             instances.append(it.value());
0254         }
0255     }
0256     for (Incidence::Ptr instance : instances) {
0257         deleteIncidence(instance);
0258     }
0259 
0260     return true;
0261 }
0262 
0263 //@cond PRIVATE
0264 bool MemoryCalendar::Private::deleteIncidence(const QString &uid, IncidenceBase::IncidenceType type, const QDateTime &recurrenceId)
0265 {
0266     for (auto it = mIncidences[type].find(uid), end = mIncidences[type].end(); it != end && it.key() == uid; ++it) {
0267         Incidence::Ptr incidence = it.value();
0268         if (recurrenceId.isNull() && incidence->hasRecurrenceId()) {
0269             continue;
0270         } else if (!recurrenceId.isNull() && (!incidence->hasRecurrenceId() || recurrenceId != incidence->recurrenceId())) {
0271             continue;
0272         }
0273         mIncidences[type].erase(it);
0274         mIncidencesByIdentifier.remove(incidence->instanceIdentifier());
0275         const QDateTime dt = incidence->dateTime(Incidence::RoleCalendarHashing);
0276         if (dt.isValid()) {
0277             mIncidencesForDate[type].remove(dt.toTimeZone(q->timeZone()).date(), incidence);
0278         }
0279         return true;
0280     }
0281     return false;
0282 }
0283 
0284 void MemoryCalendar::Private::deleteAllIncidences(Incidence::IncidenceType incidenceType)
0285 {
0286     for (auto &incidence : mIncidences[incidenceType]) {
0287         q->notifyIncidenceAboutToBeDeleted(incidence);
0288         incidence->unRegisterObserver(q);
0289     }
0290     mIncidences[incidenceType].clear();
0291     mIncidencesForDate[incidenceType].clear();
0292 }
0293 
0294 Incidence::Ptr MemoryCalendar::Private::incidence(const QString &uid, Incidence::IncidenceType type, const QDateTime &recurrenceId) const
0295 {
0296     return findIncidence(mIncidences[type], uid, recurrenceId);
0297 }
0298 
0299 Incidence::Ptr MemoryCalendar::Private::deletedIncidence(const QString &uid, const QDateTime &recurrenceId, IncidenceBase::IncidenceType type) const
0300 {
0301     if (!q->deletionTracking()) {
0302         return Incidence::Ptr();
0303     }
0304 
0305     return findIncidence(mDeletedIncidences[type], uid, recurrenceId);
0306 }
0307 
0308 void MemoryCalendar::Private::insertIncidence(const Incidence::Ptr &incidence)
0309 {
0310     const QString uid = incidence->uid();
0311     const Incidence::IncidenceType type = incidence->type();
0312     if (!mIncidences[type].contains(uid, incidence)) {
0313         mIncidences[type].insert(uid, incidence);
0314         mIncidencesByIdentifier.insert(incidence->instanceIdentifier(), incidence);
0315         const QDateTime dt = incidence->dateTime(Incidence::RoleCalendarHashing);
0316         if (dt.isValid()) {
0317             mIncidencesForDate[type].insert(dt.toTimeZone(q->timeZone()).date(), incidence);
0318         }
0319 
0320     } else {
0321 #ifndef NDEBUG
0322         // if we already have an to-do with this UID, it must be the same incidence,
0323         // otherwise something's really broken
0324         Q_ASSERT(mIncidences[type].value(uid) == incidence);
0325 #endif
0326     }
0327 }
0328 //@endcond
0329 
0330 bool MemoryCalendar::addIncidence(const Incidence::Ptr &incidence)
0331 {
0332     d->insertIncidence(incidence);
0333 
0334     notifyIncidenceAdded(incidence);
0335 
0336     incidence->registerObserver(this);
0337 
0338     setupRelations(incidence);
0339 
0340     setModified(true);
0341 
0342     return true;
0343 }
0344 
0345 bool MemoryCalendar::addEvent(const Event::Ptr &event)
0346 {
0347     return addIncidence(event);
0348 }
0349 
0350 bool MemoryCalendar::deleteEvent(const Event::Ptr &event)
0351 {
0352     return deleteIncidence(event);
0353 }
0354 
0355 bool MemoryCalendar::deleteEventInstances(const Event::Ptr &event)
0356 {
0357     return deleteIncidenceInstances(event);
0358 }
0359 
0360 Event::Ptr MemoryCalendar::event(const QString &uid, const QDateTime &recurrenceId) const
0361 {
0362     return d->incidence(uid, Incidence::TypeEvent, recurrenceId).staticCast<Event>();
0363 }
0364 
0365 Event::Ptr MemoryCalendar::deletedEvent(const QString &uid, const QDateTime &recurrenceId) const
0366 {
0367     return d->deletedIncidence(uid, recurrenceId, Incidence::TypeEvent).staticCast<Event>();
0368 }
0369 
0370 bool MemoryCalendar::addTodo(const Todo::Ptr &todo)
0371 {
0372     return addIncidence(todo);
0373 }
0374 
0375 bool MemoryCalendar::deleteTodo(const Todo::Ptr &todo)
0376 {
0377     return deleteIncidence(todo);
0378 }
0379 
0380 bool MemoryCalendar::deleteTodoInstances(const Todo::Ptr &todo)
0381 {
0382     return deleteIncidenceInstances(todo);
0383 }
0384 
0385 Todo::Ptr MemoryCalendar::todo(const QString &uid, const QDateTime &recurrenceId) const
0386 {
0387     return d->incidence(uid, Incidence::TypeTodo, recurrenceId).staticCast<Todo>();
0388 }
0389 
0390 Todo::Ptr MemoryCalendar::deletedTodo(const QString &uid, const QDateTime &recurrenceId) const
0391 {
0392     return d->deletedIncidence(uid, recurrenceId, Incidence::TypeTodo).staticCast<Todo>();
0393 }
0394 
0395 Todo::List MemoryCalendar::rawTodos(TodoSortField sortField, SortDirection sortDirection) const
0396 {
0397     return Calendar::sortTodos(d->castIncidenceList<Todo>(d->mIncidences[Incidence::TypeTodo]), sortField, sortDirection);
0398 }
0399 
0400 Todo::List MemoryCalendar::deletedTodos(TodoSortField sortField, SortDirection sortDirection) const
0401 {
0402     if (!deletionTracking()) {
0403         return Todo::List();
0404     }
0405 
0406     return Calendar::sortTodos(d->castIncidenceList<Todo>(d->mDeletedIncidences[Incidence::TypeTodo]), sortField, sortDirection);
0407 }
0408 
0409 Todo::List MemoryCalendar::todoInstances(const Incidence::Ptr &todo, TodoSortField sortField, SortDirection sortDirection) const
0410 {
0411     return Calendar::sortTodos(d->incidenceInstances<Todo>(Incidence::TypeTodo, todo), sortField, sortDirection);
0412 }
0413 
0414 Todo::List MemoryCalendar::rawTodosForDate(const QDate &date) const
0415 {
0416     Todo::List todoList;
0417 
0418     d->forIncidences<Todo>(d->mIncidencesForDate[Incidence::TypeTodo], date, [&todoList](const Todo::Ptr &todo) {
0419         todoList.append(todo);
0420     });
0421 
0422     // Iterate over all todos. Look for recurring todoss that occur on this date
0423     d->forIncidences<Todo>(d->mIncidences[Incidence::TypeTodo], [this, &todoList, &date](const Todo::Ptr &todo) {
0424         if (todo->recurs() && todo->recursOn(date, timeZone())) {
0425             todoList.append(todo);
0426         }
0427     });
0428 
0429     return todoList;
0430 }
0431 
0432 Todo::List MemoryCalendar::rawTodos(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const
0433 {
0434     Q_UNUSED(inclusive); // use only exact dtDue/dtStart, not dtStart and dtEnd
0435 
0436     Todo::List todoList;
0437     const auto ts = timeZone.isValid() ? timeZone : this->timeZone();
0438     QDateTime st(start, QTime(0, 0, 0), ts);
0439     QDateTime nd(end, QTime(23, 59, 59, 999), ts);
0440 
0441     // Get todos
0442     for (const auto &incidence : d->mIncidences[Incidence::TypeTodo]) {
0443         const auto todo = incidence.staticCast<Todo>();
0444         if (!isVisible(todo)) {
0445             continue;
0446         }
0447 
0448         QDateTime rStart = todo->hasDueDate() ? todo->dtDue() : todo->hasStartDate() ? todo->dtStart() : QDateTime();
0449         if (!rStart.isValid()) {
0450             continue;
0451         }
0452 
0453         if (!todo->recurs()) { // non-recurring todos
0454             if (nd.isValid() && nd < rStart) {
0455                 continue;
0456             }
0457             if (st.isValid() && rStart < st) {
0458                 continue;
0459             }
0460         } else { // recurring events
0461             switch (todo->recurrence()->duration()) {
0462             case -1: // infinite
0463                 break;
0464             case 0: // end date given
0465             default: // count given
0466                 QDateTime rEnd(todo->recurrence()->endDate(), QTime(23, 59, 59, 999), ts);
0467                 if (!rEnd.isValid()) {
0468                     continue;
0469                 }
0470                 if (st.isValid() && rEnd < st) {
0471                     continue;
0472                 }
0473                 break;
0474             } // switch(duration)
0475         } // if(recurs)
0476 
0477         todoList.append(todo);
0478     }
0479 
0480     return todoList;
0481 }
0482 
0483 Alarm::List MemoryCalendar::alarmsTo(const QDateTime &to) const
0484 {
0485     return alarms(QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0)), to);
0486 }
0487 
0488 Alarm::List MemoryCalendar::alarms(const QDateTime &from, const QDateTime &to, bool excludeBlockedAlarms) const
0489 {
0490     Q_UNUSED(excludeBlockedAlarms);
0491     Alarm::List alarmList;
0492 
0493     d->forIncidences<Event>(d->mIncidences[Incidence::TypeEvent], [this, &alarmList, &from, &to](const Event::Ptr &e) {
0494         if (e->recurs()) {
0495             appendRecurringAlarms(alarmList, e, from, to);
0496         } else {
0497             appendAlarms(alarmList, e, from, to);
0498         }
0499     });
0500 
0501     d->forIncidences<Todo>(d->mIncidences[IncidenceBase::TypeTodo], [this, &alarmList, &from, &to](const Todo::Ptr &t) {
0502         if (!t->isCompleted()) {
0503             appendAlarms(alarmList, t, from, to);
0504             if (t->recurs()) {
0505                 appendRecurringAlarms(alarmList, t, from, to);
0506             } else {
0507                 appendAlarms(alarmList, t, from, to);
0508             }
0509         }
0510     });
0511 
0512     return alarmList;
0513 }
0514 
0515 bool MemoryCalendar::updateLastModifiedOnChange() const
0516 {
0517     return d->mUpdateLastModified;
0518 }
0519 
0520 void MemoryCalendar::setUpdateLastModifiedOnChange(bool update)
0521 {
0522     d->mUpdateLastModified = update;
0523 }
0524 
0525 void MemoryCalendar::incidenceUpdate(const QString &uid, const QDateTime &recurrenceId)
0526 {
0527     Incidence::Ptr inc = incidence(uid, recurrenceId);
0528 
0529     if (inc) {
0530         if (!d->mIncidenceBeingUpdated.isEmpty()) {
0531             qCWarning(KCALCORE_LOG) << "Incidence::update() called twice without an updated() call in between.";
0532         }
0533 
0534         // Save it so we can detect changes to uid or recurringId.
0535         d->mIncidenceBeingUpdated = inc->instanceIdentifier();
0536 
0537         const QDateTime dt = inc->dateTime(Incidence::RoleCalendarHashing);
0538         if (dt.isValid()) {
0539             d->mIncidencesForDate[inc->type()].remove(dt.toTimeZone(timeZone()).date(), inc);
0540         }
0541     }
0542 }
0543 
0544 void MemoryCalendar::incidenceUpdated(const QString &uid, const QDateTime &recurrenceId)
0545 {
0546     Incidence::Ptr inc = incidence(uid, recurrenceId);
0547 
0548     if (inc) {
0549         if (d->mIncidenceBeingUpdated.isEmpty()) {
0550             qCWarning(KCALCORE_LOG) << "Incidence::updated() called twice without an update() call in between.";
0551         } else if (inc->instanceIdentifier() != d->mIncidenceBeingUpdated) {
0552             // Instance identifier changed, update our hash table
0553             d->mIncidencesByIdentifier.remove(d->mIncidenceBeingUpdated);
0554             d->mIncidencesByIdentifier.insert(inc->instanceIdentifier(), inc);
0555         }
0556 
0557         d->mIncidenceBeingUpdated = QString();
0558 
0559         if (d->mUpdateLastModified) {
0560             inc->setLastModified(QDateTime::currentDateTimeUtc());
0561         }
0562         // we should probably update the revision number here,
0563         // or internally in the Event itself when certain things change.
0564         // need to verify with ical documentation.
0565 
0566         const QDateTime dt = inc->dateTime(Incidence::RoleCalendarHashing);
0567         if (dt.isValid()) {
0568             d->mIncidencesForDate[inc->type()].insert(dt.toTimeZone(timeZone()).date(), inc);
0569         }
0570 
0571         notifyIncidenceChanged(inc);
0572 
0573         setModified(true);
0574     }
0575 }
0576 
0577 Event::List MemoryCalendar::rawEventsForDate(const QDate &date, const QTimeZone &timeZone, EventSortField sortField, SortDirection sortDirection) const
0578 {
0579     Event::List eventList;
0580 
0581     if (!date.isValid()) {
0582         // There can't be events on invalid dates
0583         return eventList;
0584     }
0585 
0586     if (timeZone.isValid() && timeZone != this->timeZone()) {
0587         // We cannot use the hash table on date, since time zone is different.
0588         eventList = rawEvents(date, date, timeZone, false);
0589         return Calendar::sortEvents(std::move(eventList), sortField, sortDirection);
0590     }
0591 
0592     // Iterate over all non-recurring, single-day events that start on this date
0593     d->forIncidences<Event>(d->mIncidencesForDate[Incidence::TypeEvent], date, [&eventList](const Event::Ptr &event) {
0594         eventList.append(event);
0595     });
0596 
0597     // Iterate over all events. Look for recurring events that occur on this date
0598     const auto ts = timeZone.isValid() ? timeZone : this->timeZone();
0599     for (const auto &event : d->mIncidences[Incidence::TypeEvent]) {
0600         const auto ev = event.staticCast<Event>();
0601         if (ev->recurs()) {
0602             if (ev->isMultiDay()) {
0603                 int extraDays = ev->dtStart().date().daysTo(ev->dtEnd().date());
0604                 for (int i = 0; i <= extraDays; ++i) {
0605                     if (ev->recursOn(date.addDays(-i), ts)) {
0606                         eventList.append(ev);
0607                         break;
0608                     }
0609                 }
0610             } else {
0611                 if (ev->recursOn(date, ts)) {
0612                     eventList.append(ev);
0613                 }
0614             }
0615         } else {
0616             if (ev->isMultiDay()) {
0617                 if (ev->dtStart().toTimeZone(ts).date() <= date && ev->dtEnd().toTimeZone(ts).date() >= date) {
0618                     eventList.append(ev);
0619                 }
0620             }
0621         }
0622     }
0623 
0624     return Calendar::sortEvents(std::move(eventList), sortField, sortDirection);
0625 }
0626 
0627 Event::List MemoryCalendar::rawEvents(const QDate &start, const QDate &end, const QTimeZone &timeZone, bool inclusive) const
0628 {
0629     Event::List eventList;
0630     const auto ts = timeZone.isValid() ? timeZone : this->timeZone();
0631     QDateTime st(start, QTime(0, 0, 0), ts);
0632     QDateTime nd(end, QTime(23, 59, 59, 999), ts);
0633 
0634     // Get non-recurring events
0635     for (const auto &e : d->mIncidences[Incidence::TypeEvent]) {
0636         const auto event = e.staticCast<Event>();
0637         QDateTime rStart = event->dtStart();
0638         if (nd.isValid() && nd < rStart) {
0639             continue;
0640         }
0641         if (inclusive && st.isValid() && rStart < st) {
0642             continue;
0643         }
0644 
0645         if (!event->recurs()) { // non-recurring events
0646             QDateTime rEnd = event->dtEnd();
0647             if (st.isValid() && rEnd < st) {
0648                 continue;
0649             }
0650             if (inclusive && nd.isValid() && nd < rEnd) {
0651                 continue;
0652             }
0653         } else { // recurring events
0654             switch (event->recurrence()->duration()) {
0655             case -1: // infinite
0656                 if (inclusive) {
0657                     continue;
0658                 }
0659                 break;
0660             case 0: // end date given
0661             default: // count given
0662                 QDateTime rEnd(event->recurrence()->endDate(), QTime(23, 59, 59, 999), ts);
0663                 if (!rEnd.isValid()) {
0664                     continue;
0665                 }
0666                 if (st.isValid() && rEnd < st) {
0667                     continue;
0668                 }
0669                 if (inclusive && nd.isValid() && nd < rEnd) {
0670                     continue;
0671                 }
0672                 break;
0673             } // switch(duration)
0674         } // if(recurs)
0675 
0676         eventList.append(event);
0677     }
0678 
0679     return eventList;
0680 }
0681 
0682 #if KCALENDARCORE_BUILD_DEPRECATED_SINCE(5, 95)
0683 Event::List MemoryCalendar::rawEventsForDate(const QDateTime &kdt) const
0684 {
0685     return rawEventsForDate(kdt.date(), kdt.timeZone());
0686 }
0687 #endif
0688 
0689 Event::List MemoryCalendar::rawEvents(EventSortField sortField, SortDirection sortDirection) const
0690 {
0691     return Calendar::sortEvents(d->castIncidenceList<Event>(d->mIncidences[Incidence::TypeEvent]), sortField, sortDirection);
0692 }
0693 
0694 Event::List MemoryCalendar::deletedEvents(EventSortField sortField, SortDirection sortDirection) const
0695 {
0696     if (!deletionTracking()) {
0697         return Event::List();
0698     }
0699 
0700     return Calendar::sortEvents(d->castIncidenceList<Event>(d->mDeletedIncidences[Incidence::TypeEvent]), sortField, sortDirection);
0701 }
0702 
0703 Event::List MemoryCalendar::eventInstances(const Incidence::Ptr &event, EventSortField sortField, SortDirection sortDirection) const
0704 {
0705     return Calendar::sortEvents(d->incidenceInstances<Event>(Incidence::TypeEvent, event), sortField, sortDirection);
0706 }
0707 
0708 bool MemoryCalendar::addJournal(const Journal::Ptr &journal)
0709 {
0710     return addIncidence(journal);
0711 }
0712 
0713 bool MemoryCalendar::deleteJournal(const Journal::Ptr &journal)
0714 {
0715     return deleteIncidence(journal);
0716 }
0717 
0718 bool MemoryCalendar::deleteJournalInstances(const Journal::Ptr &journal)
0719 {
0720     return deleteIncidenceInstances(journal);
0721 }
0722 
0723 Journal::Ptr MemoryCalendar::journal(const QString &uid, const QDateTime &recurrenceId) const
0724 {
0725     return d->incidence(uid, Incidence::TypeJournal, recurrenceId).staticCast<Journal>();
0726 }
0727 
0728 Journal::Ptr MemoryCalendar::deletedJournal(const QString &uid, const QDateTime &recurrenceId) const
0729 {
0730     return d->deletedIncidence(uid, recurrenceId, Incidence::TypeJournal).staticCast<Journal>();
0731 }
0732 
0733 Journal::List MemoryCalendar::rawJournals(JournalSortField sortField, SortDirection sortDirection) const
0734 {
0735     return Calendar::sortJournals(d->castIncidenceList<Journal>(d->mIncidences[Incidence::TypeJournal]), sortField, sortDirection);
0736 }
0737 
0738 Journal::List MemoryCalendar::deletedJournals(JournalSortField sortField, SortDirection sortDirection) const
0739 {
0740     if (!deletionTracking()) {
0741         return Journal::List();
0742     }
0743 
0744     return Calendar::sortJournals(d->castIncidenceList<Journal>(d->mDeletedIncidences[Incidence::TypeJournal]), sortField, sortDirection);
0745 }
0746 
0747 Journal::List MemoryCalendar::journalInstances(const Incidence::Ptr &journal, JournalSortField sortField, SortDirection sortDirection) const
0748 {
0749     return Calendar::sortJournals(d->incidenceInstances<Journal>(Incidence::TypeJournal, journal), sortField, sortDirection);
0750 }
0751 
0752 Journal::List MemoryCalendar::rawJournalsForDate(const QDate &date) const
0753 {
0754     Journal::List journalList;
0755 
0756     d->forIncidences<Journal>(d->mIncidencesForDate[Incidence::TypeJournal], date, [&journalList](const Journal::Ptr &journal) {
0757         journalList.append(journal);
0758     });
0759 
0760     return journalList;
0761 }
0762 
0763 Incidence::Ptr MemoryCalendar::instance(const QString &identifier) const
0764 {
0765     return d->mIncidencesByIdentifier.value(identifier);
0766 }
0767 
0768 void MemoryCalendar::virtual_hook(int id, void *data)
0769 {
0770     Q_UNUSED(id);
0771     Q_UNUSED(data);
0772     Q_ASSERT(false);
0773 }
0774 
0775 #include "moc_memorycalendar.cpp"