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
QDeclarativePlaceContentModel(QPlaceContent::Type type,QObject * parent)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
~QDeclarativePlaceContentModel()57 QDeclarativePlaceContentModel::~QDeclarativePlaceContentModel()
58 {
59 }
60
61 /*!
62 \internal
63 */
place() const64 QDeclarativePlace *QDeclarativePlaceContentModel::place() const
65 {
66 return m_place;
67 }
68
69 /*!
70 \internal
71 */
setPlace(QDeclarativePlace * place)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(QModelIndex());
87 }
88 }
89
90 /*!
91 \internal
92 */
batchSize() const93 int QDeclarativePlaceContentModel::batchSize() const
94 {
95 return m_batchSize;
96 }
97
98 /*!
99 \internal
100 */
setBatchSize(int batchSize)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 */
totalCount() const112 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 */
clearData()121 void QDeclarativePlaceContentModel::clearData()
122 {
123 qDeleteAll(m_users);
124 m_users.clear();
125
126 qDeleteAll(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 */
initializeCollection(int totalCount,const QPlaceContent::Collection & collection)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(i.key(), content);
158 if (!m_suppliers.contains(content.supplier().supplierId())) {
159 m_suppliers.insert(content.supplier().supplierId(),
160 new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this));
161 }
162 if (!m_users.contains(content.user().userId())) {
163 m_users.insert(content.user().userId(),
164 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 */
rowCount(const QModelIndex & parent) const179 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 */
data(const QModelIndex & index,int role) const190 QVariant QDeclarativePlaceContentModel::data(const QModelIndex &index, int role) const
191 {
192 if (!index.isValid())
193 return QVariant();
194
195 if (index.row() >= rowCount(index.parent()) || index.row() < 0)
196 return QVariant();
197
198 const QPlaceContent &content = m_content.value(index.row());
199
200 switch (role) {
201 case SupplierRole:
202 return QVariant::fromValue(static_cast<QObject *>(m_suppliers.value(content.supplier().supplierId())));
203 case PlaceUserRole:
204 return QVariant::fromValue(static_cast<QObject *>(m_users.value(content.user().userId())));
205 case AttributionRole:
206 return content.attribution();
207 default:
208 return QVariant();
209 }
210 }
211
roleNames() const212 QHash<int, QByteArray> QDeclarativePlaceContentModel::roleNames() const
213 {
214 QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
215 roles.insert(SupplierRole, "supplier");
216 roles.insert(PlaceUserRole, "user");
217 roles.insert(AttributionRole, "attribution");
218 return roles;
219 }
220
221 /*!
222 \internal
223 */
canFetchMore(const QModelIndex & parent) const224 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 */
fetchMore(const QModelIndex & parent)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(m_nextRequest);
274 }
275
276 connect(m_reply, SIGNAL(finished()), this, SLOT(fetchFinished()), Qt::QueuedConnection);
277 }
278
279 /*!
280 \internal
281 */
classBegin()282 void QDeclarativePlaceContentModel::classBegin()
283 {
284 }
285
286 /*!
287 \internal
288 */
componentComplete()289 void QDeclarativePlaceContentModel::componentComplete()
290 {
291 m_complete = true;
292 fetchMore(QModelIndex());
293 }
294
295 /*!
296 \internal
297 */
fetchFinished()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(it.key()))
321 newIndexes.append(it.key());
322 else if (it.value() != m_content.value(it.key()))
323 changedIndexes.append(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(it);
335 if (next == end || *next > (currentIndex + 1)) {
336 beginInsertRows(QModelIndex(),startIndex,currentIndex);
337 for (int i = startIndex; i <= currentIndex; ++i) {
338 const QPlaceContent &content = contents.value(i);
339
340 m_content.insert(i, content);
341 if (!m_suppliers.contains(content.supplier().supplierId())) {
342 m_suppliers.insert(content.supplier().supplierId(),
343 new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this));
344 }
345 if (!m_users.contains(content.user().userId())) {
346 m_users.insert(content.user().userId(),
347 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(it);
364 if (next == end || *next > (currentIndex + 1)) {
365 for (int i = startIndex; i <= currentIndex; ++i) {
366 const QPlaceContent &content = contents.value(i);
367 m_content.insert(i, content);
368 if (!m_suppliers.contains(content.supplier().supplierId())) {
369 m_suppliers.insert(content.supplier().supplierId(),
370 new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this));
371 }
372 if (!m_users.contains(content.user().userId())) {
373 m_users.insert(content.user().userId(),
374 new QDeclarativePlaceUser(content.user(), this));
375 }
376 }
377 emit dataChanged(index(startIndex),index(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(QModelIndex());
387 }
388
389 reply->deleteLater();
390 }
391
392 QT_END_NAMESPACE
393