summaryrefslogtreecommitdiffstats
path: root/common/modelresult.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/modelresult.cpp')
-rw-r--r--common/modelresult.cpp251
1 files changed, 251 insertions, 0 deletions
diff --git a/common/modelresult.cpp b/common/modelresult.cpp
new file mode 100644
index 0000000..c7fcd49
--- /dev/null
+++ b/common/modelresult.cpp
@@ -0,0 +1,251 @@
1/*
2 * Copyright (C) 2014 Christian Mollekopf <chrigi_1@fastmail.fm>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20#include "modelresult.h"
21
22#include <QDebug>
23
24#include "domain/folder.h"
25#include "log.h"
26
27template<class T, class Ptr>
28ModelResult<T, Ptr>::ModelResult(const Akonadi2::Query &query, const QList<QByteArray> &propertyColumns)
29 :QAbstractItemModel(),
30 mPropertyColumns(propertyColumns),
31 mQuery(query)
32{
33}
34
35static qint64 getIdentifier(const QModelIndex &idx)
36{
37 if (!idx.isValid()) {
38 return 0;
39 }
40 return idx.internalId();
41}
42
43template<class T, class Ptr>
44qint64 ModelResult<T, Ptr>::parentId(const Ptr &value)
45{
46 if (!mQuery.parentProperty.isEmpty()) {
47 const auto property = value->getProperty(mQuery.parentProperty).toByteArray();
48 if (!property.isEmpty()) {
49 return qHash(property);
50 }
51 }
52 return 0;
53}
54
55template<class T, class Ptr>
56int ModelResult<T, Ptr>::rowCount(const QModelIndex &parent) const
57{
58 return mTree[getIdentifier(parent)].size();
59}
60
61template<class T, class Ptr>
62int ModelResult<T, Ptr>::columnCount(const QModelIndex &parent) const
63{
64 return mPropertyColumns.size();
65}
66
67template<class T, class Ptr>
68QVariant ModelResult<T, Ptr>::data(const QModelIndex &index, int role) const
69{
70 if (role == DomainObjectRole) {
71 Q_ASSERT(mEntities.contains(index.internalId()));
72 return QVariant::fromValue(mEntities.value(index.internalId()));
73 }
74 if (role == ChildrenFetchedRole) {
75 return childrenFetched(index);
76 }
77 if (role == Qt::DisplayRole) {
78 if (index.column() < mPropertyColumns.size()) {
79 Q_ASSERT(mEntities.contains(index.internalId()));
80 auto entity = mEntities.value(index.internalId());
81 return entity->getProperty(mPropertyColumns.at(index.column())).toString();
82 } else {
83 return "No data available";
84 }
85 }
86 return QVariant();
87}
88
89template<class T, class Ptr>
90QModelIndex ModelResult<T, Ptr>::index(int row, int column, const QModelIndex &parent) const
91{
92 const auto id = getIdentifier(parent);
93 const auto childId = mTree.value(id).at(row);
94 return createIndex(row, column, childId);
95}
96
97template<class T, class Ptr>
98QModelIndex ModelResult<T, Ptr>::createIndexFromId(const qint64 &id) const
99{
100 if (id == 0) {
101 return QModelIndex();
102 }
103 auto grandParentId = mParents.value(id, 0);
104 auto row = mTree.value(grandParentId).indexOf(id);
105 return createIndex(row, 0, id);
106}
107
108template<class T, class Ptr>
109QModelIndex ModelResult<T, Ptr>::parent(const QModelIndex &index) const
110{
111 auto id = getIdentifier(index);
112 auto parentId = mParents.value(id);
113 return createIndexFromId(parentId);
114}
115
116template<class T, class Ptr>
117bool ModelResult<T, Ptr>::hasChildren(const QModelIndex &parent) const
118{
119 if (mQuery.parentProperty.isEmpty() && parent.isValid()) {
120 return false;
121 }
122 return QAbstractItemModel::hasChildren(parent);
123}
124
125template<class T, class Ptr>
126bool ModelResult<T, Ptr>::canFetchMore(const QModelIndex &parent) const
127{
128 return !mEntityChildrenFetched.contains(parent.internalId());
129}
130
131template<class T, class Ptr>
132void ModelResult<T, Ptr>::fetchMore(const QModelIndex &parent)
133{
134 fetchEntities(parent);
135}
136
137template<class T, class Ptr>
138void ModelResult<T, Ptr>::add(const Ptr &value)
139{
140 const auto childId = qHash(value->identifier());
141 const auto id = parentId(value);
142 //Ignore updates we get before the initial fetch is done
143 if (!mEntityChildrenFetched.contains(id)) {
144 return;
145 }
146 auto parent = createIndexFromId(id);
147 // qDebug() << "Added entity " << childId << value->identifier() << id;
148 const auto keys = mTree[id];
149 int index = 0;
150 for (; index < keys.size(); index++) {
151 if (childId < keys.at(index)) {
152 break;
153 }
154 }
155 if (mEntities.contains(childId)) {
156 Warning() << "Entity already in model " << value->identifier();
157 return;
158 }
159 // qDebug() << "Inserting rows " << index << parent;
160 beginInsertRows(parent, index, index);
161 mEntities.insert(childId, value);
162 mTree[id].insert(index, childId);
163 mParents.insert(childId, id);
164 endInsertRows();
165 // qDebug() << "Inserted rows " << mTree[id].size();
166}
167
168
169template<class T, class Ptr>
170void ModelResult<T, Ptr>::remove(const Ptr &value)
171{
172 auto childId = qHash(value->identifier());
173 auto id = parentId(value);
174 auto parent = createIndexFromId(id);
175 // qDebug() << "Removed entity" << childId;
176 auto index = mTree[id].indexOf(qHash(value->identifier()));
177 beginRemoveRows(parent, index, index);
178 mEntities.remove(childId);
179 mTree[id].removeAll(childId);
180 mParents.remove(childId);
181 //TODO remove children
182 endRemoveRows();
183}
184
185template<class T, class Ptr>
186void ModelResult<T, Ptr>::fetchEntities(const QModelIndex &parent)
187{
188 const auto id = getIdentifier(parent);
189 mEntityChildrenFetched.insert(id);
190 Trace() << "Loading child entities";
191 loadEntities(parent.data(DomainObjectRole).template value<Ptr>());
192}
193
194template<class T, class Ptr>
195void ModelResult<T, Ptr>::setFetcher(const std::function<void(const Ptr &parent)> &fetcher)
196{
197 Trace() << "Setting fetcher";
198 loadEntities = fetcher;
199}
200
201template<class T, class Ptr>
202void ModelResult<T, Ptr>::setEmitter(const typename Akonadi2::ResultEmitter<Ptr>::Ptr &emitter)
203{
204 setFetcher(emitter->mFetcher);
205 emitter->onAdded([this](const Ptr &value) {
206 this->add(value);
207 });
208 emitter->onModified([this](const Ptr &value) {
209 this->modify(value);
210 });
211 emitter->onRemoved([this](const Ptr &value) {
212 this->remove(value);
213 });
214 emitter->onInitialResultSetComplete([this](const Ptr &parent) {
215 const qint64 parentId = parent ? qHash(parent->identifier()) : 0;
216 const auto parentIndex = createIndexFromId(parentId);
217 mEntityChildrenFetchComplete.insert(parentId);
218 emit dataChanged(parentIndex, parentIndex, QVector<int>() << ChildrenFetchedRole);
219 });
220 mEmitter = emitter;
221}
222
223template<class T, class Ptr>
224bool ModelResult<T, Ptr>::childrenFetched(const QModelIndex &index) const
225{
226 return mEntityChildrenFetchComplete.contains(getIdentifier(index));
227}
228
229template<class T, class Ptr>
230void ModelResult<T, Ptr>::modify(const Ptr &value)
231{
232 auto childId = qHash(value->identifier());
233 auto id = parentId(value);
234 //Ignore updates we get before the initial fetch is done
235 if (!mEntityChildrenFetched.contains(id)) {
236 return;
237 }
238 auto parent = createIndexFromId(id);
239 // qDebug() << "Modified entity" << childId;
240 auto i = mTree[id].indexOf(childId);
241 mEntities.remove(childId);
242 mEntities.insert(childId, value);
243 //TODO check for change of parents
244 auto idx = index(i, 0, parent);
245 emit dataChanged(idx, idx);
246}
247
248template class ModelResult<Akonadi2::ApplicationDomain::Folder, Akonadi2::ApplicationDomain::Folder::Ptr>;
249template class ModelResult<Akonadi2::ApplicationDomain::Mail, Akonadi2::ApplicationDomain::Mail::Ptr>;
250template class ModelResult<Akonadi2::ApplicationDomain::Event, Akonadi2::ApplicationDomain::Event::Ptr>;
251template class ModelResult<Akonadi2::ApplicationDomain::AkonadiResource, Akonadi2::ApplicationDomain::AkonadiResource::Ptr>;