1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2015 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtLocation module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include "qdeclarativeplacecontentmodel_p.h" |
38 | #include "qdeclarativeplace_p.h" |
39 | #include "qdeclarativegeoserviceprovider_p.h" |
40 | #include "qdeclarativeplaceuser_p.h" |
41 | #include "error_messages_p.h" |
42 | |
43 | #include <QtQml/QQmlInfo> |
44 | #include <QtLocation/QGeoServiceProvider> |
45 | #include <QtLocation/QPlaceManager> |
46 | #include <QtLocation/QPlaceContentRequest> |
47 | |
48 | QT_BEGIN_NAMESPACE |
49 | |
50 | QDeclarativePlaceContentModel::QDeclarativePlaceContentModel(QPlaceContent::Type type, |
51 | QObject *parent) |
52 | : QAbstractListModel(parent), m_place(0), m_type(type), m_batchSize(1), m_contentCount(-1), |
53 | m_reply(0), m_complete(false) |
54 | { |
55 | } |
56 | |
57 | QDeclarativePlaceContentModel::~QDeclarativePlaceContentModel() |
58 | { |
59 | } |
60 | |
61 | /*! |
62 | \internal |
63 | */ |
64 | QDeclarativePlace *QDeclarativePlaceContentModel::place() const |
65 | { |
66 | return m_place; |
67 | } |
68 | |
69 | /*! |
70 | \internal |
71 | */ |
72 | void QDeclarativePlaceContentModel::setPlace(QDeclarativePlace *place) |
73 | { |
74 | if (m_place != place) { |
75 | beginResetModel(); |
76 | |
77 | int initialCount = m_contentCount; |
78 | clearData(); |
79 | m_place = place; |
80 | endResetModel(); |
81 | |
82 | emit placeChanged(); |
83 | if (initialCount != -1) |
84 | emit totalCountChanged(); |
85 | |
86 | fetchMore(parent: QModelIndex()); |
87 | } |
88 | } |
89 | |
90 | /*! |
91 | \internal |
92 | */ |
93 | int QDeclarativePlaceContentModel::batchSize() const |
94 | { |
95 | return m_batchSize; |
96 | } |
97 | |
98 | /*! |
99 | \internal |
100 | */ |
101 | void QDeclarativePlaceContentModel::setBatchSize(int batchSize) |
102 | { |
103 | if (m_batchSize != batchSize) { |
104 | m_batchSize = batchSize; |
105 | emit batchSizeChanged(); |
106 | } |
107 | } |
108 | |
109 | /*! |
110 | \internal |
111 | */ |
112 | int QDeclarativePlaceContentModel::totalCount() const |
113 | { |
114 | return m_contentCount; |
115 | } |
116 | |
117 | /*! |
118 | \internal |
119 | Clears the model data but does not reset it. |
120 | */ |
121 | void QDeclarativePlaceContentModel::clearData() |
122 | { |
123 | qDeleteAll(c: m_users); |
124 | m_users.clear(); |
125 | |
126 | qDeleteAll(c: m_suppliers); |
127 | m_suppliers.clear(); |
128 | |
129 | m_content.clear(); |
130 | |
131 | m_contentCount = -1; |
132 | |
133 | if (m_reply) { |
134 | m_reply->abort(); |
135 | m_reply->deleteLater(); |
136 | m_reply = 0; |
137 | } |
138 | |
139 | m_nextRequest.clear(); |
140 | } |
141 | |
142 | /*! |
143 | \internal |
144 | */ |
145 | void QDeclarativePlaceContentModel::initializeCollection(int totalCount, const QPlaceContent::Collection &collection) |
146 | { |
147 | beginResetModel(); |
148 | |
149 | int initialCount = m_contentCount; |
150 | clearData(); |
151 | |
152 | for (auto i = collection.cbegin(), end = collection.cend(); i != end; ++i) { |
153 | const QPlaceContent &content = i.value(); |
154 | if (content.type() != m_type) |
155 | continue; |
156 | |
157 | m_content.insert(akey: i.key(), avalue: content); |
158 | if (!m_suppliers.contains(akey: content.supplier().supplierId())) { |
159 | m_suppliers.insert(akey: content.supplier().supplierId(), |
160 | avalue: new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this)); |
161 | } |
162 | if (!m_users.contains(akey: content.user().userId())) { |
163 | m_users.insert(akey: content.user().userId(), |
164 | avalue: new QDeclarativePlaceUser(content.user(), this)); |
165 | } |
166 | } |
167 | |
168 | m_contentCount = totalCount; |
169 | |
170 | if (initialCount != totalCount) |
171 | emit totalCountChanged(); |
172 | |
173 | endResetModel(); |
174 | } |
175 | |
176 | /*! |
177 | \internal |
178 | */ |
179 | int QDeclarativePlaceContentModel::rowCount(const QModelIndex &parent) const |
180 | { |
181 | if (parent.isValid()) |
182 | return 0; |
183 | |
184 | return m_content.count(); |
185 | } |
186 | |
187 | /*! |
188 | \internal |
189 | */ |
190 | QVariant QDeclarativePlaceContentModel::data(const QModelIndex &index, int role) const |
191 | { |
192 | if (!index.isValid()) |
193 | return QVariant(); |
194 | |
195 | if (index.row() >= rowCount(parent: index.parent()) || index.row() < 0) |
196 | return QVariant(); |
197 | |
198 | const QPlaceContent &content = m_content.value(akey: index.row()); |
199 | |
200 | switch (role) { |
201 | case SupplierRole: |
202 | return QVariant::fromValue(value: static_cast<QObject *>(m_suppliers.value(akey: content.supplier().supplierId()))); |
203 | case PlaceUserRole: |
204 | return QVariant::fromValue(value: static_cast<QObject *>(m_users.value(akey: content.user().userId()))); |
205 | case AttributionRole: |
206 | return content.attribution(); |
207 | default: |
208 | return QVariant(); |
209 | } |
210 | } |
211 | |
212 | QHash<int, QByteArray> QDeclarativePlaceContentModel::roleNames() const |
213 | { |
214 | QHash<int, QByteArray> roles = QAbstractListModel::roleNames(); |
215 | roles.insert(akey: SupplierRole, avalue: "supplier" ); |
216 | roles.insert(akey: PlaceUserRole, avalue: "user" ); |
217 | roles.insert(akey: AttributionRole, avalue: "attribution" ); |
218 | return roles; |
219 | } |
220 | |
221 | /*! |
222 | \internal |
223 | */ |
224 | bool QDeclarativePlaceContentModel::canFetchMore(const QModelIndex &parent) const |
225 | { |
226 | if (parent.isValid()) |
227 | return false; |
228 | |
229 | if (!m_place) |
230 | return false; |
231 | |
232 | if (m_contentCount == -1) |
233 | return true; |
234 | |
235 | return m_content.count() != m_contentCount; |
236 | } |
237 | |
238 | /*! |
239 | \internal |
240 | */ |
241 | void QDeclarativePlaceContentModel::fetchMore(const QModelIndex &parent) |
242 | { |
243 | if (parent.isValid()) |
244 | return; |
245 | |
246 | if (!m_place) |
247 | return; |
248 | |
249 | if (m_reply) |
250 | return; |
251 | |
252 | if (!m_place->plugin()) |
253 | return; |
254 | |
255 | QDeclarativeGeoServiceProvider *plugin = m_place->plugin(); |
256 | |
257 | QGeoServiceProvider *serviceProvider = plugin->sharedGeoServiceProvider(); |
258 | if (!serviceProvider) |
259 | return; |
260 | |
261 | QPlaceManager *placeManager = serviceProvider->placeManager(); |
262 | if (!placeManager) |
263 | return; |
264 | |
265 | if (m_nextRequest == QPlaceContentRequest()) { |
266 | QPlaceContentRequest request; |
267 | request.setContentType(m_type); |
268 | request.setPlaceId(m_place->place().placeId()); |
269 | request.setLimit(m_batchSize); |
270 | |
271 | m_reply = placeManager->getPlaceContent(request); |
272 | } else { |
273 | m_reply = placeManager->getPlaceContent(request: m_nextRequest); |
274 | } |
275 | |
276 | connect(sender: m_reply, SIGNAL(finished()), receiver: this, SLOT(fetchFinished()), Qt::QueuedConnection); |
277 | } |
278 | |
279 | /*! |
280 | \internal |
281 | */ |
282 | void QDeclarativePlaceContentModel::classBegin() |
283 | { |
284 | } |
285 | |
286 | /*! |
287 | \internal |
288 | */ |
289 | void QDeclarativePlaceContentModel::componentComplete() |
290 | { |
291 | m_complete = true; |
292 | fetchMore(parent: QModelIndex()); |
293 | } |
294 | |
295 | /*! |
296 | \internal |
297 | */ |
298 | void QDeclarativePlaceContentModel::fetchFinished() |
299 | { |
300 | if (!m_reply) |
301 | return; |
302 | |
303 | QPlaceContentReply *reply = m_reply; |
304 | m_reply = 0; |
305 | |
306 | m_nextRequest = reply->nextPageRequest(); |
307 | |
308 | if (m_contentCount != reply->totalCount()) { |
309 | m_contentCount = reply->totalCount(); |
310 | emit totalCountChanged(); |
311 | } |
312 | |
313 | if (!reply->content().isEmpty()) { |
314 | QPlaceContent::Collection contents = reply->content(); |
315 | |
316 | //find out which indexes are new and which ones have changed. |
317 | QList<int> changedIndexes; |
318 | QList<int> newIndexes; |
319 | for (auto it = contents.cbegin(), end = contents.cend(); it != end; ++it) { |
320 | if (!m_content.contains(akey: it.key())) |
321 | newIndexes.append(t: it.key()); |
322 | else if (it.value() != m_content.value(akey: it.key())) |
323 | changedIndexes.append(t: it.key()); |
324 | } |
325 | |
326 | //insert new indexes in blocks where within each |
327 | //block, the indexes are consecutive. |
328 | int startIndex = -1; |
329 | for (auto it = newIndexes.cbegin(), end = newIndexes.cend(); it != end; ++it) { |
330 | int currentIndex = *it; |
331 | if (startIndex == -1) |
332 | startIndex = currentIndex; |
333 | |
334 | auto next = std::next(x: it); |
335 | if (next == end || *next > (currentIndex + 1)) { |
336 | beginInsertRows(parent: QModelIndex(),first: startIndex,last: currentIndex); |
337 | for (int i = startIndex; i <= currentIndex; ++i) { |
338 | const QPlaceContent &content = contents.value(akey: i); |
339 | |
340 | m_content.insert(akey: i, avalue: content); |
341 | if (!m_suppliers.contains(akey: content.supplier().supplierId())) { |
342 | m_suppliers.insert(akey: content.supplier().supplierId(), |
343 | avalue: new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this)); |
344 | } |
345 | if (!m_users.contains(akey: content.user().userId())) { |
346 | m_users.insert(akey: content.user().userId(), |
347 | avalue: new QDeclarativePlaceUser(content.user(), this)); |
348 | } |
349 | } |
350 | endInsertRows(); |
351 | startIndex = -1; |
352 | } |
353 | } |
354 | |
355 | //modify changed indexes in blocks where within each |
356 | //block, the indexes are consecutive. |
357 | startIndex = -1; |
358 | for (auto it = changedIndexes.cbegin(), end = changedIndexes.cend(); it != end; ++it) { |
359 | int currentIndex = *it; |
360 | if (startIndex == -1) |
361 | startIndex = currentIndex; |
362 | |
363 | auto next = std::next(x: it); |
364 | if (next == end || *next > (currentIndex + 1)) { |
365 | for (int i = startIndex; i <= currentIndex; ++i) { |
366 | const QPlaceContent &content = contents.value(akey: i); |
367 | m_content.insert(akey: i, avalue: content); |
368 | if (!m_suppliers.contains(akey: content.supplier().supplierId())) { |
369 | m_suppliers.insert(akey: content.supplier().supplierId(), |
370 | avalue: new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this)); |
371 | } |
372 | if (!m_users.contains(akey: content.user().userId())) { |
373 | m_users.insert(akey: content.user().userId(), |
374 | avalue: new QDeclarativePlaceUser(content.user(), this)); |
375 | } |
376 | } |
377 | emit dataChanged(topLeft: index(row: startIndex),bottomRight: index(row: currentIndex)); |
378 | startIndex = -1; |
379 | } |
380 | } |
381 | |
382 | // The fetch didn't add any new content and we haven't fetched all content yet. This is |
383 | // likely due to the model being prepopulated by Place::getDetails(). Keep fetching more |
384 | // data until new content is available. |
385 | if (newIndexes.isEmpty() && m_content.count() != m_contentCount) |
386 | fetchMore(parent: QModelIndex()); |
387 | } |
388 | |
389 | reply->deleteLater(); |
390 | } |
391 | |
392 | QT_END_NAMESPACE |
393 | |