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