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