summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2015-11-19 23:23:56 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2015-11-19 23:23:56 +0100
commit94a2cd6ec21bf0466a9a50d6e4a0a956ed47bc82 (patch)
tree8f05e8ed03691b006b1f2bf8f08bc21e582aad26
parentc4a6746e4420b580fe35cc89783de4dbc3205ac6 (diff)
downloadsink-94a2cd6ec21bf0466a9a50d6e4a0a956ed47bc82.tar.gz
sink-94a2cd6ec21bf0466a9a50d6e4a0a956ed47bc82.zip
Move implementations to the cpp file.
I finally figured out how to do that with cpp files. It requires instantiating the code with all expected classes, but that's not a big problem since we know all types. This will hopefully greatly reduce the compiletimes...
-rw-r--r--common/clientapi.cpp151
-rw-r--r--common/clientapi.h109
-rw-r--r--common/domain/applicationdomaintype.h2
-rw-r--r--common/modelresult.cpp203
-rw-r--r--common/modelresult.h167
-rw-r--r--examples/dummyresource/dummystore.cpp22
6 files changed, 400 insertions, 254 deletions
diff --git a/common/clientapi.cpp b/common/clientapi.cpp
index 839e77b..02f8ce6 100644
--- a/common/clientapi.cpp
+++ b/common/clientapi.cpp
@@ -1,13 +1,40 @@
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 */
1 20
2#include "clientapi.h" 21#include "clientapi.h"
22
23#include <QtConcurrent/QtConcurrentRun>
24#include <QTimer>
25#include <QEventLoop>
26#include <QAbstractItemModel>
27#include <functional>
28#include <memory>
29
3#include "resourceaccess.h" 30#include "resourceaccess.h"
4#include "commands.h" 31#include "commands.h"
5#include "resourcefacade.h" 32#include "resourcefacade.h"
6#include "log.h" 33#include "log.h"
7#include "definitions.h" 34#include "definitions.h"
8#include "resourceconfig.h" 35#include "resourceconfig.h"
9#include <QtConcurrent/QtConcurrentRun> 36#include "facadefactory.h"
10#include <QTimer> 37#include "log.h"
11 38
12#define ASYNCINTHREAD 39#define ASYNCINTHREAD
13 40
@@ -69,6 +96,114 @@ QList<QByteArray> Store::getResources(const QList<QByteArray> &resourceFilter, c
69 return resources; 96 return resources;
70} 97}
71 98
99template <class DomainType>
100QSharedPointer<ResultEmitter<typename DomainType::Ptr> > Store::load(Query query)
101{
102 auto resultSet = QSharedPointer<ResultProvider<typename DomainType::Ptr> >::create();
103
104 //Execute the search in a thread.
105 //We must guarantee that the emitter is returned before the first result is emitted.
106 //The result provider must be threadsafe.
107 async::run([query, resultSet](){
108 QEventLoop eventLoop;
109 resultSet->onDone([&eventLoop](){
110 eventLoop.quit();
111 });
112 // Query all resources and aggregate results
113 KAsync::iterate(getResources(query.resources, ApplicationDomain::getTypeName<DomainType>()))
114 .template each<void, QByteArray>([query, resultSet](const QByteArray &resource, KAsync::Future<void> &future) {
115 if (auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resource), resource)) {
116 facade->load(query, *resultSet).template then<void>([&future](){future.setFinished();}).exec();
117 //Keep the facade alive for the lifetime of the resultSet.
118 resultSet->setFacade(facade);
119 } else {
120 //Ignore the error and carry on
121 future.setFinished();
122 }
123 }).template then<void>([query, resultSet]() {
124 resultSet->initialResultSetComplete();
125 if (!query.liveQuery) {
126 resultSet->complete();
127 }
128 }).exec();
129
130 //Keep the thread alive until the result is ready
131 if (!resultSet->isDone()) {
132 eventLoop.exec();
133 }
134 });
135 return resultSet->emitter();
136}
137
138template <class DomainType>
139QSharedPointer<QAbstractItemModel> Store::loadModel(Query query)
140{
141 auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr> >::create(query, query.requestedProperties.toList());
142 auto resultProvider = std::make_shared<ModelResultProvider<DomainType, typename DomainType::Ptr> >(model);
143 //Keep the resultprovider alive for as long as the model lives
144 model->setProperty("resultProvider", QVariant::fromValue(std::shared_ptr<void>(resultProvider)));
145
146 // Query all resources and aggregate results
147 KAsync::iterate(getResources(query.resources, ApplicationDomain::getTypeName<DomainType>()))
148 .template each<void, QByteArray>([query, resultProvider](const QByteArray &resource, KAsync::Future<void> &future) {
149 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resource), resource);
150 if (facade) {
151 facade->load(query, *resultProvider).template then<void>([&future](){future.setFinished();}).exec();
152 //Keep the facade alive for the lifetime of the resultSet.
153 //FIXME this would have to become a list
154 resultProvider->setFacade(facade);
155 } else {
156 //Ignore the error and carry on
157 future.setFinished();
158 }
159 }).template then<void>([query, resultProvider]() {
160 resultProvider->initialResultSetComplete();
161 if (!query.liveQuery) {
162 resultProvider->complete();
163 }
164 }).exec();
165
166 return model;
167}
168
169template <class DomainType>
170static std::shared_ptr<StoreFacade<DomainType> > getFacade(const QByteArray &resourceInstanceIdentifier)
171{
172 if (auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resourceInstanceIdentifier), resourceInstanceIdentifier)) {
173 return facade;
174 }
175 return std::make_shared<NullFacade<DomainType> >();
176}
177
178template <class DomainType>
179KAsync::Job<void> Store::create(const DomainType &domainObject) {
180 //Potentially move to separate thread as well
181 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
182 return facade->create(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
183 Warning() << "Failed to create";
184 });
185}
186
187template <class DomainType>
188KAsync::Job<void> Store::modify(const DomainType &domainObject)
189{
190 //Potentially move to separate thread as well
191 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
192 return facade->modify(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
193 Warning() << "Failed to modify";
194 });
195}
196
197template <class DomainType>
198KAsync::Job<void> Store::remove(const DomainType &domainObject)
199{
200 //Potentially move to separate thread as well
201 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
202 return facade->remove(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
203 Warning() << "Failed to remove";
204 });
205}
206
72KAsync::Job<void> Store::shutdown(const QByteArray &identifier) 207KAsync::Job<void> Store::shutdown(const QByteArray &identifier)
73{ 208{
74 Trace() << "shutdown"; 209 Trace() << "shutdown";
@@ -103,4 +238,16 @@ KAsync::Job<void> Store::synchronize(const Akonadi2::Query &query)
103 .template then<void>([](){}); 238 .template then<void>([](){});
104} 239}
105 240
241#define REGISTER_TYPE(T) template KAsync::Job<void> Store::remove<T>(const T &domainObject); \
242 template KAsync::Job<void> Store::create<T>(const T &domainObject); \
243 template KAsync::Job<void> Store::modify<T>(const T &domainObject); \
244 template QSharedPointer<ResultEmitter<typename T::Ptr> > Store::load<T>(Query query); \
245 template QSharedPointer<QAbstractItemModel> Store::loadModel<T>(Query query); \
246
247REGISTER_TYPE(ApplicationDomain::Event);
248REGISTER_TYPE(ApplicationDomain::Mail);
249REGISTER_TYPE(ApplicationDomain::Folder);
250REGISTER_TYPE(ApplicationDomain::AkonadiResource);
251
106} // namespace Akonadi2 252} // namespace Akonadi2
253
diff --git a/common/clientapi.h b/common/clientapi.h
index 6b11ad5..c48c6e9 100644
--- a/common/clientapi.h
+++ b/common/clientapi.h
@@ -22,21 +22,17 @@
22 22
23#include <QString> 23#include <QString>
24#include <QSharedPointer> 24#include <QSharedPointer>
25#include <QEventLoop>
26#include <QAbstractItemModel>
27#include <functional>
28#include <memory>
29 25
30#include <Async/Async> 26#include <Async/Async>
31 27
32#include "query.h" 28#include "query.h"
33#include "resultprovider.h" 29#include "resultprovider.h"
34#include "applicationdomaintype.h" 30#include "applicationdomaintype.h"
35#include "facadefactory.h"
36#include "log.h"
37 31
38Q_DECLARE_METATYPE(std::shared_ptr<void>); 32Q_DECLARE_METATYPE(std::shared_ptr<void>);
39 33
34class QAbstractItemModel;
35
40namespace async { 36namespace async {
41 //This should abstract if we execute from eventloop or in thread. 37 //This should abstract if we execute from eventloop or in thread.
42 //It supposed to allow the caller to finish the current method before executing the runner. 38 //It supposed to allow the caller to finish the current method before executing the runner.
@@ -62,43 +58,7 @@ public:
62 * Asynchronusly load a dataset 58 * Asynchronusly load a dataset
63 */ 59 */
64 template <class DomainType> 60 template <class DomainType>
65 static QSharedPointer<ResultEmitter<typename DomainType::Ptr> > load(Query query) 61 static QSharedPointer<ResultEmitter<typename DomainType::Ptr> > load(Query query);
66 {
67 auto resultSet = QSharedPointer<ResultProvider<typename DomainType::Ptr> >::create();
68
69 //Execute the search in a thread.
70 //We must guarantee that the emitter is returned before the first result is emitted.
71 //The result provider must be threadsafe.
72 async::run([query, resultSet](){
73 QEventLoop eventLoop;
74 resultSet->onDone([&eventLoop](){
75 eventLoop.quit();
76 });
77 // Query all resources and aggregate results
78 KAsync::iterate(getResources(query.resources, ApplicationDomain::getTypeName<DomainType>()))
79 .template each<void, QByteArray>([query, resultSet](const QByteArray &resource, KAsync::Future<void> &future) {
80 if (auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resource), resource)) {
81 facade->load(query, *resultSet).template then<void>([&future](){future.setFinished();}).exec();
82 //Keep the facade alive for the lifetime of the resultSet.
83 resultSet->setFacade(facade);
84 } else {
85 //Ignore the error and carry on
86 future.setFinished();
87 }
88 }).template then<void>([query, resultSet]() {
89 resultSet->initialResultSetComplete();
90 if (!query.liveQuery) {
91 resultSet->complete();
92 }
93 }).exec();
94
95 //Keep the thread alive until the result is ready
96 if (!resultSet->isDone()) {
97 eventLoop.exec();
98 }
99 });
100 return resultSet->emitter();
101 }
102 62
103 enum Roles { 63 enum Roles {
104 DomainObjectRole = Qt::UserRole + 1 //Must be the same as in ModelResult 64 DomainObjectRole = Qt::UserRole + 1 //Must be the same as in ModelResult
@@ -108,56 +68,13 @@ public:
108 * Asynchronusly load a dataset with tree structure information 68 * Asynchronusly load a dataset with tree structure information
109 */ 69 */
110 template <class DomainType> 70 template <class DomainType>
111 static QSharedPointer<QAbstractItemModel> loadModel(Query query) 71 static QSharedPointer<QAbstractItemModel> loadModel(Query query);
112 {
113 auto model = QSharedPointer<ModelResult<DomainType, typename DomainType::Ptr> >::create(query, query.requestedProperties.toList());
114 auto resultProvider = std::make_shared<ModelResultProvider<DomainType, typename DomainType::Ptr> >(model);
115 //Keep the resultprovider alive for as long as the model lives
116 model->setProperty("resultProvider", QVariant::fromValue(std::shared_ptr<void>(resultProvider)));
117
118 // Query all resources and aggregate results
119 KAsync::iterate(getResources(query.resources, ApplicationDomain::getTypeName<DomainType>()))
120 .template each<void, QByteArray>([query, resultProvider](const QByteArray &resource, KAsync::Future<void> &future) {
121 auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resource), resource);
122 if (facade) {
123 facade->load(query, *resultProvider).template then<void>([&future](){future.setFinished();}).exec();
124 //Keep the facade alive for the lifetime of the resultSet.
125 //FIXME this would have to become a list
126 resultProvider->setFacade(facade);
127 } else {
128 //Ignore the error and carry on
129 future.setFinished();
130 }
131 }).template then<void>([query, resultProvider]() {
132 resultProvider->initialResultSetComplete();
133 if (!query.liveQuery) {
134 resultProvider->complete();
135 }
136 }).exec();
137
138 return model;
139 }
140
141 template <class DomainType>
142 static std::shared_ptr<StoreFacade<DomainType> > getFacade(const QByteArray &resourceInstanceIdentifier)
143 {
144 if (auto facade = FacadeFactory::instance().getFacade<DomainType>(resourceName(resourceInstanceIdentifier), resourceInstanceIdentifier)) {
145 return facade;
146 }
147 return std::make_shared<NullFacade<DomainType> >();
148 }
149 72
150 /** 73 /**
151 * Create a new entity. 74 * Create a new entity.
152 */ 75 */
153 template <class DomainType> 76 template <class DomainType>
154 static KAsync::Job<void> create(const DomainType &domainObject) { 77 static KAsync::Job<void> create(const DomainType &domainObject);
155 //Potentially move to separate thread as well
156 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
157 return facade->create(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
158 Warning() << "Failed to create";
159 });
160 }
161 78
162 /** 79 /**
163 * Modify an entity. 80 * Modify an entity.
@@ -165,25 +82,13 @@ public:
165 * This includes moving etc. since these are also simple settings on a property. 82 * This includes moving etc. since these are also simple settings on a property.
166 */ 83 */
167 template <class DomainType> 84 template <class DomainType>
168 static KAsync::Job<void> modify(const DomainType &domainObject) { 85 static KAsync::Job<void> modify(const DomainType &domainObject);
169 //Potentially move to separate thread as well
170 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
171 return facade->modify(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
172 Warning() << "Failed to modify";
173 });
174 }
175 86
176 /** 87 /**
177 * Remove an entity. 88 * Remove an entity.
178 */ 89 */
179 template <class DomainType> 90 template <class DomainType>
180 static KAsync::Job<void> remove(const DomainType &domainObject) { 91 static KAsync::Job<void> remove(const DomainType &domainObject);
181 //Potentially move to separate thread as well
182 auto facade = getFacade<DomainType>(domainObject.resourceInstanceIdentifier());
183 return facade->remove(domainObject).template then<void>([facade](){}, [](int errorCode, const QString &error) {
184 Warning() << "Failed to remove";
185 });
186 }
187 92
188 /** 93 /**
189 * Shutdown resource. 94 * Shutdown resource.
diff --git a/common/domain/applicationdomaintype.h b/common/domain/applicationdomaintype.h
index b4cf8c4..227ab4d 100644
--- a/common/domain/applicationdomaintype.h
+++ b/common/domain/applicationdomaintype.h
@@ -162,3 +162,5 @@ Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Mail)
162Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Mail::Ptr) 162Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Mail::Ptr)
163Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Folder) 163Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Folder)
164Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Folder::Ptr) 164Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Folder::Ptr)
165Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::AkonadiResource)
166Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::AkonadiResource::Ptr)
diff --git a/common/modelresult.cpp b/common/modelresult.cpp
new file mode 100644
index 0000000..1abcc62
--- /dev/null
+++ b/common/modelresult.cpp
@@ -0,0 +1,203 @@
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{
32}
33
34static qint64 getIdentifier(const QModelIndex &idx)
35{
36 if (!idx.isValid()) {
37 return 0;
38 }
39 return idx.internalId();
40}
41
42template<class T, class Ptr>
43qint64 ModelResult<T, Ptr>::parentId(const Ptr &value)
44{
45 return qHash(value->getProperty("parent").toByteArray());
46}
47
48template<class T, class Ptr>
49int ModelResult<T, Ptr>::rowCount(const QModelIndex &parent) const
50{
51 return mTree[getIdentifier(parent)].size();
52}
53
54template<class T, class Ptr>
55int ModelResult<T, Ptr>::columnCount(const QModelIndex &parent) const
56{
57 return mPropertyColumns.size();
58}
59
60template<class T, class Ptr>
61QVariant ModelResult<T, Ptr>::data(const QModelIndex &index, int role) const
62{
63 if (role == DomainObjectRole) {
64 Q_ASSERT(mEntities.contains(index.internalId()));
65 return QVariant::fromValue(mEntities.value(index.internalId()));
66 }
67 if (role == Qt::DisplayRole) {
68 if (index.column() < mPropertyColumns.size()) {
69 Q_ASSERT(mEntities.contains(index.internalId()));
70 auto entity = mEntities.value(index.internalId());
71 return entity->getProperty(mPropertyColumns.at(index.column())).toString();
72 } else {
73 return "No data available";
74 }
75 }
76 return QVariant();
77}
78
79template<class T, class Ptr>
80QModelIndex ModelResult<T, Ptr>::index(int row, int column, const QModelIndex &parent) const
81{
82 auto id = getIdentifier(parent);
83 auto childId = mTree.value(id).at(row);
84 return createIndex(row, column, childId);
85}
86
87template<class T, class Ptr>
88QModelIndex ModelResult<T, Ptr>::createIndexFromId(const qint64 &id) const
89{
90 auto grandParentId = mParents.value(id, 0);
91 auto row = mTree.value(grandParentId).indexOf(id);
92 return createIndex(row, 0, id);
93}
94
95template<class T, class Ptr>
96QModelIndex ModelResult<T, Ptr>::parent(const QModelIndex &index) const
97{
98 auto id = getIdentifier(index);
99 auto parentId = mParents.value(id);
100 return createIndexFromId(parentId);
101}
102
103template<class T, class Ptr>
104bool ModelResult<T, Ptr>::canFetchMore(const QModelIndex &parent) const
105{
106 qDebug() << "Can fetch more: " << parent << mEntityChildrenFetched.value(parent.internalId());
107 return mEntityChildrenFetched.value(parent.internalId());
108}
109
110template<class T, class Ptr>
111void ModelResult<T, Ptr>::fetchMore(const QModelIndex &parent)
112{
113 qDebug() << "Fetch more: " << parent;
114 fetchEntities(parent);
115}
116
117template<class T, class Ptr>
118void ModelResult<T, Ptr>::add(const Ptr &value)
119{
120 auto childId = qHash(value->identifier());
121 auto id = parentId(value);
122 //Ignore updates we get before the initial fetch is done
123 if (!mEntityChildrenFetched[id]) {
124 return;
125 }
126 auto parent = createIndexFromId(id);
127 qDebug() << "Added entity " << childId << value->identifier() << id;
128 const auto keys = mTree[id];
129 int index = 0;
130 for (; index < keys.size(); index++) {
131 if (childId < keys.at(index)) {
132 break;
133 }
134 }
135 if (mEntities.contains(childId)) {
136 qWarning() << "Entity already in model " << value->identifier();
137 return;
138 }
139 qDebug() << "Inserting rows " << index << parent;
140 beginInsertRows(QModelIndex(), index, index);
141 mEntities.insert(childId, value);
142 mTree[id].insert(index, childId);
143 mParents.insert(childId, id);
144 endInsertRows();
145 qDebug() << "Inserted rows " << mTree[id].size();
146}
147
148
149template<class T, class Ptr>
150void ModelResult<T, Ptr>::remove(const Ptr &value)
151{
152 auto childId = qHash(value->identifier());
153 auto id = parentId(value);
154 auto parent = createIndexFromId(id);
155 qDebug() << "Removed entity" << childId;
156 auto index = mTree[id].indexOf(qHash(value->identifier()));
157 beginRemoveRows(parent, index, index);
158 mEntities.remove(childId);
159 mTree[id].removeAll(childId);
160 mParents.remove(childId);
161 //TODO remove children
162 endRemoveRows();
163}
164
165template<class T, class Ptr>
166void ModelResult<T, Ptr>::fetchEntities(const QModelIndex &parent)
167{
168 const auto id = getIdentifier(parent);
169 mEntityChildrenFetched[id] = true;
170 Trace() << "Loading child entities";
171 loadEntities(parent.data(DomainObjectRole).template value<Ptr>());
172}
173
174template<class T, class Ptr>
175void ModelResult<T, Ptr>::setFetcher(const std::function<void(const Ptr &parent)> &fetcher)
176{
177 Trace() << "Setting fetcher";
178 loadEntities = fetcher;
179}
180
181template<class T, class Ptr>
182void ModelResult<T, Ptr>::modify(const Ptr &value)
183{
184 auto childId = qHash(value->identifier());
185 auto id = parentId(value);
186 //Ignore updates we get before the initial fetch is done
187 if (!mEntityChildrenFetched[id]) {
188 return;
189 }
190 auto parent = createIndexFromId(id);
191 qDebug() << "Modified entity" << childId;
192 auto i = mTree[id].indexOf(childId);
193 mEntities.remove(childId);
194 mEntities.insert(childId, value);
195 //TODO check for change of parents
196 auto idx = index(i, 0, parent);
197 emit dataChanged(idx, idx);
198}
199
200template class ModelResult<Akonadi2::ApplicationDomain::Folder, Akonadi2::ApplicationDomain::Folder::Ptr>;
201template class ModelResult<Akonadi2::ApplicationDomain::Mail, Akonadi2::ApplicationDomain::Mail::Ptr>;
202template class ModelResult<Akonadi2::ApplicationDomain::Event, Akonadi2::ApplicationDomain::Event::Ptr>;
203template class ModelResult<Akonadi2::ApplicationDomain::AkonadiResource, Akonadi2::ApplicationDomain::AkonadiResource::Ptr>;
diff --git a/common/modelresult.h b/common/modelresult.h
index 26f96d8..40a9d9d 100644
--- a/common/modelresult.h
+++ b/common/modelresult.h
@@ -23,10 +23,9 @@
23#include <QAbstractItemModel> 23#include <QAbstractItemModel>
24#include <QModelIndex> 24#include <QModelIndex>
25#include <QDebug> 25#include <QDebug>
26#include <functional>
26#include "query.h" 27#include "query.h"
27 28
28#include "resultprovider.h"
29
30template<class T, class Ptr> 29template<class T, class Ptr>
31class ModelResult : public QAbstractItemModel 30class ModelResult : public QAbstractItemModel
32{ 31{
@@ -36,160 +35,28 @@ public:
36 DomainObjectRole = Qt::UserRole + 1 35 DomainObjectRole = Qt::UserRole + 1
37 }; 36 };
38 37
39 ModelResult(const Akonadi2::Query &query, const QList<QByteArray> &propertyColumns) 38 ModelResult(const Akonadi2::Query &query, const QList<QByteArray> &propertyColumns);
40 :QAbstractItemModel(),
41 mPropertyColumns(propertyColumns)
42 {
43 }
44
45 static qint64 getIdentifier(const QModelIndex &idx)
46 {
47 if (!idx.isValid()) {
48 return 0;
49 }
50 return idx.internalId();
51 }
52
53 int rowCount(const QModelIndex &parent = QModelIndex()) const
54 {
55 return mTree[getIdentifier(parent)].size();
56 }
57
58 int columnCount(const QModelIndex &parent = QModelIndex()) const
59 {
60 return mPropertyColumns.size();
61 }
62
63 virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
64 {
65 if (role == DomainObjectRole) {
66 Q_ASSERT(mEntities.contains(index.internalId()));
67 return QVariant::fromValue(mEntities.value(index.internalId()));
68 }
69 if (role == Qt::DisplayRole) {
70 if (index.column() < mPropertyColumns.size()) {
71 Q_ASSERT(mEntities.contains(index.internalId()));
72 auto entity = mEntities.value(index.internalId());
73 return entity->getProperty(mPropertyColumns.at(index.column())).toString();
74 } else {
75 return "No data available";
76 }
77 }
78 return QVariant();
79 }
80
81 QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const
82 {
83 auto id = getIdentifier(parent);
84 auto childId = mTree.value(id).at(row);
85 return createIndex(row, column, childId);
86 }
87
88 QModelIndex createIndexFromId(const qint64 &id) const
89 {
90 auto grandParentId = mParents.value(id, 0);
91 auto row = mTree.value(grandParentId).indexOf(id);
92 return createIndex(row, 0, id);
93 }
94 39
95 QModelIndex parent(const QModelIndex &index) const 40 int rowCount(const QModelIndex &parent = QModelIndex()) const;
96 { 41 int columnCount(const QModelIndex &parent = QModelIndex()) const;
97 auto id = getIdentifier(index); 42 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
98 auto parentId = mParents.value(id); 43 QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
99 return createIndexFromId(parentId); 44 QModelIndex parent(const QModelIndex &index) const;
100 }
101 45
102 bool canFetchMore(const QModelIndex &parent) const 46 bool canFetchMore(const QModelIndex &parent) const;
103 { 47 void fetchMore(const QModelIndex &parent);
104 return mEntityChildrenFetched.value(parent.internalId());
105 }
106 48
107 void fetchMore(const QModelIndex &parent) 49 void add(const Ptr &value);
108 { 50 void modify(const Ptr &value);
109 fetchEntities(parent); 51 void remove(const Ptr &value);
110 }
111 52
112 qint64 parentId(const Ptr &value) 53 void setFetcher(const std::function<void(const Ptr &parent)> &fetcher);
113 {
114 return qHash(value->getProperty("parent").toByteArray());
115 }
116
117 void add(const Ptr &value)
118 {
119 auto childId = qHash(value->identifier());
120 auto id = parentId(value);
121 //Ignore updates we get before the initial fetch is done
122 if (!mEntityChildrenFetched[id]) {
123 return;
124 }
125 auto parent = createIndexFromId(id);
126 qDebug() << "Added entity " << childId << value->identifier();
127 const auto keys = mTree[id];
128 int index = 0;
129 for (; index < keys.size(); index++) {
130 if (childId < keys.at(index)) {
131 break;
132 }
133 }
134 if (mEntities.contains(childId)) {
135 qWarning() << "Entity already in model " << value->identifier();
136 return;
137 }
138 beginInsertRows(QModelIndex(), index, index);
139 mEntities.insert(childId, value);
140 mTree[id].insert(index, childId);
141 mParents.insert(childId, id);
142 endInsertRows();
143 }
144
145 void modify(const Ptr &value)
146 {
147 auto childId = qHash(value->identifier());
148 auto id = parentId(value);
149 //Ignore updates we get before the initial fetch is done
150 if (!mEntityChildrenFetched[id]) {
151 return;
152 }
153 auto parent = createIndexFromId(id);
154 qDebug() << "Modified entity" << childId;
155 auto i = mTree[id].indexOf(childId);
156 mEntities.remove(childId);
157 mEntities.insert(childId, value);
158 //TODO check for change of parents
159 auto idx = index(i, 0, parent);
160 emit dataChanged(idx, idx);
161 }
162
163 void remove(const Ptr &value)
164 {
165 auto childId = qHash(value->identifier());
166 auto id = parentId(value);
167 auto parent = createIndexFromId(id);
168 qDebug() << "Removed entity" << childId;
169 auto index = mTree[id].indexOf(qHash(value->identifier()));
170 beginRemoveRows(parent, index, index);
171 mEntities.remove(childId);
172 mTree[id].removeAll(childId);
173 mParents.remove(childId);
174 //TODO remove children
175 endRemoveRows();
176 }
177
178 void fetchEntities(const QModelIndex &parent)
179 {
180 const auto id = getIdentifier(parent);
181 mEntityChildrenFetched[id] = true;
182 Trace() << "Loading child entities";
183 loadEntities(parent.data(DomainObjectRole).template value<Ptr>());
184 }
185
186 void setFetcher(const std::function<void(const Ptr &parent)> &fetcher)
187 {
188 Trace() << "Setting fetcher";
189 loadEntities = fetcher;
190 }
191 54
192private: 55private:
56 static qint64 parentId(const Ptr &value);
57 QModelIndex createIndexFromId(const qint64 &id) const;
58 void fetchEntities(const QModelIndex &parent);
59
193 //TODO we should be able to directly use T as index, with an appropriate hash function, and thus have a QMap<T, T> and QList<T> 60 //TODO we should be able to directly use T as index, with an appropriate hash function, and thus have a QMap<T, T> and QList<T>
194 QMap<qint64 /* entity id */, Ptr> mEntities; 61 QMap<qint64 /* entity id */, Ptr> mEntities;
195 QMap<qint64 /* parent entity id */, QList<qint64> /* child entity id*/> mTree; 62 QMap<qint64 /* parent entity id */, QList<qint64> /* child entity id*/> mTree;
diff --git a/examples/dummyresource/dummystore.cpp b/examples/dummyresource/dummystore.cpp
index 0356ec0..39ecfe4 100644
--- a/examples/dummyresource/dummystore.cpp
+++ b/examples/dummyresource/dummystore.cpp
@@ -36,6 +36,13 @@ static QMap<QString, QVariant> createMail(int i)
36 return mail; 36 return mail;
37} 37}
38 38
39static QMap<QString, QVariant> createFolder(int i)
40{
41 QMap<QString, QVariant> folder;
42 folder.insert("name", QString("folder%1").arg(i));
43 return folder;
44}
45
39QMap<QString, QMap<QString, QVariant> > populateEvents() 46QMap<QString, QMap<QString, QVariant> > populateEvents()
40{ 47{
41 QMap<QString, QMap<QString, QVariant>> content; 48 QMap<QString, QMap<QString, QVariant>> content;
@@ -54,8 +61,18 @@ QMap<QString, QMap<QString, QVariant> > populateMails()
54 return content; 61 return content;
55} 62}
56 63
64QMap<QString, QMap<QString, QVariant> > populateFolders()
65{
66 QMap<QString, QMap<QString, QVariant>> content;
67 for (int i = 0; i < 5000; i++) {
68 content.insert(QString("key%1").arg(i), createFolder(i));
69 }
70 return content;
71}
72
57static QMap<QString, QMap<QString, QVariant> > s_eventSource = populateEvents(); 73static QMap<QString, QMap<QString, QVariant> > s_eventSource = populateEvents();
58static QMap<QString, QMap<QString, QVariant> > s_mailSource = populateMails(); 74static QMap<QString, QMap<QString, QVariant> > s_mailSource = populateMails();
75static QMap<QString, QMap<QString, QVariant> > s_folderSource = populateFolders();
59 76
60QMap<QString, QMap<QString, QVariant> > DummyStore::events() const 77QMap<QString, QMap<QString, QVariant> > DummyStore::events() const
61{ 78{
@@ -66,3 +83,8 @@ QMap<QString, QMap<QString, QVariant> > DummyStore::mails() const
66{ 83{
67 return s_mailSource; 84 return s_mailSource;
68} 85}
86
87QMap<QString, QMap<QString, QVariant> > DummyStore::folders() const
88{
89 return s_folderSource;
90}