summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--framework/src/domain/eventtreemodel.cpp231
-rw-r--r--framework/src/domain/eventtreemodel.h98
2 files changed, 329 insertions, 0 deletions
diff --git a/framework/src/domain/eventtreemodel.cpp b/framework/src/domain/eventtreemodel.cpp
new file mode 100644
index 00000000..2207e4e4
--- /dev/null
+++ b/framework/src/domain/eventtreemodel.cpp
@@ -0,0 +1,231 @@
1/*
2 Copyright (c) 2016 Michael Bohlender <michael.bohlender@kdemail.net>
3 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com>
4
5 This library is free software; you can redistribute it and/or modify it
6 under the terms of the GNU Library General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or (at your
8 option) any later version.
9
10 This library is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301, USA.
19*/
20
21#include "eventtreemodel.h"
22
23#include <sink/log.h>
24#include <sink/query.h>
25#include <sink/store.h>
26
27#include <QJsonArray>
28#include <QJsonObject>
29#include <QMetaEnum>
30
31EventTreeModel::EventTreeModel(QObject *parent) : QAbstractItemModel(parent), partitionedEvents(7)
32{
33 Sink::Query query;
34 query.setFlags(Sink::Query::LiveQuery);
35 query.request<Event::Summary>();
36 query.request<Event::Description>();
37 query.request<Event::StartTime>();
38 query.request<Event::EndTime>();
39
40 eventModel = Sink::Store::loadModel<Event>(query);
41
42 QObject::connect(eventModel.data(), &QAbstractItemModel::dataChanged, this, &EventTreeModel::partitionData);
43 QObject::connect(eventModel.data(), &QAbstractItemModel::layoutChanged, this, &EventTreeModel::partitionData);
44 QObject::connect(eventModel.data(), &QAbstractItemModel::rowsInserted, this, &EventTreeModel::partitionData);
45 QObject::connect(eventModel.data(), &QAbstractItemModel::rowsMoved, this, &EventTreeModel::partitionData);
46 QObject::connect(eventModel.data(), &QAbstractItemModel::rowsRemoved, this, &EventTreeModel::partitionData);
47
48 partitionData();
49}
50
51void EventTreeModel::partitionData()
52{
53 partitioningMutex.lock();
54
55 SinkLog() << "Partitioning event data";
56
57 beginResetModel();
58
59 for (int i = 0; i < 7; ++i) {
60 partitionedEvents[i].clear();
61 }
62
63 for (int i = 0; i < eventModel->rowCount(); ++i) {
64 auto event = eventModel->index(i, 0).data(Sink::Store::DomainObjectRole).value<Event::Ptr>();
65 int dayOfWeek = event->getStartTime().date().dayOfWeek();
66
67 Q_ASSERT(dayOfWeek != 0);
68 dayOfWeek -= 1;
69
70 SinkTrace() << "Adding event:" << event->getSummary() << "in bucket #" << dayOfWeek;
71
72 partitionedEvents[dayOfWeek].append(event);
73 }
74
75 endResetModel();
76
77 partitioningMutex.unlock();
78}
79
80QModelIndex EventTreeModel::index(int row, int column, const QModelIndex &parent) const
81{
82 if (!hasIndex(row, column, parent)) {
83 return {};
84 }
85
86 if (!parent.isValid()) {
87 // Asking for a LocalEventListModel
88
89 // TODO: Assuming week view
90 if (!(0 <= row && row <= 6)) {
91 return {};
92 }
93
94 return createIndex(row, column, DAY_ID);
95 }
96
97 // Asking for an Event
98 auto day = static_cast<int>(parent.row());
99
100 Q_ASSERT(0 <= day && day <= 6);
101 if (row >= partitionedEvents[day].size()) {
102 return {};
103 }
104
105 return createIndex(row, column, day);
106}
107
108QModelIndex EventTreeModel::parent(const QModelIndex &index) const
109{
110 if (!index.isValid()) {
111 return {};
112 }
113
114 if (index.internalId() == DAY_ID) {
115 return {};
116 }
117
118 auto day = index.internalId();
119
120 return this->index(day, 0);
121}
122
123int EventTreeModel::rowCount(const QModelIndex &parent) const
124{
125 if (!parent.isValid()) {
126 return 7;
127 }
128
129 auto day = parent.row();
130
131 return partitionedEvents[day].size();
132}
133
134int EventTreeModel::columnCount(const QModelIndex &parent) const
135{
136 if (!parent.isValid()) {
137 return 1;
138 }
139
140 return eventModel->columnCount();
141}
142
143QVariant EventTreeModel::data(const QModelIndex &id, int role) const
144{
145 if (id.internalId() == DAY_ID) {
146 auto day = id.row();
147
148 SinkTrace() << "Fetching data for day" << day << "with role" << QMetaEnum::fromType<Roles>().valueToKey(role);
149
150 switch (role) {
151 case Qt::DisplayRole:
152 switch (day) {
153 case 0:
154 return "Monday";
155 case 1:
156 return "Tuesday";
157 case 2:
158 return "Wednesday";
159 case 3:
160 return "Thursday";
161 case 4:
162 return "Friday";
163 case 5:
164 return "Saturday";
165 case 6:
166 return "Sunday";
167 default:
168 SinkWarning() << "Unknown day";
169 return {};
170 }
171 case Events: {
172 auto result = QVariantList{};
173
174 for (int i = 0; i < partitionedEvents[day].size(); ++i) {
175 auto eventId = index(i, 0, id);
176 SinkTrace() << "Appending event:" << data(eventId, Summary);
177
178 auto startTime = data(eventId, StartTime).toDateTime().time();
179
180 result.append(QVariantMap{
181 {"text", data(eventId, Summary)},
182 {"description", data(eventId, Description)},
183 {"starts", startTime.hour() + startTime.minute() / 60.},
184 {"duration", data(eventId, Duration)},
185 {"color", "#134bab"},
186 {"indention", 0},
187 });
188 }
189
190 return result;
191 }
192 default:
193 SinkWarning() << "Unknown role for day:" << QMetaEnum::fromType<Roles>().valueToKey(role);
194 return {};
195 }
196 } else {
197 auto day = id.internalId();
198 SinkTrace() << "Fetching data for event on day" << day << "with role" << QMetaEnum::fromType<Roles>().valueToKey(role);
199 auto event = partitionedEvents[day].at(id.row());
200
201 switch (role) {
202 case Summary:
203 return event->getSummary();
204 case Description:
205 return event->getDescription();
206 case StartTime:
207 return event->getStartTime();
208 case Duration: {
209 auto start = event->getStartTime();
210 auto end = event->getEndTime();
211 return start.secsTo(end) / 3600;
212 }
213 default:
214 SinkWarning() << "Unknown role for event:" << QMetaEnum::fromType<Roles>().valueToKey(role);
215 return {};
216 }
217 }
218}
219
220QHash<int, QByteArray> EventTreeModel::roleNames() const
221{
222 QHash<int, QByteArray> roles;
223
224 roles[Events] = "events";
225 roles[Summary] = "summary";
226 roles[Description] = "description";
227 roles[StartTime] = "starts";
228 roles[Duration] = "duration";
229
230 return roles;
231}
diff --git a/framework/src/domain/eventtreemodel.h b/framework/src/domain/eventtreemodel.h
new file mode 100644
index 00000000..daa45a7b
--- /dev/null
+++ b/framework/src/domain/eventtreemodel.h
@@ -0,0 +1,98 @@
1/*
2 Copyright (c) 2016 Michael Bohlender <michael.bohlender@kdemail.net>
3 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com>
4
5 This library is free software; you can redistribute it and/or modify it
6 under the terms of the GNU Library General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or (at your
8 option) any later version.
9
10 This library is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301, USA.
19*/
20
21#pragma once
22
23#include <sink/applicationdomaintype.h>
24
25#include <QAbstractItemModel>
26#include <QList>
27#include <QMutex>
28#include <QSharedPointer>
29#include <QVector>
30
31// Implementation notes
32// ====================
33//
34// On the model side
35// -----------------
36//
37// Columns are never used.
38//
39// Top-level items just contains the ".events" attribute, and their rows
40// correspond to their day of week. In that case the internalId contains
41// DAY_ID.
42//
43// Direct children are events, and their rows corresponds to their index in
44// their partition. In that case no internalId / internalPointer is used.
45//
46// Internally:
47// -----------
48//
49// On construction and on dataChanged, all events are processed and partitioned
50// in partitionedEvents:
51//
52// QVector< QList<QSharedPointer<Event> >
53// ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
54// | |
55// | '--- List of event pointers for that day
56// '--- Partition / day
57//
58class EventTreeModel : public QAbstractItemModel
59{
60 Q_OBJECT
61
62public:
63 using Event = Sink::ApplicationDomain::Event;
64
65 enum Roles
66 {
67 Events = Qt::UserRole + 1,
68 Summary,
69 Description,
70 StartTime,
71 Duration,
72 };
73 Q_ENUM(Roles);
74 EventTreeModel(QObject *parent = nullptr);
75 ~EventTreeModel() = default;
76
77 QModelIndex index(int row, int column, const QModelIndex &parent = {}) const override;
78 QModelIndex parent(const QModelIndex &index) const override;
79
80 int rowCount(const QModelIndex &parent) const override;
81 int columnCount(const QModelIndex &parent) const override;
82
83 //bool hasChildren(const QModelIndex &parent) const override;
84
85 QVariant data(const QModelIndex &index, int role) const override;
86
87 QHash<int, QByteArray> roleNames() const override;
88
89private:
90 void partitionData();
91
92 QSharedPointer<QAbstractItemModel> eventModel;
93 QVector<QList<QSharedPointer<Event>>> partitionedEvents;
94
95 QMutex partitioningMutex;
96
97 static const constexpr quintptr DAY_ID = 7;
98};