diff options
Diffstat (limited to 'framework/src')
-rw-r--r-- | framework/src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | framework/src/domain/perioddayeventmodel.cpp | 256 | ||||
-rw-r--r-- | framework/src/domain/perioddayeventmodel.h | 129 | ||||
-rw-r--r-- | framework/src/frameworkplugin.cpp | 2 |
4 files changed, 389 insertions, 0 deletions
diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt index 0059bd00..a3c489b4 100644 --- a/framework/src/CMakeLists.txt +++ b/framework/src/CMakeLists.txt | |||
@@ -1,6 +1,7 @@ | |||
1 | 1 | ||
2 | find_package(Qt5 COMPONENTS REQUIRED Core Concurrent Quick Qml WebEngineWidgets Test WebEngine Gui) | 2 | find_package(Qt5 COMPONENTS REQUIRED Core Concurrent Quick Qml WebEngineWidgets Test WebEngine Gui) |
3 | find_package(KF5Mime 4.87.0 CONFIG REQUIRED) | 3 | find_package(KF5Mime 4.87.0 CONFIG REQUIRED) |
4 | find_package(KF5CalendarCore CONFIG REQUIRED) | ||
4 | find_package(Sink 0.6.0 CONFIG REQUIRED) | 5 | find_package(Sink 0.6.0 CONFIG REQUIRED) |
5 | find_package(KAsync CONFIG REQUIRED) | 6 | find_package(KAsync CONFIG REQUIRED) |
6 | find_package(QGpgme CONFIG REQUIRED) | 7 | find_package(QGpgme CONFIG REQUIRED) |
@@ -16,6 +17,7 @@ add_library(kubeframework SHARED | |||
16 | settings/settings.cpp | 17 | settings/settings.cpp |
17 | domain/maillistmodel.cpp | 18 | domain/maillistmodel.cpp |
18 | domain/folderlistmodel.cpp | 19 | domain/folderlistmodel.cpp |
20 | domain/perioddayeventmodel.cpp | ||
19 | domain/composercontroller.cpp | 21 | domain/composercontroller.cpp |
20 | domain/modeltest.cpp | 22 | domain/modeltest.cpp |
21 | domain/retriever.cpp | 23 | domain/retriever.cpp |
diff --git a/framework/src/domain/perioddayeventmodel.cpp b/framework/src/domain/perioddayeventmodel.cpp new file mode 100644 index 00000000..637e5584 --- /dev/null +++ b/framework/src/domain/perioddayeventmodel.cpp | |||
@@ -0,0 +1,256 @@ | |||
1 | /* | ||
2 | Copyright (c) 2018 Michael Bohlender <michael.bohlender@kdemail.net> | ||
3 | Copyright (c) 2018 Christian Mollekopf <mollekopf@kolabsys.com> | ||
4 | Copyright (c) 2018 Rémi Nicole <minijackson@riseup.net> | ||
5 | |||
6 | This library is free software; you can redistribute it and/or modify it | ||
7 | under the terms of the GNU Library General Public License as published by | ||
8 | the Free Software Foundation; either version 2 of the License, or (at your | ||
9 | option) any later version. | ||
10 | |||
11 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
14 | License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU Library General Public License | ||
17 | along with this library; see the file COPYING.LIB. If not, write to the | ||
18 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | 02110-1301, USA. | ||
20 | */ | ||
21 | |||
22 | #include "perioddayeventmodel.h" | ||
23 | |||
24 | #include <sink/log.h> | ||
25 | #include <sink/query.h> | ||
26 | #include <sink/store.h> | ||
27 | |||
28 | #include <QJsonArray> | ||
29 | #include <QJsonObject> | ||
30 | #include <QMetaEnum> | ||
31 | |||
32 | PeriodDayEventModel::PeriodDayEventModel(QObject *parent) | ||
33 | : QAbstractItemModel(parent), partitionedEvents(7) | ||
34 | { | ||
35 | Sink::Query query; | ||
36 | query.setFlags(Sink::Query::LiveQuery); | ||
37 | query.request<Event::Summary>(); | ||
38 | query.request<Event::Description>(); | ||
39 | query.request<Event::StartTime>(); | ||
40 | query.request<Event::EndTime>(); | ||
41 | |||
42 | eventModel = Sink::Store::loadModel<Event>(query); | ||
43 | |||
44 | QObject::connect(eventModel.data(), &QAbstractItemModel::dataChanged, this, &PeriodDayEventModel::partitionData); | ||
45 | QObject::connect(eventModel.data(), &QAbstractItemModel::layoutChanged, this, &PeriodDayEventModel::partitionData); | ||
46 | QObject::connect(eventModel.data(), &QAbstractItemModel::modelReset, this, &PeriodDayEventModel::partitionData); | ||
47 | QObject::connect(eventModel.data(), &QAbstractItemModel::rowsInserted, this, &PeriodDayEventModel::partitionData); | ||
48 | QObject::connect(eventModel.data(), &QAbstractItemModel::rowsMoved, this, &PeriodDayEventModel::partitionData); | ||
49 | QObject::connect(eventModel.data(), &QAbstractItemModel::rowsRemoved, this, &PeriodDayEventModel::partitionData); | ||
50 | |||
51 | partitionData(); | ||
52 | } | ||
53 | |||
54 | void PeriodDayEventModel::partitionData() | ||
55 | { | ||
56 | SinkLog() << "Partitioning event data"; | ||
57 | |||
58 | beginResetModel(); | ||
59 | |||
60 | partitionedEvents = QVector<QList<QSharedPointer<Event>>>(mPeriodLength); | ||
61 | |||
62 | for (int i = 0; i < eventModel->rowCount(); ++i) { | ||
63 | auto event = eventModel->index(i, 0).data(Sink::Store::DomainObjectRole).value<Event::Ptr>(); | ||
64 | QDate eventDate = event->getStartTime().date(); | ||
65 | |||
66 | if (!eventDate.isValid()) { | ||
67 | SinkWarning() << "Invalid date in the eventModel, ignoring..."; | ||
68 | continue; | ||
69 | } | ||
70 | |||
71 | int bucket = bucketOf(eventDate); | ||
72 | |||
73 | if (bucket >= 0) { | ||
74 | SinkTrace() << "Adding event:" << event->getSummary() << "in bucket #" << bucket; | ||
75 | partitionedEvents[bucket].append(event); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | endResetModel(); | ||
80 | } | ||
81 | |||
82 | int PeriodDayEventModel::bucketOf(const QDate &candidate) const | ||
83 | { | ||
84 | int bucket = mPeriodStart.daysTo(candidate); | ||
85 | if (bucket >= mPeriodLength || bucket < 0) { | ||
86 | return -1; | ||
87 | } | ||
88 | |||
89 | return bucket; | ||
90 | } | ||
91 | |||
92 | QModelIndex PeriodDayEventModel::index(int row, int column, const QModelIndex &parent) const | ||
93 | { | ||
94 | if (!hasIndex(row, column, parent)) { | ||
95 | return {}; | ||
96 | } | ||
97 | |||
98 | if (!parent.isValid()) { | ||
99 | // Asking for a day | ||
100 | |||
101 | if (!(0 <= row && row < mPeriodLength)) { | ||
102 | return {}; | ||
103 | } | ||
104 | |||
105 | return createIndex(row, column, DAY_ID); | ||
106 | } | ||
107 | |||
108 | // Asking for an Event | ||
109 | auto day = static_cast<int>(parent.row()); | ||
110 | |||
111 | Q_ASSERT(0 <= day && day <= mPeriodLength); | ||
112 | if (row >= partitionedEvents[day].size()) { | ||
113 | return {}; | ||
114 | } | ||
115 | |||
116 | return createIndex(row, column, day); | ||
117 | } | ||
118 | |||
119 | QModelIndex PeriodDayEventModel::parent(const QModelIndex &index) const | ||
120 | { | ||
121 | if (!index.isValid()) { | ||
122 | return {}; | ||
123 | } | ||
124 | |||
125 | if (index.internalId() == DAY_ID) { | ||
126 | return {}; | ||
127 | } | ||
128 | |||
129 | auto day = index.internalId(); | ||
130 | |||
131 | return this->index(day, 0); | ||
132 | } | ||
133 | |||
134 | int PeriodDayEventModel::rowCount(const QModelIndex &parent) const | ||
135 | { | ||
136 | if (!parent.isValid()) { | ||
137 | return mPeriodLength; | ||
138 | } | ||
139 | |||
140 | auto day = parent.row(); | ||
141 | |||
142 | return partitionedEvents[day].size(); | ||
143 | } | ||
144 | |||
145 | int PeriodDayEventModel::columnCount(const QModelIndex &parent) const | ||
146 | { | ||
147 | if (!parent.isValid()) { | ||
148 | return 1; | ||
149 | } | ||
150 | |||
151 | return eventModel->columnCount(); | ||
152 | } | ||
153 | |||
154 | QVariant PeriodDayEventModel::data(const QModelIndex &id, int role) const | ||
155 | { | ||
156 | if (id.internalId() == DAY_ID) { | ||
157 | auto day = id.row(); | ||
158 | |||
159 | SinkTrace() << "Fetching data for day" << day << "with role" | ||
160 | << QMetaEnum::fromType<Roles>().valueToKey(role); | ||
161 | |||
162 | switch (role) { | ||
163 | case Qt::DisplayRole: | ||
164 | return mPeriodStart.addDays(day).toString(); | ||
165 | case Events: { | ||
166 | auto result = QVariantList{}; | ||
167 | |||
168 | for (int i = 0; i < partitionedEvents[day].size(); ++i) { | ||
169 | auto eventId = index(i, 0, id); | ||
170 | SinkTrace() << "Appending event:" << data(eventId, Summary); | ||
171 | |||
172 | auto startTime = data(eventId, StartTime).toDateTime().time(); | ||
173 | |||
174 | result.append(QVariantMap{ | ||
175 | {"text", data(eventId, Summary)}, | ||
176 | {"description", data(eventId, Description)}, | ||
177 | {"starts", startTime.hour() + startTime.minute() / 60.}, | ||
178 | {"duration", data(eventId, Duration)}, | ||
179 | {"color", "#134bab"}, | ||
180 | {"indention", 0}, | ||
181 | }); | ||
182 | } | ||
183 | |||
184 | return result; | ||
185 | } | ||
186 | default: | ||
187 | SinkWarning() << "Unknown role for day:" << QMetaEnum::fromType<Roles>().valueToKey(role); | ||
188 | return {}; | ||
189 | } | ||
190 | } else { | ||
191 | auto day = id.internalId(); | ||
192 | SinkTrace() << "Fetching data for event on day" << day << "with role" | ||
193 | << QMetaEnum::fromType<Roles>().valueToKey(role); | ||
194 | auto event = partitionedEvents[day].at(id.row()); | ||
195 | |||
196 | switch (role) { | ||
197 | case Summary: | ||
198 | return event->getSummary(); | ||
199 | case Description: | ||
200 | return event->getDescription(); | ||
201 | case StartTime: | ||
202 | return event->getStartTime(); | ||
203 | case Duration: { | ||
204 | auto start = event->getStartTime(); | ||
205 | auto end = event->getEndTime(); | ||
206 | return start.secsTo(end) / 3600; | ||
207 | } | ||
208 | default: | ||
209 | SinkWarning() << "Unknown role for event:" << QMetaEnum::fromType<Roles>().valueToKey(role); | ||
210 | return {}; | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | |||
215 | QHash<int, QByteArray> PeriodDayEventModel::roleNames() const | ||
216 | { | ||
217 | return { | ||
218 | {Events, "events"}, | ||
219 | {Summary, "summary"}, | ||
220 | {Description, "description"}, | ||
221 | {StartTime, "starts"}, | ||
222 | {Duration, "duration"}, | ||
223 | }; | ||
224 | } | ||
225 | |||
226 | QDate PeriodDayEventModel::periodStart() const | ||
227 | { | ||
228 | return mPeriodStart; | ||
229 | } | ||
230 | |||
231 | void PeriodDayEventModel::setPeriodStart(const QDate &start) | ||
232 | { | ||
233 | if (!start.isValid()) { | ||
234 | SinkWarning() << "Passed an invalid starting date in setPeriodStart, ignoring..."; | ||
235 | return; | ||
236 | } | ||
237 | |||
238 | mPeriodStart = start; | ||
239 | partitionData(); | ||
240 | } | ||
241 | |||
242 | void PeriodDayEventModel::setPeriodStart(const QVariant &start) | ||
243 | { | ||
244 | setPeriodStart(start.toDate()); | ||
245 | } | ||
246 | |||
247 | int PeriodDayEventModel::periodLength() const | ||
248 | { | ||
249 | return mPeriodLength; | ||
250 | } | ||
251 | |||
252 | void PeriodDayEventModel::setPeriodLength(int length) | ||
253 | { | ||
254 | mPeriodLength = length; | ||
255 | partitionData(); | ||
256 | } | ||
diff --git a/framework/src/domain/perioddayeventmodel.h b/framework/src/domain/perioddayeventmodel.h new file mode 100644 index 00000000..e116d9a2 --- /dev/null +++ b/framework/src/domain/perioddayeventmodel.h | |||
@@ -0,0 +1,129 @@ | |||
1 | /* | ||
2 | Copyright (c) 2018 Michael Bohlender <michael.bohlender@kdemail.net> | ||
3 | Copyright (c) 2018 Christian Mollekopf <mollekopf@kolabsys.com> | ||
4 | Copyright (c) 2018 Rémi Nicole <minijackson@riseup.net> | ||
5 | |||
6 | This library is free software; you can redistribute it and/or modify it | ||
7 | under the terms of the GNU Library General Public License as published by | ||
8 | the Free Software Foundation; either version 2 of the License, or (at your | ||
9 | option) any later version. | ||
10 | |||
11 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
14 | License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU Library General Public License | ||
17 | along with this library; see the file COPYING.LIB. If not, write to the | ||
18 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | 02110-1301, USA. | ||
20 | */ | ||
21 | |||
22 | #pragma once | ||
23 | |||
24 | #include <sink/applicationdomaintype.h> | ||
25 | |||
26 | #include <QAbstractItemModel> | ||
27 | #include <QList> | ||
28 | #include <QSharedPointer> | ||
29 | #include <QVector> | ||
30 | |||
31 | #include <limits> | ||
32 | |||
33 | // Facility used to get a restricted period into a Sink model comprised of | ||
34 | // events, partitioned according to the day the events take place. | ||
35 | // | ||
36 | // Model Format | ||
37 | // ============ | ||
38 | // | ||
39 | // Day 0 | ||
40 | // |--- Event 0 starting at `periodStart + 0d` | ||
41 | // |--- Event 1 starting at `periodStart + 0d` | ||
42 | // '--- Event 2 starting at `periodStart + 0d` | ||
43 | // Day 1 | ||
44 | // '--- Event 0 starting at `periodStart + 1d` | ||
45 | // Day 2 | ||
46 | // Day 3 | ||
47 | // |--- Event 0 starting at `periodStart + 3d` | ||
48 | // '--- Event 1 starting at `periodStart + 3d` | ||
49 | // Day 4 | ||
50 | // ⋮ | ||
51 | // | ||
52 | // Implementation notes | ||
53 | // ==================== | ||
54 | // | ||
55 | // On the model side | ||
56 | // ----------------- | ||
57 | // | ||
58 | // Columns are never used. | ||
59 | // | ||
60 | // Top-level items just contains the ".events" attribute, and their rows | ||
61 | // correspond to their offset compared to the start of the period (in number of | ||
62 | // days). In that case the internalId contains DAY_ID. | ||
63 | // | ||
64 | // Direct children are events, and their rows corresponds to their index in | ||
65 | // their partition. In that case no internalId / internalPointer is used. | ||
66 | // | ||
67 | // Internally: | ||
68 | // ----------- | ||
69 | // | ||
70 | // On construction and on dataChanged, all events are processed and partitioned | ||
71 | // in partitionedEvents: | ||
72 | // | ||
73 | // QVector< QList<QSharedPointer<Event> > | ||
74 | // ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
75 | // | | | ||
76 | // | '--- List of event pointers for that day | ||
77 | // '--- Partition / day | ||
78 | // | ||
79 | class PeriodDayEventModel : public QAbstractItemModel | ||
80 | { | ||
81 | Q_OBJECT | ||
82 | |||
83 | Q_PROPERTY(QVariant start READ periodStart WRITE setPeriodStart) | ||
84 | Q_PROPERTY(int length READ periodLength WRITE setPeriodLength) | ||
85 | |||
86 | public: | ||
87 | using Event = Sink::ApplicationDomain::Event; | ||
88 | |||
89 | enum Roles | ||
90 | { | ||
91 | Events = Qt::UserRole + 1, | ||
92 | Summary, | ||
93 | Description, | ||
94 | StartTime, | ||
95 | Duration, | ||
96 | }; | ||
97 | Q_ENUM(Roles); | ||
98 | PeriodDayEventModel(QObject *parent = nullptr); | ||
99 | ~PeriodDayEventModel() = default; | ||
100 | |||
101 | QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override; | ||
102 | QModelIndex parent(const QModelIndex &index) const override; | ||
103 | |||
104 | int rowCount(const QModelIndex &parent) const override; | ||
105 | int columnCount(const QModelIndex &parent) const override; | ||
106 | |||
107 | QVariant data(const QModelIndex &index, int role) const override; | ||
108 | |||
109 | QHash<int, QByteArray> roleNames() const override; | ||
110 | |||
111 | QDate periodStart() const; | ||
112 | void setPeriodStart(const QDate &); | ||
113 | void setPeriodStart(const QVariant &); | ||
114 | int periodLength() const; | ||
115 | void setPeriodLength(int); | ||
116 | |||
117 | private: | ||
118 | void partitionData(); | ||
119 | |||
120 | int bucketOf(const QDate &candidate) const; | ||
121 | |||
122 | QDate mPeriodStart; | ||
123 | int mPeriodLength = 7; | ||
124 | |||
125 | QSharedPointer<QAbstractItemModel> eventModel; | ||
126 | QVector<QList<QSharedPointer<Event>>> partitionedEvents; | ||
127 | |||
128 | static const constexpr quintptr DAY_ID = std::numeric_limits<quintptr>::max(); | ||
129 | }; | ||
diff --git a/framework/src/frameworkplugin.cpp b/framework/src/frameworkplugin.cpp index d512ce10..2eb53237 100644 --- a/framework/src/frameworkplugin.cpp +++ b/framework/src/frameworkplugin.cpp | |||
@@ -22,6 +22,7 @@ | |||
22 | 22 | ||
23 | #include "domain/maillistmodel.h" | 23 | #include "domain/maillistmodel.h" |
24 | #include "domain/folderlistmodel.h" | 24 | #include "domain/folderlistmodel.h" |
25 | #include "domain/perioddayeventmodel.h" | ||
25 | #include "domain/composercontroller.h" | 26 | #include "domain/composercontroller.h" |
26 | #include "domain/mime/messageparser.h" | 27 | #include "domain/mime/messageparser.h" |
27 | #include "domain/retriever.h" | 28 | #include "domain/retriever.h" |
@@ -120,6 +121,7 @@ void FrameworkPlugin::registerTypes (const char *uri) | |||
120 | { | 121 | { |
121 | qmlRegisterType<FolderListModel>(uri, 1, 0, "FolderListModel"); | 122 | qmlRegisterType<FolderListModel>(uri, 1, 0, "FolderListModel"); |
122 | qmlRegisterType<MailListModel>(uri, 1, 0, "MailListModel"); | 123 | qmlRegisterType<MailListModel>(uri, 1, 0, "MailListModel"); |
124 | qmlRegisterType<PeriodDayEventModel>(uri, 1, 0, "PeriodDayEventModel"); | ||
123 | qmlRegisterType<ComposerController>(uri, 1, 0, "ComposerController"); | 125 | qmlRegisterType<ComposerController>(uri, 1, 0, "ComposerController"); |
124 | qmlRegisterType<Kube::ControllerAction>(uri, 1, 0, "ControllerAction"); | 126 | qmlRegisterType<Kube::ControllerAction>(uri, 1, 0, "ControllerAction"); |
125 | qmlRegisterType<MessageParser>(uri, 1, 0, "MessageParser"); | 127 | qmlRegisterType<MessageParser>(uri, 1, 0, "MessageParser"); |