diff --git a/framework/src/domain/eventoccurrencemodel.cpp b/framework/src/domain/eventoccurrencemodel.cpp index 98f6c5b8..0251337f 100644 --- a/framework/src/domain/eventoccurrencemodel.cpp +++ b/framework/src/domain/eventoccurrencemodel.cpp @@ -1,264 +1,267 @@ /* Copyright (c) 2018 Michael Bohlender Copyright (c) 2018 Christian Mollekopf Copyright (c) 2018 Rémi Nicole This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "eventoccurrencemodel.h" #include #include #include #include #include #include #include #include #include using namespace Sink; EventOccurrenceModel::EventOccurrenceModel(QObject *parent) : QAbstractItemModel(parent), mCalendarCache{EntityCache::Ptr::create(QByteArrayList{{ApplicationDomain::Calendar::Color::name}})} { mRefreshTimer.setSingleShot(true); QObject::connect(&mRefreshTimer, &QTimer::timeout, this, &EventOccurrenceModel::updateFromSource); } void EventOccurrenceModel::setStart(const QDate &start) { if (start != mStart) { mStart = start; updateQuery(); } } QDate EventOccurrenceModel::start() const { return mStart; } void EventOccurrenceModel::setLength(int length) { mLength = length; updateQuery(); } int EventOccurrenceModel::length() const { return mLength; } -void EventOccurrenceModel::setCalendarFilter(const QSet &calendarFilter) +void EventOccurrenceModel::setCalendarFilter(const QList &calendarFilter) { - mCalendarFilter = calendarFilter; + mCalendarFilter.clear(); + for (const auto &id : calendarFilter) { + mCalendarFilter << id.toLatin1(); + } updateQuery(); } void EventOccurrenceModel::setFilter(const QVariantMap &filter) { mFilter = filter; updateQuery(); } void EventOccurrenceModel::updateQuery() { using namespace Sink::ApplicationDomain; if (mCalendarFilter.isEmpty() || !mLength || !mStart.isValid()) { refreshView(); return; } mEnd = mStart.addDays(mLength); Sink::Query query; query.setFlags(Sink::Query::LiveQuery); query.request(); query.request(); query.request(); query.request(); query.request(); query.request(); query.request(); query.filter(Sink::Query::Comparator(QVariantList{mStart, mEnd}, Sink::Query::Comparator::Overlap)); mSourceModel = Store::loadModel(query); QObject::connect(mSourceModel.data(), &QAbstractItemModel::dataChanged, this, &EventOccurrenceModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::layoutChanged, this, &EventOccurrenceModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::modelReset, this, &EventOccurrenceModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::rowsInserted, this, &EventOccurrenceModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::rowsMoved, this, &EventOccurrenceModel::refreshView); QObject::connect(mSourceModel.data(), &QAbstractItemModel::rowsRemoved, this, &EventOccurrenceModel::refreshView); refreshView(); } void EventOccurrenceModel::refreshView() { if (!mRefreshTimer.isActive()) { //Instant update, but then only refresh every 50ms max. updateFromSource(); mRefreshTimer.start(50); } } void EventOccurrenceModel::updateFromSource() { beginResetModel(); mEvents.clear(); if (mSourceModel) { QMap recurringEvents; QMultiMap exceptions; QMap> events; for (int i = 0; i < mSourceModel->rowCount(); ++i) { auto event = mSourceModel->index(i, 0).data(Sink::Store::DomainObjectRole).value(); const bool skip = [&] { if (!mCalendarFilter.contains(event->getCalendar())) { return true; } for (auto it = mFilter.constBegin(); it!= mFilter.constEnd(); it++) { if (event->getProperty(it.key().toLatin1()) != it.value()) { return true; } } return false; }(); if (skip) { continue; } //Parse the event auto icalEvent = KCalCore::ICalFormat().readIncidence(event->getIcal()).dynamicCast(); if(!icalEvent) { SinkWarning() << "Invalid ICal to process, ignoring..."; continue; } //Collect recurring events and add the rest immediately if (icalEvent->recurs()) { recurringEvents.insert(icalEvent->uid().toLatin1(), icalEvent); events.insert(icalEvent->instanceIdentifier().toLatin1(), event); } else if(icalEvent->recurrenceId().isValid()) { exceptions.insert(icalEvent->uid().toLatin1(), icalEvent); events.insert(icalEvent->instanceIdentifier().toLatin1(), event); } else { if (icalEvent->dtStart().date() < mEnd && icalEvent->dtEnd().date() >= mStart) { mEvents.append({icalEvent->dtStart(), icalEvent->dtEnd(), icalEvent, getColor(event->getCalendar()), event->getAllDay(), event}); } } } //process all recurring events and their exceptions. for (const auto &uid : recurringEvents.keys()) { KCalCore::MemoryCalendar calendar{QTimeZone::systemTimeZone()}; calendar.addIncidence(recurringEvents.value(uid)); for (const auto &event : exceptions.values(uid)) { calendar.addIncidence(event); } KCalCore::OccurrenceIterator occurrenceIterator{calendar, QDateTime{mStart, {0, 0, 0}}, QDateTime{mEnd, {12, 59, 59}}}; while (occurrenceIterator.hasNext()) { occurrenceIterator.next(); const auto incidence = occurrenceIterator.incidence(); const auto event = events.value(incidence->instanceIdentifier().toLatin1()); const auto start = occurrenceIterator.occurrenceStartDate(); const auto end = incidence->endDateForStart(start); if (start.date() < mEnd && end.date() >= mStart) { mEvents.append({start, end, incidence, getColor(event->getCalendar()), event->getAllDay(), event}); } } } } endResetModel(); } QModelIndex EventOccurrenceModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return {}; } if (!parent.isValid()) { return createIndex(row, column); } return {}; } QModelIndex EventOccurrenceModel::parent(const QModelIndex &) const { return {}; } int EventOccurrenceModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { return mEvents.size(); } return 0; } int EventOccurrenceModel::columnCount(const QModelIndex &) const { return 1; } QByteArray EventOccurrenceModel::getColor(const QByteArray &calendar) const { const auto color = mCalendarCache->getProperty(calendar, "color").toByteArray(); if (color.isEmpty()) { qWarning() << "Failed to get color for calendar " << calendar; } return color; } QVariant EventOccurrenceModel::data(const QModelIndex &idx, int role) const { if (!hasIndex(idx.row(), idx.column())) { return {}; } auto event = mEvents.at(idx.row()); auto icalEvent = event.incidence; switch (role) { case Summary: return icalEvent->summary(); case Description: return icalEvent->description(); case StartTime: return event.start; case EndTime: return event.end; case Color: return event.color; case AllDay: return event.allDay; case Event: return QVariant::fromValue(event.domainObject); case EventOccurrence: return QVariant::fromValue(event); default: SinkWarning() << "Unknown role for event:" << QMetaEnum::fromType().valueToKey(role); return {}; } } diff --git a/framework/src/domain/eventoccurrencemodel.h b/framework/src/domain/eventoccurrencemodel.h index b7de31c6..5b0dc7c5 100644 --- a/framework/src/domain/eventoccurrencemodel.h +++ b/framework/src/domain/eventoccurrencemodel.h @@ -1,119 +1,119 @@ /* Copyright (c) 2018 Michael Bohlender Copyright (c) 2018 Christian Mollekopf Copyright (c) 2018 Rémi Nicole This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once #include "kube_export.h" #include #include #include #include #include #include namespace KCalCore { class MemoryCalendar; class Incidence; } namespace Sink { namespace ApplicationDomain { struct Event; } } class EntityCacheInterface; /** * Loads all event occurrences within the given period and matching the given filter. * * Recurrences are expanded */ class KUBE_EXPORT EventOccurrenceModel : public QAbstractItemModel { Q_OBJECT Q_PROPERTY(QDate start READ start WRITE setStart) Q_PROPERTY(int length READ length WRITE setLength) - Q_PROPERTY(QSet calendarFilter WRITE setCalendarFilter) + Q_PROPERTY(QList calendarFilter WRITE setCalendarFilter) Q_PROPERTY(QVariantMap filter WRITE setFilter) public: enum Roles { Summary = Qt::UserRole + 1, Description, StartTime, EndTime, Color, AllDay, Event, EventOccurrence, LastRole }; Q_ENUM(Roles); EventOccurrenceModel(QObject *parent = nullptr); ~EventOccurrenceModel() = default; QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; QModelIndex parent(const QModelIndex &index) const override; int rowCount(const QModelIndex &parent = {}) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; void updateQuery(const QDate &start, const QDate &end, const QSet &calendarFilter); void setStart(const QDate &); QDate start() const; void setLength(int); int length() const; - void setCalendarFilter(const QSet &); + void setCalendarFilter(const QList &); void setFilter(const QVariantMap &); struct Occurrence { QDateTime start; QDateTime end; QSharedPointer incidence; QByteArray color; bool allDay; QSharedPointer domainObject; }; private: void updateQuery(); void refreshView(); void updateFromSource(); QByteArray getColor(const QByteArray &calendar) const; QSharedPointer mSourceModel; QSet mCalendarFilter; QDate mStart; QDate mEnd; int mLength{0}; QSharedPointer mCalendarCache; QTimer mRefreshTimer; QList mEvents; QVariantMap mFilter; }; Q_DECLARE_METATYPE(EventOccurrenceModel::Occurrence);