diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2015-11-19 23:23:56 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2015-11-19 23:23:56 +0100 |
commit | 94a2cd6ec21bf0466a9a50d6e4a0a956ed47bc82 (patch) | |
tree | 8f05e8ed03691b006b1f2bf8f08bc21e582aad26 | |
parent | c4a6746e4420b580fe35cc89783de4dbc3205ac6 (diff) | |
download | sink-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.cpp | 151 | ||||
-rw-r--r-- | common/clientapi.h | 109 | ||||
-rw-r--r-- | common/domain/applicationdomaintype.h | 2 | ||||
-rw-r--r-- | common/modelresult.cpp | 203 | ||||
-rw-r--r-- | common/modelresult.h | 167 | ||||
-rw-r--r-- | examples/dummyresource/dummystore.cpp | 22 |
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 | ||
99 | template <class DomainType> | ||
100 | QSharedPointer<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 | |||
138 | template <class DomainType> | ||
139 | QSharedPointer<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 | |||
169 | template <class DomainType> | ||
170 | static 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 | |||
178 | template <class DomainType> | ||
179 | KAsync::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 | |||
187 | template <class DomainType> | ||
188 | KAsync::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 | |||
197 | template <class DomainType> | ||
198 | KAsync::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 | |||
72 | KAsync::Job<void> Store::shutdown(const QByteArray &identifier) | 207 | KAsync::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 | |||
247 | REGISTER_TYPE(ApplicationDomain::Event); | ||
248 | REGISTER_TYPE(ApplicationDomain::Mail); | ||
249 | REGISTER_TYPE(ApplicationDomain::Folder); | ||
250 | REGISTER_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 | ||
38 | Q_DECLARE_METATYPE(std::shared_ptr<void>); | 32 | Q_DECLARE_METATYPE(std::shared_ptr<void>); |
39 | 33 | ||
34 | class QAbstractItemModel; | ||
35 | |||
40 | namespace async { | 36 | namespace 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) | |||
162 | Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Mail::Ptr) | 162 | Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Mail::Ptr) |
163 | Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Folder) | 163 | Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Folder) |
164 | Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Folder::Ptr) | 164 | Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::Folder::Ptr) |
165 | Q_DECLARE_METATYPE(Akonadi2::ApplicationDomain::AkonadiResource) | ||
166 | Q_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 | |||
27 | template<class T, class Ptr> | ||
28 | ModelResult<T, Ptr>::ModelResult(const Akonadi2::Query &query, const QList<QByteArray> &propertyColumns) | ||
29 | :QAbstractItemModel(), | ||
30 | mPropertyColumns(propertyColumns) | ||
31 | { | ||
32 | } | ||
33 | |||
34 | static qint64 getIdentifier(const QModelIndex &idx) | ||
35 | { | ||
36 | if (!idx.isValid()) { | ||
37 | return 0; | ||
38 | } | ||
39 | return idx.internalId(); | ||
40 | } | ||
41 | |||
42 | template<class T, class Ptr> | ||
43 | qint64 ModelResult<T, Ptr>::parentId(const Ptr &value) | ||
44 | { | ||
45 | return qHash(value->getProperty("parent").toByteArray()); | ||
46 | } | ||
47 | |||
48 | template<class T, class Ptr> | ||
49 | int ModelResult<T, Ptr>::rowCount(const QModelIndex &parent) const | ||
50 | { | ||
51 | return mTree[getIdentifier(parent)].size(); | ||
52 | } | ||
53 | |||
54 | template<class T, class Ptr> | ||
55 | int ModelResult<T, Ptr>::columnCount(const QModelIndex &parent) const | ||
56 | { | ||
57 | return mPropertyColumns.size(); | ||
58 | } | ||
59 | |||
60 | template<class T, class Ptr> | ||
61 | QVariant 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 | |||
79 | template<class T, class Ptr> | ||
80 | QModelIndex 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 | |||
87 | template<class T, class Ptr> | ||
88 | QModelIndex 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 | |||
95 | template<class T, class Ptr> | ||
96 | QModelIndex 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 | |||
103 | template<class T, class Ptr> | ||
104 | bool 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 | |||
110 | template<class T, class Ptr> | ||
111 | void ModelResult<T, Ptr>::fetchMore(const QModelIndex &parent) | ||
112 | { | ||
113 | qDebug() << "Fetch more: " << parent; | ||
114 | fetchEntities(parent); | ||
115 | } | ||
116 | |||
117 | template<class T, class Ptr> | ||
118 | void 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 | |||
149 | template<class T, class Ptr> | ||
150 | void 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 | |||
165 | template<class T, class Ptr> | ||
166 | void 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 | |||
174 | template<class T, class Ptr> | ||
175 | void ModelResult<T, Ptr>::setFetcher(const std::function<void(const Ptr &parent)> &fetcher) | ||
176 | { | ||
177 | Trace() << "Setting fetcher"; | ||
178 | loadEntities = fetcher; | ||
179 | } | ||
180 | |||
181 | template<class T, class Ptr> | ||
182 | void 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 | |||
200 | template class ModelResult<Akonadi2::ApplicationDomain::Folder, Akonadi2::ApplicationDomain::Folder::Ptr>; | ||
201 | template class ModelResult<Akonadi2::ApplicationDomain::Mail, Akonadi2::ApplicationDomain::Mail::Ptr>; | ||
202 | template class ModelResult<Akonadi2::ApplicationDomain::Event, Akonadi2::ApplicationDomain::Event::Ptr>; | ||
203 | template 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 | |||
30 | template<class T, class Ptr> | 29 | template<class T, class Ptr> |
31 | class ModelResult : public QAbstractItemModel | 30 | class 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 | ||
192 | private: | 55 | private: |
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 | ||
39 | static 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 | |||
39 | QMap<QString, QMap<QString, QVariant> > populateEvents() | 46 | QMap<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 | ||
64 | QMap<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 | |||
57 | static QMap<QString, QMap<QString, QVariant> > s_eventSource = populateEvents(); | 73 | static QMap<QString, QMap<QString, QVariant> > s_eventSource = populateEvents(); |
58 | static QMap<QString, QMap<QString, QVariant> > s_mailSource = populateMails(); | 74 | static QMap<QString, QMap<QString, QVariant> > s_mailSource = populateMails(); |
75 | static QMap<QString, QMap<QString, QVariant> > s_folderSource = populateFolders(); | ||
59 | 76 | ||
60 | QMap<QString, QMap<QString, QVariant> > DummyStore::events() const | 77 | QMap<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 | |||
87 | QMap<QString, QMap<QString, QVariant> > DummyStore::folders() const | ||
88 | { | ||
89 | return s_folderSource; | ||
90 | } | ||