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
48QT_BEGIN_NAMESPACE
49
50QDeclarativePlaceContentModel::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
57QDeclarativePlaceContentModel::~QDeclarativePlaceContentModel()
58{
59}
60
61/*!
62 \internal
63*/
64QDeclarativePlace *QDeclarativePlaceContentModel::place() const
65{
66 return m_place;
67}
68
69/*!
70 \internal
71*/
72void 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*/
93int QDeclarativePlaceContentModel::batchSize() const
94{
95 return m_batchSize;
96}
97
98/*!
99 \internal
100*/
101void QDeclarativePlaceContentModel::setBatchSize(int batchSize)
102{
103 if (m_batchSize != batchSize) {
104 m_batchSize = batchSize;
105 emit batchSizeChanged();
106 }
107}
108
109/*!
110 \internal
111*/
112int QDeclarativePlaceContentModel::totalCount() const
113{
114 return m_contentCount;
115}
116
117/*!
118 \internal
119 Clears the model data but does not reset it.
120*/
121void 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*/
145void 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*/
179int 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*/
190QVariant 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
212QHash<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*/
224bool 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*/
241void 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*/
282void QDeclarativePlaceContentModel::classBegin()
283{
284}
285
286/*!
287 \internal
288*/
289void QDeclarativePlaceContentModel::componentComplete()
290{
291 m_complete = true;
292 fetchMore(parent: QModelIndex());
293}
294
295/*!
296 \internal
297*/
298void 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
392QT_END_NAMESPACE
393

source code of qtlocation/src/location/declarativeplaces/qdeclarativeplacecontentmodel.cpp