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

source code of qtlocation/src/location/declarativeplaces/qdeclarativeplace.cpp