Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdeclarativeplacecontentmodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
8
9#include <QtQml/QQmlInfo>
10#include <QtLocation/QGeoServiceProvider>
11#include <QtLocation/QPlaceManager>
12#include <QtLocation/QPlaceContentRequest>
13#include <QtLocation/QPlaceUser>
14
16
17/*!
18 \qmltype EditorialModel
19 \inqmlmodule QtLocation
20 \ingroup qml-QtLocation5-places
21 \ingroup qml-QtLocation5-places-models
22 \since QtLocation 5.5
23
24 \brief The EditorialModel type provides a model of place editorials.
25
26 The EditorialModel is a read-only model used to fetch editorials related to a \l Place.
27 Binding a \l Place via \l EditorialModel::place initiates an initial fetch of editorials.
28 The model performs fetches incrementally and is intended to be used in conjunction
29 with a View such as a \l ListView. When the View reaches the last of the editorials
30 currently in the model, a fetch is performed to retrieve more if they are available.
31 The View is automatically updated as the editorials are received. The number of
32 editorials which are fetched at a time is specified by the
33 \l {EditorialModel::}{batchSize} property. The total number of editorials available can be
34 accessed via the \l {EditorialModel::}{totalCount} property.
35
36 The model returns data for the following roles:
37
38 \table
39 \header
40 \li Role
41 \li Type
42 \li Description
43 \row
44 \li supplier
45 \li \l supplier
46 \li The supplier of the content.
47 \row
48 \li user
49 \li \l user
50 \li The user who contributed the content.
51 \row
52 \li attribution
53 \li string
54 \li Attribution text which must be displayed when displaying the content.
55 \row
56 \li url
57 \li url
58 \li The URL of the image.
59 \row
60 \li imageId
61 \li string
62 \li The identifier of the image.
63 \row
64 \li mimeType
65 \li string
66 \li The MIME type of the image.
67 \row
68 \li text
69 \li string
70 \li The editorial's textual description of the place. It can be either rich (HTML based) text or plain text
71 depending upon the provider.
72 \row
73 \li title
74 \li string
75 \li The title of the editorial.
76 \row
77 \li language
78 \li string
79 \li The language that the editorial is written in.
80 \row
81 \li dateTime
82 \li datetime
83 \li The date and time that the review was posted.
84 \row
85 \li text
86 \li string
87 \li The review's textual description of the place. It can be either rich (HTML based) text or plain text
88 depending on the provider.
89 \row
90 \li language
91 \li string
92 \li The language that the review is written in.
93 \row
94 \li rating
95 \li real
96 \li The rating that the reviewer gave to the place.
97 \row
98 \li reviewId
99 \li string
100 \li The identifier of the review.
101 \row
102 \li title
103 \li string
104 \li The title of the review.
105 \endtable
106
107 \section1 Example
108
109 The following example shows how to display editorials for a place:
110
111 \snippet declarative/places.qml QtQuick import
112 \snippet declarative/maps.qml QtLocation import
113 \codeline
114 \snippet declarative/places.qml EditorialModel
115
116*/
117
118/*!
119 \qmlproperty Place EditorialModel::place
120
121 This property holds the Place that the editorials are for.
122*/
123
124/*!
125 \qmlproperty int EditorialModel::batchSize
126
127 This property holds the batch size to use when fetching more editorials items.
128*/
129
130/*!
131 \qmlproperty int EditorialModel::totalCount
132
133 This property holds the total number of editorial items for the place.
134*/
135
136/*!
137 \qmltype ImageModel
138 \inqmlmodule QtLocation
139 \ingroup qml-QtLocation5-places
140 \ingroup qml-QtLocation5-places-models
141 \since QtLocation 5.5
142
143 \brief The ImageModel type provides a model of place images.
144
145 \sa EditorialModel
146*/
147
148/*!
149 \qmltype ReviewModel
150 \inqmlmodule QtLocation
151 \ingroup qml-QtLocation5-places
152 \ingroup qml-QtLocation5-places-models
153 \since QtLocation 5.5
154
155 \brief The ReviewModel type provides a model of place reviews.
156
157 \sa EditorialModel
158*/
159
160QDeclarativePlaceContentModel::QDeclarativePlaceContentModel(QPlaceContent::Type type,
161 QObject *parent)
162 : QAbstractListModel(parent), m_type(type)
163{
164}
165
166QDeclarativePlaceContentModel::~QDeclarativePlaceContentModel()
167{
168}
169
170/*!
171 \internal
172*/
173QDeclarativePlace *QDeclarativePlaceContentModel::place() const
174{
175 return m_place;
176}
177
178/*!
179 \internal
180*/
181void QDeclarativePlaceContentModel::setPlace(QDeclarativePlace *place)
182{
183 if (m_place != place) {
184 beginResetModel();
185
186 int initialCount = m_contentCount;
187 clearData();
188 m_place = place;
189 endResetModel();
190
191 emit placeChanged();
192 if (initialCount != -1)
193 emit totalCountChanged();
194
195 fetchMore(QModelIndex());
196 }
197}
198
199/*!
200 \internal
201*/
202int QDeclarativePlaceContentModel::batchSize() const
203{
204 return m_batchSize;
205}
206
207/*!
208 \internal
209*/
210void QDeclarativePlaceContentModel::setBatchSize(int batchSize)
211{
212 if (m_batchSize != batchSize) {
213 m_batchSize = batchSize;
214 emit batchSizeChanged();
215 }
216}
217
218/*!
219 \internal
220*/
221int QDeclarativePlaceContentModel::totalCount() const
222{
223 return m_contentCount;
224}
225
226/*!
227 \internal
228 Clears the model data but does not reset it.
229*/
230void QDeclarativePlaceContentModel::clearData()
231{
232 m_users.clear();
233 m_suppliers.clear();
234 m_content.clear();
235
236 m_contentCount = -1;
237
238 if (m_reply) {
239 m_reply->abort();
240 m_reply->deleteLater();
241 m_reply = nullptr;
242 }
243
244 m_nextRequest.clear();
245}
246
247/*!
248 \internal
249*/
250void QDeclarativePlaceContentModel::initializeCollection(int totalCount, const QPlaceContent::Collection &collection)
251{
252 beginResetModel();
253
254 int initialCount = m_contentCount;
255 clearData();
256
257 for (auto i = collection.cbegin(), end = collection.cend(); i != end; ++i) {
258 const QPlaceContent &content = i.value();
259 if (content.type() != m_type)
260 continue;
261
262 m_content.insert(i.key(), content);
263 const auto supplier = content.value(QPlaceContent::ContentSupplier)
264 .value<QPlaceSupplier>();
265 if (!m_suppliers.contains(supplier.supplierId()))
266 m_suppliers.insert(supplier.supplierId(), supplier);
267 const auto user = content.value(QPlaceContent::ContentUser)
268 .value<QPlaceUser>();
269 if (!m_users.contains(user.userId()))
270 m_users.insert(user.userId(), user);
271 }
272
273 m_contentCount = totalCount;
274
275 if (initialCount != totalCount)
276 emit totalCountChanged();
277
278 endResetModel();
279}
280
281/*!
282 \internal
283*/
284int QDeclarativePlaceContentModel::rowCount(const QModelIndex &parent) const
285{
286 if (parent.isValid())
287 return 0;
288
289 return m_content.count();
290}
291
292/*!
293 \internal
294*/
295QVariant QDeclarativePlaceContentModel::data(const QModelIndex &index, int role) const
296{
297 if (!index.isValid())
298 return QVariant();
299
300 if (index.row() >= rowCount(index.parent()) || index.row() < 0)
301 return QVariant();
302
303 const QPlaceContent &content = m_content.value(index.row());
304 if (content.type() != m_type)
305 return QVariant();
306
307 switch (role) {
308 case ContentSupplierRole:
309 return QVariant::fromValue(m_suppliers.value(content.value(QPlaceContent::ContentSupplier)
310 .value<QPlaceSupplier>().supplierId()));
311 case ContentUserRole:
312 return QVariant::fromValue(m_users.value(content.value(QPlaceContent::ContentUser)
313 .value<QPlaceUser>().userId()));
314 case ContentAttributionRole:
315 return content.value(QPlaceContent::ContentAttribution);
316 case ImageUrlRole:
317 return content.value(QPlaceContent::ImageUrl);
318 case ImageIdRole:
319 return content.value(QPlaceContent::ImageId);
320 case ImageMimeTypeRole:
321 return content.value(QPlaceContent::ImageMimeType);
322
323 case EditorialTextRole:
324 return content.value(QPlaceContent::EditorialText);
325 case EditorialTitleRole:
326 return content.value(QPlaceContent::EditorialTitle);
327 case EditorialLanguageRole:
328 return content.value(QPlaceContent::EditorialLanguage);
329
330 case ReviewDateTimeRole:
331 return content.value(QPlaceContent::ReviewDateTime);
332 case ReviewTextRole:
333 return content.value(QPlaceContent::ReviewText);
334 case ReviewLanguageRole:
335 return content.value(QPlaceContent::ReviewLanguage);
336 case ReviewRatingRole:
337 return content.value(QPlaceContent::ReviewRating);
338 case ReviewIdRole:
339 return content.value(QPlaceContent::ReviewId);
340 case ReviewTitleRole:
341 return content.value(QPlaceContent::ReviewTitle);
342 default:
343 return QVariant();
344 }
345}
346
347QHash<int, QByteArray> QDeclarativePlaceContentModel::roleNames() const
348{
349 QHash<int, QByteArray> roles = QAbstractListModel::roleNames();
350 roles.insert(ContentSupplierRole, "supplier");
351 roles.insert(ContentUserRole, "user");
352 roles.insert(ContentAttributionRole, "attribution");
353
354 switch (m_type) {
355 case QPlaceContent::EditorialType:
356 roles.insert(EditorialTextRole, "text");
357 roles.insert(EditorialTitleRole, "title");
358 roles.insert(EditorialLanguageRole, "language");
359 break;
360 case QPlaceContent::ImageType:
361 roles.insert(ImageUrlRole, "url");
362 roles.insert(ImageIdRole, "imageId");
363 roles.insert(ImageMimeTypeRole, "mimeType");
364 break;
365 case QPlaceContent::ReviewType:
366 roles.insert(ReviewDateTimeRole, "dateTime");
367 roles.insert(ReviewTextRole, "text");
368 roles.insert(ReviewLanguageRole, "language");
369 roles.insert(ReviewRatingRole, "rating");
370 roles.insert(ReviewIdRole, "reviewId");
371 roles.insert(ReviewTitleRole, "title");
372 break;
373 default:
374 break;
375 }
376 return roles;
377}
378
379/*!
380 \internal
381*/
382bool QDeclarativePlaceContentModel::canFetchMore(const QModelIndex &parent) const
383{
384 if (parent.isValid())
385 return false;
386
387 if (!m_place)
388 return false;
389
390 if (m_contentCount == -1)
391 return true;
392
393 return m_content.count() != m_contentCount;
394}
395
396/*!
397 \internal
398*/
399void QDeclarativePlaceContentModel::fetchMore(const QModelIndex &parent)
400{
401 if (parent.isValid())
402 return;
403
404 if (!m_place)
405 return;
406
407 if (m_reply)
408 return;
409
410 if (!m_place->plugin())
411 return;
412
413 QDeclarativeGeoServiceProvider *plugin = m_place->plugin();
414
415 QGeoServiceProvider *serviceProvider = plugin->sharedGeoServiceProvider();
416 if (!serviceProvider)
417 return;
418
419 QPlaceManager *placeManager = serviceProvider->placeManager();
420 if (!placeManager)
421 return;
422
423 if (m_nextRequest == QPlaceContentRequest()) {
424 QPlaceContentRequest request;
425 request.setContentType(m_type);
426 request.setPlaceId(m_place->place().placeId());
427 request.setLimit(m_batchSize);
428
429 m_reply = placeManager->getPlaceContent(request);
430 } else {
431 m_reply = placeManager->getPlaceContent(m_nextRequest);
432 }
433
434 connect(m_reply, &QPlaceReply::finished,
435 this, &QDeclarativePlaceContentModel::fetchFinished, Qt::QueuedConnection);
436}
437
438/*!
439 \internal
440*/
441void QDeclarativePlaceContentModel::classBegin()
442{
443}
444
445/*!
446 \internal
447*/
448void QDeclarativePlaceContentModel::componentComplete()
449{
450 m_complete = true;
451 fetchMore(QModelIndex());
452}
453
454/*!
455 \internal
456*/
457void QDeclarativePlaceContentModel::fetchFinished()
458{
459 if (!m_reply)
460 return;
461
462 QPlaceContentReply *reply = m_reply;
463 m_reply = nullptr;
464
465 m_nextRequest = reply->nextPageRequest();
466
467 if (m_contentCount != reply->totalCount()) {
468 m_contentCount = reply->totalCount();
469 emit totalCountChanged();
470 }
471
472 if (!reply->content().isEmpty()) {
473 QPlaceContent::Collection contents = reply->content();
474
475 //find out which indexes are new and which ones have changed.
476 QList<int> changedIndexes;
477 QList<int> newIndexes;
478 for (auto it = contents.cbegin(), end = contents.cend(); it != end; ++it) {
479 if (!m_content.contains(it.key()))
480 newIndexes.append(it.key());
481 else if (it.value() != m_content.value(it.key()))
482 changedIndexes.append(it.key());
483 }
484
485 //insert new indexes in blocks where within each
486 //block, the indexes are consecutive.
487 int startIndex = -1;
488 for (auto it = newIndexes.cbegin(), end = newIndexes.cend(); it != end; ++it) {
489 int currentIndex = *it;
490 if (startIndex == -1)
491 startIndex = currentIndex;
492
493 auto next = std::next(it);
494 if (next == end || *next > (currentIndex + 1)) {
495 beginInsertRows(QModelIndex(),startIndex,currentIndex);
496 for (int i = startIndex; i <= currentIndex; ++i) {
497 const QPlaceContent &content = contents.value(i);
498
499 m_content.insert(i, content);
500 const auto supplier = content.value(QPlaceContent::ContentSupplier)
501 .value<QPlaceSupplier>();
502 if (!m_suppliers.contains(supplier.supplierId()))
503 m_suppliers.insert(supplier.supplierId(), supplier);
504 const auto user = content.value(QPlaceContent::ContentUser)
505 .value<QPlaceUser>();
506 if (!m_users.contains(user.userId()))
507 m_users.insert(user.userId(), user);
508 }
509 endInsertRows();
510 startIndex = -1;
511 }
512 }
513
514 //modify changed indexes in blocks where within each
515 //block, the indexes are consecutive.
516 startIndex = -1;
517 for (auto it = changedIndexes.cbegin(), end = changedIndexes.cend(); it != end; ++it) {
518 int currentIndex = *it;
519 if (startIndex == -1)
520 startIndex = currentIndex;
521
522 auto next = std::next(it);
523 if (next == end || *next > (currentIndex + 1)) {
524 for (int i = startIndex; i <= currentIndex; ++i) {
525 const QPlaceContent &content = contents.value(i);
526 m_content.insert(i, content);
527 const auto supplier = content.value(QPlaceContent::ContentSupplier)
528 .value<QPlaceSupplier>();
529 if (!m_suppliers.contains(supplier.supplierId()))
530 m_suppliers.insert(supplier.supplierId(), supplier);
531 const auto user = content.value(QPlaceContent::ContentUser)
532 .value<QPlaceUser>();
533 if (!m_users.contains(user.userId()))
534 m_users.insert(user.userId(), user);
535 }
536 emit dataChanged(index(startIndex),index(currentIndex));
537 startIndex = -1;
538 }
539 }
540
541 // The fetch didn't add any new content and we haven't fetched all content yet. This is
542 // likely due to the model being prepopulated by Place::getDetails(). Keep fetching more
543 // data until new content is available.
544 if (newIndexes.isEmpty() && m_content.count() != m_contentCount)
545 fetchMore(QModelIndex());
546 }
547
548 reply->deleteLater();
549}
550
551QT_END_NAMESPACE