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