File indexing completed on 2024-04-28 11:34:10
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"