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
qdeclarativeplace.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 <QtCore/QCoreApplication>
10#include <QtCore/QMetaObject>
11#include <QtQml/QQmlEngine>
12#include <QtQml/QQmlInfo>
13#include <QtLocation/QGeoServiceProvider>
14#include <QtLocation/QPlaceAttribute>
15#include <QtLocation/QPlaceManager>
16#include <QtLocation/QPlaceDetailsReply>
17#include <QtLocation/QPlaceReply>
18#include <QtLocation/QPlaceIdReply>
19#include <QtLocation/QPlaceContactDetail>
20
22
23/*!
24 \qmltype Place
25 \nativetype QDeclarativePlace
26 \inqmlmodule QtLocation
27 \ingroup qml-QtLocation5-places
28 \ingroup qml-QtLocation5-places-data
29 \since QtLocation 5.5
30
31 \brief The Place type represents a location that is a position of interest.
32
33 The Place type represents a physical location with additional metadata describing that
34 location. Contrasted with \l Location, \l Address, and
35 \l {coordinate} type which are used to describe where a location is.
36 The basic properties of a Place are its \l name and \l location.
37
38 Place objects are typically obtained from a search model and will generally only have their
39 basic properties set. The \l detailsFetched property can be used to test if further property
40 values need to be fetched from the \l Plugin. This can be done by invoking the \l getDetails()
41 method. Progress of the fetching operation can be monitored with the \l status property, which
42 will be set to Place.Fetching when the details are being fetched.
43
44 The Place type has many properties holding information about the location. Details on how to
45 contact the place are available from the \l contactDetails property. Convenience properties
46 for obtaining the primary \l {primaryPhone}{phone}, \l {primaryFax}{fax},
47 \l {primaryEmail}{email} and \l {primaryWebsite}{website} are also available.
48
49 Each place is assigned zero or more \l categories. Categories are typically used when
50 searching for a particular kind of place, such as a restaurant or hotel. Some places have a
51 \l ratings object, which gives an indication of the quality of the place.
52
53 Place metadata is provided by a \l supplier who may require that an \l attribution message be
54 displayed to the user when the place details are viewed.
55
56 Places have an associated \l icon which can be used to represent a place on a map or to
57 decorate a delegate in a view.
58
59 Places may have additional rich content associated with them. The currently supported rich
60 content include editorial descriptions, reviews and images. These are exposed as a set of
61 models for retrieving the content. Editorial descriptions of the place are available from the
62 \l editorialModel property. Reviews of the place are available from the \l reviewModel
63 property. A gallery of pictures of the place can be accessed using the \l imageModel property.
64
65 Places may have additional attributes which are not covered in the formal API. The
66 \l extendedAttributes property provides access to these. The type of extended attributes
67 available is specific to each \l Plugin.
68
69 A Place is almost always tied to a \l plugin. The \l plugin property must be set before it is
70 possible to call \l save(), \l remove() or \l getDetails(). The \l reviewModel, \l imageModel
71 and \l editorialModel are only valid then the \l plugin property is set.
72
73 \section2 Saving a Place
74
75 If the \l Plugin supports it, the Place type can be used to save a place. First create a new
76 Place and set its properties:
77
78 \snippet declarative/places.qml Place savePlace def
79
80 Then invoke the \l save() method:
81
82 \snippet declarative/places.qml Place savePlace
83
84 The \l status property will change to Place.Saving and then to Place.Ready if the save was
85 successful or to Place.Error if an error occurs.
86
87 If the \l placeId property is set, the backend will update an existing place otherwise it will
88 create a new place. On success the \l placeId property will be updated with the identifier of the newly
89 saved place.
90
91 \section3 Caveats
92 \input place-caveats.qdocinc
93
94 \section3 Saving Between Plugins
95 When saving places between plugins, there are a few things to be aware of.
96 Some fields of a place such as the id, categories and icons are plugin specific entities. For example
97 the categories in one manager may not be recognised in another.
98 Therefore trying to save a place directly from one plugin to another is not possible.
99
100 It is generally recommended that saving across plugins be handled as saving \l {Favorites}{favorites}
101 as explained in the Favorites section. However there is another approach which is to create a new place,
102 set its (destination) plugin and then use the \l copyFrom() method to copy the details of the original place.
103 Using \l copyFrom() only copies data that is supported by the destination plugin,
104 plugin specific data such as the place identifier is not copied over. Once the copy is done,
105 the place is in a suitable state to be saved.
106
107 The following snippet provides an example of saving a place to a different plugin
108 using the \l copyFrom method:
109
110 \snippet declarative/places.qml Place save to different plugin
111
112 \section2 Removing a Place
113
114 To remove a place, ensure that a Place object with a valid \l placeId property exists and call
115 its \l remove() method. The \l status property will change to Place.Removing and then to
116 Place.Ready if the save was successful or to Place.Error if an error occurs.
117
118 \section2 Favorites
119 The Places API supports the concept of favorites. Favorites are generally implemented
120 by using two plugins, the first plugin is typically a read-only source of places (origin plugin) and a second
121 read/write plugin (destination plugin) is used to store places from the origin as favorites.
122
123 Each Place has a favorite property which is intended to contain the corresponding place
124 from the destination plugin (the place itself is sourced from the origin plugin). Because both the original
125 place and favorite instances are available, the developer can choose which
126 properties to show to the user. For example the favorite may have a modified name which should
127 be displayed rather than the original name.
128
129 \snippet declarative/places.qml Place favorite
130
131 The following demonstrates how to save a new favorite instance. A call is made
132 to create/initialize the favorite instance and then the instance is saved.
133
134 \snippet declarative/places.qml Place saveFavorite
135
136 The following demonstrates favorite removal:
137
138 \snippet declarative/places.qml Place removeFavorite 1
139 \dots
140 \snippet declarative/places.qml Place removeFavorite 2
141
142 The PlaceSearchModel has a favoritesPlugin property. If the property is set, any places found
143 during a search are checked against the favoritesPlugin to see if there is a corresponding
144 favorite place. If so, the favorite property of the Place is set, otherwise the favorite
145 property is remains null.
146
147 \sa PlaceSearchModel
148*/
149
150QDeclarativePlace::QDeclarativePlace(QObject *parent)
151: QObject(parent),
152 m_extendedAttributes(new QQmlPropertyMap(this)),
153 m_contactDetails(new QDeclarativeContactDetails(this))
154{
155 connect(m_contactDetails, &QDeclarativeContactDetails::valueChanged,
156 this, &QDeclarativePlace::contactsModified);
157
158 setPlace(QPlace());
159}
160
161QDeclarativePlace::QDeclarativePlace(const QPlace &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent)
162: QObject(parent),
163 m_extendedAttributes(new QQmlPropertyMap(this)),
164 m_contactDetails(new QDeclarativeContactDetails(this)),
165 m_plugin(plugin)
166{
167 Q_ASSERT(plugin);
168
169 connect(m_contactDetails, &QDeclarativeContactDetails::valueChanged,
170 this, &QDeclarativePlace::contactsModified);
171
172 setPlace(src);
173}
174
175QDeclarativePlace::~QDeclarativePlace()
176{
177}
178
179// From QQmlParserStatus
180void QDeclarativePlace::componentComplete()
181{
182 m_complete = true;
183}
184
185/*!
186 \qmlproperty Plugin Place::plugin
187
188 This property holds the \l Plugin that provided this place which can be used to retrieve more information about the service.
189*/
190void QDeclarativePlace::setPlugin(QDeclarativeGeoServiceProvider *plugin)
191{
192 if (m_plugin == plugin)
193 return;
194
195 m_plugin = plugin;
196 if (m_complete)
197 emit pluginChanged();
198
199 if (m_plugin->isAttached()) {
200 pluginReady();
201 } else {
202 connect(m_plugin, &QDeclarativeGeoServiceProvider::attached,
203 this, &QDeclarativePlace::pluginReady);
204 }
205}
206
207void QDeclarativePlace::pluginReady()
208{
209 QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider();
210 QPlaceManager *placeManager = serviceProvider->placeManager();
211 if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) {
212 setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR)
213 .arg(m_plugin->name()).arg(serviceProvider->errorString()));
214 return;
215 }
216}
217
218QDeclarativeGeoServiceProvider *QDeclarativePlace::plugin() const
219{
220 return m_plugin;
221}
222
223/*!
224 \qmlproperty ReviewModel Place::reviewModel
225
226 This property holds a model which can be used to retrieve reviews about the place.
227*/
228QDeclarativePlaceReviewModel *QDeclarativePlace::reviewModel()
229{
230 if (!m_reviewModel) {
231 m_reviewModel = new QDeclarativePlaceReviewModel(this);
232 m_reviewModel->setPlace(this);
233 }
234
235 return m_reviewModel;
236}
237
238/*!
239 \qmlproperty ImageModel Place::imageModel
240
241 This property holds a model which can be used to retrieve images of the place.
242*/
243QDeclarativePlaceImageModel *QDeclarativePlace::imageModel()
244{
245 if (!m_imageModel) {
246 m_imageModel = new QDeclarativePlaceImageModel(this);
247 m_imageModel->setPlace(this);
248 }
249
250 return m_imageModel;
251}
252
253/*!
254 \qmlproperty EditorialModel Place::editorialModel
255
256 This property holds a model which can be used to retrieve editorial descriptions of the place.
257*/
258QDeclarativePlaceEditorialModel *QDeclarativePlace::editorialModel()
259{
260 if (!m_editorialModel) {
261 m_editorialModel = new QDeclarativePlaceEditorialModel(this);
262 m_editorialModel->setPlace(this);
263 }
264
265 return m_editorialModel;
266}
267
268/*!
269 \internal
270*/
271void QDeclarativePlace::setPlace(const QPlace &src)
272{
273 QPlace previous = m_src;
274 m_src = src;
275
276 if (previous.categories() != m_src.categories()) {
277 synchronizeCategories();
278 emit categoriesChanged();
279 }
280
281 if (m_location && m_location->parent() == this) {
282 m_location->setLocation(m_src.location());
283 } else if (!m_location || m_location->parent() != this) {
284 m_location = new QDeclarativeGeoLocation(m_src.location(), this);
285 emit locationChanged();
286 }
287
288 if (previous.ratings() != m_src.ratings())
289 emit ratingsChanged();
290 if (previous.supplier() != m_src.supplier())
291 emit supplierChanged();
292 if (previous.icon() != m_src.icon())
293 emit iconChanged();
294 if (previous.name() != m_src.name())
295 emit nameChanged();
296 if (previous.placeId() != m_src.placeId())
297 emit placeIdChanged();
298 if (previous.attribution() != m_src.attribution())
299 emit attributionChanged();
300 if (previous.detailsFetched() != m_src.detailsFetched())
301 emit detailsFetchedChanged();
302 if (previous.primaryPhone() != m_src.primaryPhone())
303 emit primaryPhoneChanged();
304 if (previous.primaryFax() != m_src.primaryFax())
305 emit primaryFaxChanged();
306 if (previous.primaryEmail() != m_src.primaryEmail())
307 emit primaryEmailChanged();
308 if (previous.primaryWebsite() != m_src.primaryWebsite())
309 emit primaryWebsiteChanged();
310
311 if (m_reviewModel && m_src.totalContentCount(QPlaceContent::ReviewType) >= 0) {
312 m_reviewModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ReviewType),
313 m_src.content(QPlaceContent::ReviewType));
314 }
315 if (m_imageModel && m_src.totalContentCount(QPlaceContent::ImageType) >= 0) {
316 m_imageModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ImageType),
317 m_src.content(QPlaceContent::ImageType));
318 }
319 if (m_editorialModel && m_src.totalContentCount(QPlaceContent::EditorialType) >= 0) {
320 m_editorialModel->initializeCollection(m_src.totalContentCount(QPlaceContent::EditorialType),
321 m_src.content(QPlaceContent::EditorialType));
322 }
323
324 pullExtendedAttributes();
325 synchronizeContacts();
326}
327
328QPlace QDeclarativePlace::place() const
329{
330 // The properties handled explicirly here are not stored in m_src, but
331 // but are instead stored in QDeclarative* objects which we need to update
332 // explicitly.
333
334 QPlace result = m_src;
335
336 // Categories
337 QList<QPlaceCategory> categories;
338 for (QDeclarativeCategory *value : std::as_const(m_categories))
339 categories.append(value->category());
340
341 result.setCategories(categories);
342
343 // Location
344 result.setLocation(m_location ? m_location->location() : QGeoLocation());
345
346 //contact details
347 QList<QPlaceContactDetail> cppDetails;
348 for (const QString &key : m_contactDetails->keys()) {
349 cppDetails.clear();
350 if (m_contactDetails->value(key).typeId() == QMetaType::QVariantList) {
351 const QVariantList detailsVarList = m_contactDetails->value(key).toList();
352 for (const QVariant &detailVar : detailsVarList)
353 cppDetails.append(detailVar.value<QPlaceContactDetail>());
354 } else {
355 cppDetails.append(m_contactDetails->value(key).value<QPlaceContactDetail>());
356 }
357 result.setContactDetails(key, cppDetails);
358 }
359
360 return result;
361}
362
363/*!
364 \qmlproperty QtPositioning::Location Place::location
365
366 This property holds the location of the place which can be used to retrieve the coordinate,
367 address and the bounding box.
368*/
369void QDeclarativePlace::setLocation(QDeclarativeGeoLocation *location)
370{
371 if (m_location == location)
372 return;
373
374 if (m_location && m_location->parent() == this)
375 delete m_location;
376
377 m_location = location;
378 emit locationChanged();
379}
380
381QDeclarativeGeoLocation *QDeclarativePlace::location() const
382{
383 return m_location;
384}
385
386/*!
387 \qmlproperty Ratings Place::ratings
388
389 This property holds ratings of the place. The ratings provide an indication of the quality of a
390 place.
391*/
392void QDeclarativePlace::setRatings(const QPlaceRatings &rating)
393{
394 if (m_src.ratings() != rating) {
395 m_src.setRatings(rating);
396 emit ratingsChanged();
397 }
398}
399
400QPlaceRatings QDeclarativePlace::ratings() const
401{
402 return m_src.ratings();
403}
404
405/*!
406 \qmlproperty Supplier Place::supplier
407
408 This property holds the supplier of the place data.
409 The supplier is typically a business or organization that collected the data about the place.
410*/
411void QDeclarativePlace::setSupplier(const QPlaceSupplier &supplier)
412{
413 if (m_src.supplier() != supplier) {
414 m_src.setSupplier(supplier);
415 emit supplierChanged();
416 }
417}
418
419QPlaceSupplier QDeclarativePlace::supplier() const
420{
421 return m_src.supplier();
422}
423
424/*!
425 \qmlproperty Icon Place::icon
426
427 This property holds a graphical icon which can be used to represent the place.
428*/
429QPlaceIcon QDeclarativePlace::icon() const
430{
431 return m_src.icon();
432}
433
434void QDeclarativePlace::setIcon(const QPlaceIcon &icon)
435{
436 if (m_src.icon() != icon) {
437 m_src.setIcon(icon);
438 emit iconChanged();
439 }
440}
441
442/*!
443 \qmlproperty string Place::name
444
445 This property holds the name of the place which can be used to represent the place.
446*/
447void QDeclarativePlace::setName(const QString &name)
448{
449 if (m_src.name() != name) {
450 m_src.setName(name);
451 emit nameChanged();
452 }
453}
454
455QString QDeclarativePlace::name() const
456{
457 return m_src.name();
458}
459
460/*!
461 \qmlproperty string Place::placeId
462
463 This property holds the unique identifier of the place. The place identifier is only meaningful to the
464 \l Plugin that generated it and is not transferable between \l {Plugin}{Plugins}. The place id
465 is not guaranteed to be universally unique, but unique within the \l Plugin that generated it.
466
467 If only the place identifier is known, all other place data can fetched from the \l Plugin.
468
469 \snippet declarative/places.qml Place placeId
470*/
471void QDeclarativePlace::setPlaceId(const QString &placeId)
472{
473 if (m_src.placeId() != placeId) {
474 m_src.setPlaceId(placeId);
475 emit placeIdChanged();
476 }
477}
478
479QString QDeclarativePlace::placeId() const
480{
481 return m_src.placeId();
482}
483
484/*!
485 \qmlproperty string Place::attribution
486
487 This property holds a rich text attribution string for the place.
488 Some providers may require that the attribution be shown to the user
489 whenever a place is displayed. The contents of this property should
490 be shown to the user if it is not empty.
491*/
492void QDeclarativePlace::setAttribution(const QString &attribution)
493{
494 if (m_src.attribution() != attribution) {
495 m_src.setAttribution(attribution);
496 emit attributionChanged();
497 }
498}
499
500QString QDeclarativePlace::attribution() const
501{
502 return m_src.attribution();
503}
504
505/*!
506 \qmlproperty bool Place::detailsFetched
507
508 This property indicates whether the details of the place have been fetched. If this property
509 is false, the place details have not yet been fetched. Fetching can be done by invoking the
510 \l getDetails() method.
511
512 \sa getDetails()
513*/
514bool QDeclarativePlace::detailsFetched() const
515{
516 return m_src.detailsFetched();
517}
518
519/*!
520 \qmlproperty enumeration Place::status
521
522 This property holds the status of the place. It can be one of:
523
524 \table
525 \row
526 \li Place.Ready
527 \li No error occurred during the last operation, further operations may be performed on
528 the place.
529 \row
530 \li Place.Saving
531 \li The place is currently being saved, no other operation may be performed until
532 complete.
533 \row
534 \li Place.Fetching
535 \li The place details are currently being fetched, no other operations may be performed
536 until complete.
537 \row
538 \li Place.Removing
539 \li The place is currently being removed, no other operations can be performed until
540 complete.
541 \row
542 \li Place.Error
543 \li An error occurred during the last operation, further operations can still be
544 performed on the place.
545 \endtable
546
547 The status of a place can be checked by connecting the status property
548 to a handler function, and then have the handler function process the change
549 in status.
550
551 \snippet declarative/places.qml Place checkStatus
552 \dots
553 \snippet declarative/places.qml Place checkStatus handler
554
555*/
556void QDeclarativePlace::setStatus(Status status, const QString &errorString)
557{
558 Status originalStatus = m_status;
559 m_status = status;
560 m_errorString = errorString;
561
562 if (originalStatus != m_status)
563 emit statusChanged();
564}
565
566QDeclarativePlace::Status QDeclarativePlace::status() const
567{
568 return m_status;
569}
570
571/*!
572 \internal
573*/
574void QDeclarativePlace::finished()
575{
576 if (!m_reply)
577 return;
578
579 if (m_reply->error() == QPlaceReply::NoError) {
580 switch (m_reply->type()) {
581 case (QPlaceReply::IdReply) : {
582 QPlaceIdReply *idReply = qobject_cast<QPlaceIdReply *>(m_reply);
583
584 switch (idReply->operationType()) {
585 case QPlaceIdReply::SavePlace:
586 setPlaceId(idReply->id());
587 break;
588 case QPlaceIdReply::RemovePlace:
589 break;
590 default:
591 //Other operation types shouldn't ever be received.
592 break;
593 }
594 break;
595 }
596 case (QPlaceReply::DetailsReply): {
597 QPlaceDetailsReply *detailsReply = qobject_cast<QPlaceDetailsReply *>(m_reply);
598 setPlace(detailsReply->place());
599 break;
600 }
601 default:
602 //other types of replies shouldn't ever be received.
603 break;
604 }
605
606 m_errorString.clear();
607
608 m_reply->deleteLater();
609 m_reply = nullptr;
610
611 setStatus(QDeclarativePlace::Ready);
612 } else {
613 QString errorString = m_reply->errorString();
614
615 m_reply->deleteLater();
616 m_reply = nullptr;
617
618 setStatus(QDeclarativePlace::Error, errorString);
619 }
620}
621
622/*!
623 \internal
624*/
625void QDeclarativePlace::contactsModified(const QString &key, const QVariant &)
626{
627 primarySignalsEmission(key);
628}
629
630/*!
631 \internal
632*/
633void QDeclarativePlace::cleanupDeletedCategories()
634{
635 for (QDeclarativeCategory * category : m_categoriesToBeDeleted) {
636 if (category->parent() == this)
637 delete category;
638 }
639 m_categoriesToBeDeleted.clear();
640}
641
642/*!
643 \qmlmethod void Place::getDetails()
644
645 This method starts fetching place details.
646
647 The \l status property will change to Place.Fetching while the fetch is in progress. On
648 success the object's properties will be updated, \l status will be set to Place.Ready and
649 \l detailsFetched will be set to true. On error \l status will be set to Place.Error. The
650 \l errorString() method can be used to get the details of the error.
651*/
652void QDeclarativePlace::getDetails()
653{
654 QPlaceManager *placeManager = manager();
655 if (!placeManager)
656 return;
657
658 m_reply = placeManager->getPlaceDetails(placeId());
659 connect(m_reply, &QPlaceReply::finished, this, &QDeclarativePlace::finished);
660 setStatus(QDeclarativePlace::Fetching);
661}
662
663/*!
664 \qmlmethod void Place::save()
665
666 This method performs a save operation on the place.
667
668 The \l status property will change to Place.Saving while the save operation is in progress. On
669 success the \l status will be set to Place.Ready. On error \l status will be set to Place.Error.
670 The \l errorString() method can be used to get the details of the error.
671
672 If the \l placeId property was previously empty, it will be assigned a valid value automatically
673 during a successful save operation.
674
675 Note that a \l PlaceSearchModel will call Place::getDetails on any place that it detects an update
676 on. A consequence of this is that whenever a Place from a \l PlaceSearchModel is successfully saved,
677 it will be followed by a fetch of place details, leading to a sequence of state changes
678 of \c Saving, \c Ready, \c Fetching, \c Ready.
679
680*/
681void QDeclarativePlace::save()
682{
683 QPlaceManager *placeManager = manager();
684 if (!placeManager)
685 return;
686
687 m_reply = placeManager->savePlace(place());
688 connect(m_reply, &QPlaceReply::finished, this, &QDeclarativePlace::finished);
689 setStatus(QDeclarativePlace::Saving);
690}
691
692/*!
693 \qmlmethod void Place::remove()
694
695 This method performs a remove operation on the place.
696
697 The \l status property will change to Place.Removing while the save operation is in progress.
698 On success \l status will be set to Place.Ready. On error \l status will be set to
699 Place.Error. The \l errorString() method can be used to get the details of the error.
700*/
701void QDeclarativePlace::remove()
702{
703 QPlaceManager *placeManager = manager();
704 if (!placeManager)
705 return;
706
707 m_reply = placeManager->removePlace(place().placeId());
708 connect(m_reply, &QPlaceReply::finished, this, &QDeclarativePlace::finished);
709 setStatus(QDeclarativePlace::Removing);
710}
711
712/*!
713 \qmlmethod string Place::errorString()
714
715 Returns a string description of the error of the last operation. If the last operation
716 completed successfully then the string is empty.
717*/
718QString QDeclarativePlace::errorString() const
719{
720 return m_errorString;
721}
722
723/*!
724 \qmlproperty string Place::primaryPhone
725
726 This property holds the primary phone number of the place. If no "phone" contact detail is
727 defined for this place, this property will be an empty string. It is equivalent to:
728
729
730 \snippet declarative/places.qml Place primaryPhone
731*/
732QString QDeclarativePlace::primaryPhone() const
733{
734 return primaryValue(QPlaceContactDetail::Phone);
735}
736
737/*!
738 \qmlproperty string Place::primaryFax
739
740 This property holds the primary fax number of the place. If no "fax" contact detail is
741 defined for this place this property will be an empty string. It is equivalent to
742
743 \snippet declarative/places.qml Place primaryFax
744*/
745QString QDeclarativePlace::primaryFax() const
746{
747 return primaryValue(QPlaceContactDetail::Fax);
748}
749
750/*!
751 \qmlproperty string Place::primaryEmail
752
753 This property holds the primary email address of the place. If no "email" contact detail is
754 defined for this place this property will be an empty string. It is equivalent to
755
756 \snippet declarative/places.qml Place primaryEmail
757*/
758QString QDeclarativePlace::primaryEmail() const
759{
760 return primaryValue(QPlaceContactDetail::Email);
761}
762
763/*!
764 \qmlproperty string Place::primaryWebsite
765
766 This property holds the primary website url of the place. If no "website" contact detail is
767 defined for this place this property will be an empty string. It is equivalent to
768
769 \snippet declarative/places.qml Place primaryWebsite
770*/
771
772QUrl QDeclarativePlace::primaryWebsite() const
773{
774 return QUrl(primaryValue(QPlaceContactDetail::Website));
775}
776
777/*!
778 \qmlproperty ExtendedAttributes Place::extendedAttributes
779
780 This property holds the extended attributes of a place. Extended attributes are additional
781 information about a place not covered by the place's properties.
782*/
783QQmlPropertyMap *QDeclarativePlace::extendedAttributes() const
784{
785 return m_extendedAttributes;
786}
787
788/*!
789 \qmlproperty ContactDetails Place::contactDetails
790
791 This property holds the contact information for this place, for example a phone number or
792 a website URL. This property is a map of \l contactDetail objects.
793*/
794QDeclarativeContactDetails *QDeclarativePlace::contactDetails() const
795{
796 return m_contactDetails;
797}
798
799/*!
800 \qmlproperty list<Category> Place::categories
801
802 This property holds the list of categories this place is a member of. The categories that can
803 be assigned to a place are specific to each \l plugin.
804*/
805QQmlListProperty<QDeclarativeCategory> QDeclarativePlace::categories()
806{
807 return QQmlListProperty<QDeclarativeCategory>(this,
808 0, // opaque data parameter
809 category_append,
810 category_count,
811 category_at,
812 category_clear);
813}
814
815/*!
816 \internal
817*/
818void QDeclarativePlace::category_append(QQmlListProperty<QDeclarativeCategory> *prop,
819 QDeclarativeCategory *value)
820{
821 QDeclarativePlace *object = static_cast<QDeclarativePlace *>(prop->object);
822
823 if (object->m_categoriesToBeDeleted.contains(value))
824 object->m_categoriesToBeDeleted.removeAll(value);
825
826 if (!object->m_categories.contains(value)) {
827 object->m_categories.append(value);
828 QList<QPlaceCategory> list = object->m_src.categories();
829 list.append(value->category());
830 object->m_src.setCategories(list);
831
832 emit object->categoriesChanged();
833 }
834}
835
836/*!
837 \internal
838*/
839qsizetype QDeclarativePlace::category_count(QQmlListProperty<QDeclarativeCategory> *prop)
840{
841 return static_cast<QDeclarativePlace *>(prop->object)->m_categories.count();
842}
843
844/*!
845 \internal
846*/
847QDeclarativeCategory *QDeclarativePlace::category_at(QQmlListProperty<QDeclarativeCategory> *prop,
848 qsizetype index)
849{
850 QDeclarativePlace *object = static_cast<QDeclarativePlace *>(prop->object);
851 QDeclarativeCategory *res = NULL;
852 if (object->m_categories.count() > index && index > -1) {
853 res = object->m_categories[index];
854 }
855 return res;
856}
857
858/*!
859 \internal
860*/
861void QDeclarativePlace::category_clear(QQmlListProperty<QDeclarativeCategory> *prop)
862{
863 QDeclarativePlace *object = static_cast<QDeclarativePlace *>(prop->object);
864 if (object->m_categories.isEmpty())
865 return;
866
867 for (auto *category : std::as_const(object->m_categories)) {
868 if (category->parent() == object)
869 object->m_categoriesToBeDeleted.append(category);
870 }
871
872 object->m_categories.clear();
873 object->m_src.setCategories(QList<QPlaceCategory>());
874 emit object->categoriesChanged();
875 QMetaObject::invokeMethod(object, "cleanupDeletedCategories", Qt::QueuedConnection);
876}
877
878/*!
879 \internal
880*/
881void QDeclarativePlace::synchronizeCategories()
882{
883 qDeleteAll(m_categories);
884 m_categories.clear();
885 for (const QPlaceCategory &value : m_src.categories()) {
886 QDeclarativeCategory *declarativeValue = new QDeclarativeCategory(value, m_plugin, this);
887 m_categories.append(declarativeValue);
888 }
889}
890
891/*!
892 \qmlproperty enumeration Place::visibility
893
894 This property holds the visibility of the place. It can be one of:
895
896 \table
897 \row
898 \li Place.UnspecifiedVisibility
899 \li The visibility of the place is unspecified, the default visibility of the \l Plugin
900 will be used.
901 \row
902 \li Place.DeviceVisibility
903 \li The place is limited to the current device. The place will not be transferred off
904 of the device.
905 \row
906 \li Place.PrivateVisibility
907 \li The place is private to the current user. The place may be transferred to an online
908 service but is only ever visible to the current user.
909 \row
910 \li Place.PublicVisibility
911 \li The place is public.
912 \endtable
913
914 Note that visibility does not affect how the place is displayed
915 in the user-interface of an application on the device. Instead,
916 it defines the sharing semantics of the place.
917*/
918QDeclarativePlace::Visibility QDeclarativePlace::visibility() const
919{
920 return static_cast<QDeclarativePlace::Visibility>(m_src.visibility());
921}
922
923void QDeclarativePlace::setVisibility(Visibility visibility)
924{
925 if (static_cast<QDeclarativePlace::Visibility>(m_src.visibility()) == visibility)
926 return;
927
928 m_src.setVisibility(static_cast<QLocation::Visibility>(visibility));
929 emit visibilityChanged();
930}
931
932/*!
933 \qmlproperty Place Place::favorite
934
935 This property holds the favorite instance of a place.
936*/
937QDeclarativePlace *QDeclarativePlace::favorite() const
938{
939 return m_favorite;
940}
941
942void QDeclarativePlace::setFavorite(QDeclarativePlace *favorite)
943{
944
945 if (m_favorite == favorite)
946 return;
947
948 if (m_favorite && m_favorite->parent() == this)
949 delete m_favorite;
950
951 m_favorite = favorite;
952 emit favoriteChanged();
953}
954
955/*!
956 \qmlmethod void Place::copyFrom(Place original)
957
958 Copies data from an \a original place into this place. Only data that is supported by this
959 place's plugin is copied over and plugin specific data such as place identifier is not copied over.
960*/
961void QDeclarativePlace::copyFrom(QDeclarativePlace *original)
962{
963 QPlaceManager *placeManager = manager();
964 if (!placeManager)
965 return;
966
967 setPlace(placeManager->compatiblePlace(original->place()));
968}
969
970/*!
971 \qmlmethod void Place::initializeFavorite(Plugin destinationPlugin)
972
973 Creates a favorite instance for the place which is to be saved into the
974 destination plugin \a destinationPlugin. This method does nothing if the
975 favorite property is not \c null.
976*/
977void QDeclarativePlace::initializeFavorite(QDeclarativeGeoServiceProvider *plugin)
978{
979 if (m_favorite == 0) {
980 QDeclarativePlace *place = new QDeclarativePlace(this);
981 place->setPlugin(plugin);
982 place->copyFrom(this);
983 setFavorite(place);
984 }
985}
986
987/*!
988 \internal
989*/
990void QDeclarativePlace::pullExtendedAttributes()
991{
992 const QStringList keys = m_extendedAttributes->keys();
993 for (const QString &key : keys)
994 m_extendedAttributes->clear(key);
995
996 const QStringList attributeTypes = m_src.extendedAttributeTypes();
997 for (const QString &attributeType : attributeTypes) {
998 m_extendedAttributes->insert(attributeType,
999 QVariant::fromValue(m_src.extendedAttribute(attributeType)));
1000 }
1001
1002 emit extendedAttributesChanged();
1003}
1004
1005/*!
1006 \internal
1007*/
1008void QDeclarativePlace::synchronizeContacts()
1009{
1010 //clear out contact data
1011 for (const QString &contactType : m_contactDetails->keys()) {
1012 const QList<QVariant> contacts = m_contactDetails->value(contactType).toList();
1013 for (const QVariant &var : contacts) {
1014 QObject *obj = var.value<QObject *>();
1015 if (obj->parent() == this)
1016 delete obj;
1017 }
1018 m_contactDetails->insert(contactType, QVariantList());
1019 }
1020
1021 //insert new contact data from source place
1022 for (const QString &contactType : m_src.contactTypes()) {
1023 const QList<QPlaceContactDetail> sourceContacts = m_src.contactDetails(contactType);
1024 QVariantList declContacts;
1025 for (const QPlaceContactDetail &sourceContact : sourceContacts)
1026 declContacts.append(QVariant::fromValue(sourceContact));
1027 m_contactDetails->insert(contactType, declContacts);
1028 }
1029 primarySignalsEmission();
1030}
1031
1032/*!
1033 \internal
1034 Helper function to emit the signals for the primary___()
1035 fields. It is expected that the values of the primary___()
1036 functions have already been modified to new values.
1037*/
1038void QDeclarativePlace::primarySignalsEmission(const QString &type)
1039{
1040 if (type.isEmpty() || type == QPlaceContactDetail::Phone) {
1041 if (m_prevPrimaryPhone != primaryPhone()) {
1042 m_prevPrimaryPhone = primaryPhone();
1043 emit primaryPhoneChanged();
1044 }
1045 if (!type.isEmpty())
1046 return;
1047 }
1048
1049 if (type.isEmpty() || type == QPlaceContactDetail::Email) {
1050 if (m_prevPrimaryEmail != primaryEmail()) {
1051 m_prevPrimaryEmail = primaryEmail();
1052 emit primaryEmailChanged();
1053 }
1054 if (!type.isEmpty())
1055 return;
1056 }
1057
1058 if (type.isEmpty() || type == QPlaceContactDetail::Website) {
1059 if (m_prevPrimaryWebsite != primaryWebsite()) {
1060 m_prevPrimaryWebsite = primaryWebsite();
1061 emit primaryWebsiteChanged();
1062 }
1063 if (!type.isEmpty())
1064 return;
1065 }
1066
1067 if (type.isEmpty() || type == QPlaceContactDetail::Fax) {
1068 if (m_prevPrimaryFax != primaryFax()) {
1069 m_prevPrimaryFax = primaryFax();
1070 emit primaryFaxChanged();
1071 }
1072 }
1073}
1074
1075/*!
1076 \internal
1077 Helper function to return the manager, this manager is intended to be used
1078 to perform the next operation. If a an operation is currently underway
1079 then return a null pointer.
1080*/
1081QPlaceManager *QDeclarativePlace::manager()
1082{
1083 if (m_status != QDeclarativePlace::Ready && m_status != QDeclarativePlace::Error)
1084 return nullptr;
1085
1086 if (m_reply) {
1087 m_reply->abort();
1088 m_reply->deleteLater();
1089 m_reply = nullptr;
1090 }
1091
1092 if (!m_plugin) {
1093 qmlWarning(this) << QStringLiteral("Plugin is not assigned to place.");
1094 return nullptr;
1095 }
1096
1097 QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider();
1098 if (!serviceProvider)
1099 return nullptr;
1100
1101 QPlaceManager *placeManager = serviceProvider->placeManager();
1102
1103 if (!placeManager) {
1104 setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR)
1105 .arg(m_plugin->name()).arg(serviceProvider->errorString()));
1106 return nullptr;
1107 }
1108
1109 return placeManager;
1110}
1111
1112/*!
1113 \internal
1114*/
1115QString QDeclarativePlace::primaryValue(const QString &contactType) const
1116{
1117 QVariant value = m_contactDetails->value(contactType);
1118 if (value.userType() == qMetaTypeId<QJSValue>())
1119 value = value.value<QJSValue>().toVariant();
1120
1121 if (value.userType() == QMetaType::QVariantList) {
1122 QVariantList detailList = m_contactDetails->value(contactType).toList();
1123 if (!detailList.isEmpty())
1124 return detailList.at(0).value<QPlaceContactDetail>().value();
1125 } else if (value.metaType() == QMetaType::fromType<QPlaceContactDetail>()) {
1126 return value.value<QPlaceContactDetail>().value();
1127 }
1128
1129 return QString();
1130}
1131
1132QT_END_NAMESPACE