From 6a9ff4ac53f7c60902b851f4635ee4bf6ff2d024 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Mon, 16 Apr 2018 10:09:30 +0200 Subject: Rename EventTreeModel -> PeriodDayEventModel + doc the model architecture --- framework/src/CMakeLists.txt | 2 +- framework/src/domain/eventtreemodel.cpp | 257 --------------------------- framework/src/domain/eventtreemodel.h | 110 ------------ framework/src/domain/perioddayeventmodel.cpp | 257 +++++++++++++++++++++++++++ framework/src/domain/perioddayeventmodel.h | 129 ++++++++++++++ framework/src/frameworkplugin.cpp | 4 +- views/calendar/qml/WeekEvents.qml | 2 +- 7 files changed, 390 insertions(+), 371 deletions(-) delete mode 100644 framework/src/domain/eventtreemodel.cpp delete mode 100644 framework/src/domain/eventtreemodel.h create mode 100644 framework/src/domain/perioddayeventmodel.cpp create mode 100644 framework/src/domain/perioddayeventmodel.h diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt index a4392b33..a3c489b4 100644 --- a/framework/src/CMakeLists.txt +++ b/framework/src/CMakeLists.txt @@ -17,7 +17,7 @@ add_library(kubeframework SHARED settings/settings.cpp domain/maillistmodel.cpp domain/folderlistmodel.cpp - domain/eventtreemodel.cpp + domain/perioddayeventmodel.cpp domain/composercontroller.cpp domain/modeltest.cpp domain/retriever.cpp diff --git a/framework/src/domain/eventtreemodel.cpp b/framework/src/domain/eventtreemodel.cpp deleted file mode 100644 index 4e25cffe..00000000 --- a/framework/src/domain/eventtreemodel.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - 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 "eventtreemodel.h" - -#include -#include -#include - -#include -#include -#include - -EventTreeModel::EventTreeModel(QObject *parent) : QAbstractItemModel(parent), partitionedEvents(7) -{ - Sink::Query query; - query.setFlags(Sink::Query::LiveQuery); - query.request(); - query.request(); - query.request(); - query.request(); - - eventModel = Sink::Store::loadModel(query); - - QObject::connect(eventModel.data(), &QAbstractItemModel::dataChanged, this, &EventTreeModel::partitionData); - QObject::connect(eventModel.data(), &QAbstractItemModel::layoutChanged, this, &EventTreeModel::partitionData); - QObject::connect(eventModel.data(), &QAbstractItemModel::modelReset, this, &EventTreeModel::partitionData); - QObject::connect(eventModel.data(), &QAbstractItemModel::rowsInserted, this, &EventTreeModel::partitionData); - QObject::connect(eventModel.data(), &QAbstractItemModel::rowsMoved, this, &EventTreeModel::partitionData); - QObject::connect(eventModel.data(), &QAbstractItemModel::rowsRemoved, this, &EventTreeModel::partitionData); - - partitionData(); -} - -void EventTreeModel::partitionData() -{ - SinkLog() << "Partitioning event data"; - - beginResetModel(); - - partitionedEvents = QVector>>(mViewLength); - - for (int i = 0; i < eventModel->rowCount(); ++i) { - auto event = eventModel->index(i, 0).data(Sink::Store::DomainObjectRole).value(); - QDate eventDate = event->getStartTime().date(); - - if(!eventDate.isValid()) { - SinkWarning() << "Invalid date in the eventModel, ignoring..."; - continue; - } - - int bucket = bucketOf(eventDate); - - if(bucket >= 0) { - SinkTrace() << "Adding event:" << event->getSummary() << "in bucket #" << bucket; - partitionedEvents[bucket].append(event); - } - } - - endResetModel(); -} - -int EventTreeModel::bucketOf(QDate const &candidate) const -{ - int bucket = mViewStart.daysTo(candidate); - if(bucket >= mViewLength || bucket < 0) { - return -1; - } - - return bucket; -} - -QModelIndex EventTreeModel::index(int row, int column, const QModelIndex &parent) const -{ - if (!hasIndex(row, column, parent)) { - return {}; - } - - if (!parent.isValid()) { - // Asking for a day - - if (!(0 <= row && row < mViewLength)) { - return {}; - } - - return createIndex(row, column, DAY_ID); - } - - // Asking for an Event - auto day = static_cast(parent.row()); - - Q_ASSERT(0 <= day && day <= mViewLength); - if (row >= partitionedEvents[day].size()) { - return {}; - } - - return createIndex(row, column, day); -} - -QModelIndex EventTreeModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) { - return {}; - } - - if (index.internalId() == DAY_ID) { - return {}; - } - - auto day = index.internalId(); - - return this->index(day, 0); -} - -int EventTreeModel::rowCount(const QModelIndex &parent) const -{ - if (!parent.isValid()) { - return mViewLength; - } - - auto day = parent.row(); - - return partitionedEvents[day].size(); -} - -int EventTreeModel::columnCount(const QModelIndex &parent) const -{ - if (!parent.isValid()) { - return 1; - } - - return eventModel->columnCount(); -} - -QVariant EventTreeModel::data(const QModelIndex &id, int role) const -{ - if (id.internalId() == DAY_ID) { - auto day = id.row(); - - SinkTrace() << "Fetching data for day" << day << "with role" - << QMetaEnum::fromType().valueToKey(role); - - switch (role) { - case Qt::DisplayRole: - return mViewStart.addDays(day).toString(); - case Events: { - auto result = QVariantList{}; - - for (int i = 0; i < partitionedEvents[day].size(); ++i) { - auto eventId = index(i, 0, id); - SinkTrace() << "Appending event:" << data(eventId, Summary); - - auto startTime = data(eventId, StartTime).toDateTime().time(); - - result.append(QVariantMap{ - {"text", data(eventId, Summary)}, - {"description", data(eventId, Description)}, - {"starts", startTime.hour() + startTime.minute() / 60.}, - {"duration", data(eventId, Duration)}, - {"color", "#134bab"}, - {"indention", 0}, - }); - } - - return result; - } - default: - SinkWarning() << "Unknown role for day:" << QMetaEnum::fromType().valueToKey(role); - return {}; - } - } else { - auto day = id.internalId(); - SinkTrace() << "Fetching data for event on day" << day << "with role" - << QMetaEnum::fromType().valueToKey(role); - auto event = partitionedEvents[day].at(id.row()); - - switch (role) { - case Summary: - return event->getSummary(); - case Description: - return event->getDescription(); - case StartTime: - return event->getStartTime(); - case Duration: { - auto start = event->getStartTime(); - auto end = event->getEndTime(); - return start.secsTo(end) / 3600; - } - default: - SinkWarning() << "Unknown role for event:" << QMetaEnum::fromType().valueToKey(role); - return {}; - } - } -} - -QHash EventTreeModel::roleNames() const -{ - QHash roles; - - roles[Events] = "events"; - roles[Summary] = "summary"; - roles[Description] = "description"; - roles[StartTime] = "starts"; - roles[Duration] = "duration"; - - return roles; -} - -QDate EventTreeModel::viewStart() const -{ - return mViewStart; -} - -void EventTreeModel::setViewStart(QDate start) -{ - if (!start.isValid()) { - SinkWarning() << "Passed an invalid starting date in setViewStart, ignoring..."; - return; - } - - mViewStart = std::move(start); - partitionData(); -} - -void EventTreeModel::setViewStart(QVariant start) -{ - setViewStart(start.toDate()); -} - -int EventTreeModel::viewLength() const -{ - return mViewLength; -} - -void EventTreeModel::setViewLength(int length) -{ - mViewLength = std::move(length); - partitionData(); -} diff --git a/framework/src/domain/eventtreemodel.h b/framework/src/domain/eventtreemodel.h deleted file mode 100644 index 516410ee..00000000 --- a/framework/src/domain/eventtreemodel.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - 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 - -#include -#include -#include -#include - -#include - -// Implementation notes -// ==================== -// -// On the model side -// ----------------- -// -// Columns are never used. -// -// Top-level items just contains the ".events" attribute, and their rows -// correspond to their offset compared to the start of the view (in number of -// days). In that case the internalId contains DAY_ID. -// -// Direct children are events, and their rows corresponds to their index in -// their partition. In that case no internalId / internalPointer is used. -// -// Internally: -// ----------- -// -// On construction and on dataChanged, all events are processed and partitioned -// in partitionedEvents: -// -// QVector< QList > -// ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// | | -// | '--- List of event pointers for that day -// '--- Partition / day -// -class EventTreeModel : public QAbstractItemModel -{ - Q_OBJECT - - Q_PROPERTY(QVariant viewStart READ viewStart WRITE setViewStart) - Q_PROPERTY(int viewLength READ viewLength WRITE setViewLength) - -public: - using Event = Sink::ApplicationDomain::Event; - - enum Roles - { - Events = Qt::UserRole + 1, - Summary, - Description, - StartTime, - Duration, - }; - Q_ENUM(Roles); - EventTreeModel(QObject *parent = nullptr); - ~EventTreeModel() = 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; - - QHash roleNames() const override; - - QDate viewStart() const; - void setViewStart(QDate); - void setViewStart(QVariant); - int viewLength() const; - void setViewLength(int); - -private: - void partitionData(); - - int bucketOf(QDate const &candidate) const; - - QDate mViewStart; - int mViewLength = 7; - - QSharedPointer eventModel; - QVector>> partitionedEvents; - - static const constexpr quintptr DAY_ID = std::numeric_limits::max(); -}; diff --git a/framework/src/domain/perioddayeventmodel.cpp b/framework/src/domain/perioddayeventmodel.cpp new file mode 100644 index 00000000..0b374d5c --- /dev/null +++ b/framework/src/domain/perioddayeventmodel.cpp @@ -0,0 +1,257 @@ +/* + 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 "perioddayeventmodel.h" + +#include +#include +#include + +#include +#include +#include + +PeriodDayEventModel::PeriodDayEventModel(QObject *parent) : QAbstractItemModel(parent), partitionedEvents(7) +{ + Sink::Query query; + query.setFlags(Sink::Query::LiveQuery); + query.request(); + query.request(); + query.request(); + query.request(); + + eventModel = Sink::Store::loadModel(query); + + QObject::connect(eventModel.data(), &QAbstractItemModel::dataChanged, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::layoutChanged, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::modelReset, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::rowsInserted, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::rowsMoved, this, &PeriodDayEventModel::partitionData); + QObject::connect(eventModel.data(), &QAbstractItemModel::rowsRemoved, this, &PeriodDayEventModel::partitionData); + + partitionData(); +} + +void PeriodDayEventModel::partitionData() +{ + SinkLog() << "Partitioning event data"; + + beginResetModel(); + + partitionedEvents = QVector>>(mViewLength); + + for (int i = 0; i < eventModel->rowCount(); ++i) { + auto event = eventModel->index(i, 0).data(Sink::Store::DomainObjectRole).value(); + QDate eventDate = event->getStartTime().date(); + + if(!eventDate.isValid()) { + SinkWarning() << "Invalid date in the eventModel, ignoring..."; + continue; + } + + int bucket = bucketOf(eventDate); + + if(bucket >= 0) { + SinkTrace() << "Adding event:" << event->getSummary() << "in bucket #" << bucket; + partitionedEvents[bucket].append(event); + } + } + + endResetModel(); +} + +int PeriodDayEventModel::bucketOf(QDate const &candidate) const +{ + int bucket = mViewStart.daysTo(candidate); + if(bucket >= mViewLength || bucket < 0) { + return -1; + } + + return bucket; +} + +QModelIndex PeriodDayEventModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) { + return {}; + } + + if (!parent.isValid()) { + // Asking for a day + + if (!(0 <= row && row < mViewLength)) { + return {}; + } + + return createIndex(row, column, DAY_ID); + } + + // Asking for an Event + auto day = static_cast(parent.row()); + + Q_ASSERT(0 <= day && day <= mViewLength); + if (row >= partitionedEvents[day].size()) { + return {}; + } + + return createIndex(row, column, day); +} + +QModelIndex PeriodDayEventModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) { + return {}; + } + + if (index.internalId() == DAY_ID) { + return {}; + } + + auto day = index.internalId(); + + return this->index(day, 0); +} + +int PeriodDayEventModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return mViewLength; + } + + auto day = parent.row(); + + return partitionedEvents[day].size(); +} + +int PeriodDayEventModel::columnCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return 1; + } + + return eventModel->columnCount(); +} + +QVariant PeriodDayEventModel::data(const QModelIndex &id, int role) const +{ + if (id.internalId() == DAY_ID) { + auto day = id.row(); + + SinkTrace() << "Fetching data for day" << day << "with role" + << QMetaEnum::fromType().valueToKey(role); + + switch (role) { + case Qt::DisplayRole: + return mViewStart.addDays(day).toString(); + case Events: { + auto result = QVariantList{}; + + for (int i = 0; i < partitionedEvents[day].size(); ++i) { + auto eventId = index(i, 0, id); + SinkTrace() << "Appending event:" << data(eventId, Summary); + + auto startTime = data(eventId, StartTime).toDateTime().time(); + + result.append(QVariantMap{ + {"text", data(eventId, Summary)}, + {"description", data(eventId, Description)}, + {"starts", startTime.hour() + startTime.minute() / 60.}, + {"duration", data(eventId, Duration)}, + {"color", "#134bab"}, + {"indention", 0}, + }); + } + + return result; + } + default: + SinkWarning() << "Unknown role for day:" << QMetaEnum::fromType().valueToKey(role); + return {}; + } + } else { + auto day = id.internalId(); + SinkTrace() << "Fetching data for event on day" << day << "with role" + << QMetaEnum::fromType().valueToKey(role); + auto event = partitionedEvents[day].at(id.row()); + + switch (role) { + case Summary: + return event->getSummary(); + case Description: + return event->getDescription(); + case StartTime: + return event->getStartTime(); + case Duration: { + auto start = event->getStartTime(); + auto end = event->getEndTime(); + return start.secsTo(end) / 3600; + } + default: + SinkWarning() << "Unknown role for event:" << QMetaEnum::fromType().valueToKey(role); + return {}; + } + } +} + +QHash PeriodDayEventModel::roleNames() const +{ + QHash roles; + + roles[Events] = "events"; + roles[Summary] = "summary"; + roles[Description] = "description"; + roles[StartTime] = "starts"; + roles[Duration] = "duration"; + + return roles; +} + +QDate PeriodDayEventModel::viewStart() const +{ + return mViewStart; +} + +void PeriodDayEventModel::setViewStart(QDate start) +{ + if (!start.isValid()) { + SinkWarning() << "Passed an invalid starting date in setViewStart, ignoring..."; + return; + } + + mViewStart = std::move(start); + partitionData(); +} + +void PeriodDayEventModel::setViewStart(QVariant start) +{ + setViewStart(start.toDate()); +} + +int PeriodDayEventModel::viewLength() const +{ + return mViewLength; +} + +void PeriodDayEventModel::setViewLength(int length) +{ + mViewLength = std::move(length); + partitionData(); +} diff --git a/framework/src/domain/perioddayeventmodel.h b/framework/src/domain/perioddayeventmodel.h new file mode 100644 index 00000000..cd26fe46 --- /dev/null +++ b/framework/src/domain/perioddayeventmodel.h @@ -0,0 +1,129 @@ +/* + 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 + +#include +#include +#include +#include + +#include + +// Facility used to get a restricted view into a Sink model comprised of +// events, partitioned according to the day the events take place. +// +// Model Format +// ============ +// +// Day 0 +// |--- Event 0 starting at `viewStart + 0d` +// |--- Event 1 starting at `viewStart + 0d` +// '--- Event 2 starting at `viewStart + 0d` +// Day 1 +// '--- Event 0 starting at `viewStart + 1d` +// Day 2 +// Day 3 +// |--- Event 0 starting at `viewStart + 3d` +// '--- Event 1 starting at `viewStart + 3d` +// Day 4 +// ⋮ +// +// Implementation notes +// ==================== +// +// On the model side +// ----------------- +// +// Columns are never used. +// +// Top-level items just contains the ".events" attribute, and their rows +// correspond to their offset compared to the start of the view (in number of +// days). In that case the internalId contains DAY_ID. +// +// Direct children are events, and their rows corresponds to their index in +// their partition. In that case no internalId / internalPointer is used. +// +// Internally: +// ----------- +// +// On construction and on dataChanged, all events are processed and partitioned +// in partitionedEvents: +// +// QVector< QList > +// ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// | | +// | '--- List of event pointers for that day +// '--- Partition / day +// +class PeriodDayEventModel : public QAbstractItemModel +{ + Q_OBJECT + + Q_PROPERTY(QVariant viewStart READ viewStart WRITE setViewStart) + Q_PROPERTY(int viewLength READ viewLength WRITE setViewLength) + +public: + using Event = Sink::ApplicationDomain::Event; + + enum Roles + { + Events = Qt::UserRole + 1, + Summary, + Description, + StartTime, + Duration, + }; + Q_ENUM(Roles); + PeriodDayEventModel(QObject *parent = nullptr); + ~PeriodDayEventModel() = 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; + + QHash roleNames() const override; + + QDate viewStart() const; + void setViewStart(QDate); + void setViewStart(QVariant); + int viewLength() const; + void setViewLength(int); + +private: + void partitionData(); + + int bucketOf(QDate const &candidate) const; + + QDate mViewStart; + int mViewLength = 7; + + QSharedPointer eventModel; + QVector>> partitionedEvents; + + static const constexpr quintptr DAY_ID = std::numeric_limits::max(); +}; diff --git a/framework/src/frameworkplugin.cpp b/framework/src/frameworkplugin.cpp index 289f502b..2eb53237 100644 --- a/framework/src/frameworkplugin.cpp +++ b/framework/src/frameworkplugin.cpp @@ -22,7 +22,7 @@ #include "domain/maillistmodel.h" #include "domain/folderlistmodel.h" -#include "domain/eventtreemodel.h" +#include "domain/perioddayeventmodel.h" #include "domain/composercontroller.h" #include "domain/mime/messageparser.h" #include "domain/retriever.h" @@ -121,7 +121,7 @@ void FrameworkPlugin::registerTypes (const char *uri) { qmlRegisterType(uri, 1, 0, "FolderListModel"); qmlRegisterType(uri, 1, 0, "MailListModel"); - qmlRegisterType(uri, 1, 0, "EventTreeModel"); + qmlRegisterType(uri, 1, 0, "PeriodDayEventModel"); qmlRegisterType(uri, 1, 0, "ComposerController"); qmlRegisterType(uri, 1, 0, "ControllerAction"); qmlRegisterType(uri, 1, 0, "MessageParser"); diff --git a/views/calendar/qml/WeekEvents.qml b/views/calendar/qml/WeekEvents.qml index 0b66eb99..2956f3ac 100644 --- a/views/calendar/qml/WeekEvents.qml +++ b/views/calendar/qml/WeekEvents.qml @@ -2,6 +2,6 @@ import QtQuick 2.7 import org.kube.framework 1.0 as Kube -Kube.EventTreeModel { +Kube.PeriodDayEventModel { viewStart: "2018-04-09" } -- cgit v1.2.3