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 
55 QT_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 
QDeclarativePlace(QObject * parent)184 QDeclarativePlace::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(m_contactDetails, SIGNAL(valueChanged(QString,QVariant)),
192             this, SLOT(contactsModified(QString,QVariant)));
193 
194     setPlace(QPlace());
195 }
196 
QDeclarativePlace(const QPlace & src,QDeclarativeGeoServiceProvider * plugin,QObject * parent)197 QDeclarativePlace::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(m_contactDetails, SIGNAL(valueChanged(QString,QVariant)),
207             this, SLOT(contactsModified(QString,QVariant)));
208 
209     setPlace(src);
210 }
211 
~QDeclarativePlace()212 QDeclarativePlace::~QDeclarativePlace()
213 {
214 }
215 
216 // From QQmlParserStatus
componentComplete()217 void 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 */
setPlugin(QDeclarativeGeoServiceProvider * plugin)227 void 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(m_plugin, SIGNAL(attached()),
240                 this, SLOT(pluginReady()));
241     }
242 }
243 
pluginReady()244 void QDeclarativePlace::pluginReady()
245 {
246     QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider();
247     QPlaceManager *placeManager = serviceProvider->placeManager();
248     if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) {
249         setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR)
250                          .arg(m_plugin->name()).arg(serviceProvider->errorString()));
251         return;
252     }
253 }
254 
plugin() const255 QDeclarativeGeoServiceProvider *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 */
reviewModel()265 QDeclarativeReviewModel *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 */
imageModel()280 QDeclarativePlaceImageModel *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 */
editorialModel()295 QDeclarativePlaceEditorialModel *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 */
setPlace(const QPlace & src)311 void 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(m_src.supplier(), 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(QPlaceContent::ReviewType) >= 0) {
376         m_reviewModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ReviewType),
377                                             m_src.content(QPlaceContent::ReviewType));
378     }
379     if (m_imageModel && m_src.totalContentCount(QPlaceContent::ImageType) >= 0) {
380         m_imageModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ImageType),
381                                            m_src.content(QPlaceContent::ImageType));
382     }
383     if (m_editorialModel && m_src.totalContentCount(QPlaceContent::EditorialType) >= 0) {
384         m_editorialModel->initializeCollection(m_src.totalContentCount(QPlaceContent::EditorialType),
385                                                m_src.content(QPlaceContent::EditorialType));
386     }
387 
388     pullExtendedAttributes();
389     synchronizeContacts();
390 }
391 
place()392 QPlace 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(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 *>(detailVar.value<QObject *>());
425                 if (detail)
426                     cppDetails.append(detail->contactDetail());
427             }
428         } else {
429             QDeclarativeContactDetail *detail = qobject_cast<QDeclarativeContactDetail *>(m_contactDetails->value(key).value<QObject *>());
430             if (detail)
431                 cppDetails.append(detail->contactDetail());
432         }
433         result.setContactDetails(key, 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 */
setLocation(QDeclarativeGeoLocation * location)445 void 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 
location()457 QDeclarativeGeoLocation *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 */
setRatings(QDeclarativeRatings * rating)468 void 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 
ratings()480 QDeclarativeRatings *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 */
setSupplier(QDeclarativeSupplier * supplier)492 void 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 
supplier() const504 QDeclarativeSupplier *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 */
icon() const514 QDeclarativePlaceIcon *QDeclarativePlace::icon() const
515 {
516     return m_icon;
517 }
518 
setIcon(QDeclarativePlaceIcon * icon)519 void 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 */
setName(const QString & name)536 void QDeclarativePlace::setName(const QString &name)
537 {
538     if (m_src.name() != name) {
539         m_src.setName(name);
540         emit nameChanged();
541     }
542 }
543 
name() const544 QString 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 */
setPlaceId(const QString & placeId)560 void QDeclarativePlace::setPlaceId(const QString &placeId)
561 {
562     if (m_src.placeId() != placeId) {
563         m_src.setPlaceId(placeId);
564         emit placeIdChanged();
565     }
566 }
567 
placeId() const568 QString 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 */
setAttribution(const QString & attribution)581 void QDeclarativePlace::setAttribution(const QString &attribution)
582 {
583     if (m_src.attribution() != attribution) {
584         m_src.setAttribution(attribution);
585         emit attributionChanged();
586     }
587 }
588 
attribution() const589 QString 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 */
detailsFetched() const603 bool 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 */
setStatus(Status status,const QString & errorString)645 void 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 
status() const655 QDeclarativePlace::Status QDeclarativePlace::status() const
656 {
657     return m_status;
658 }
659 
660 /*!
661     \internal
662 */
finished()663 void 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 *>(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 *>(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(QDeclarativePlace::Ready);
701     } else {
702         QString errorString = m_reply->errorString();
703 
704         m_reply->deleteLater();
705         m_reply = 0;
706 
707         setStatus(QDeclarativePlace::Error, errorString);
708     }
709 }
710 
711 /*!
712     \internal
713 */
contactsModified(const QString & key,const QVariant &)714 void QDeclarativePlace::contactsModified(const QString &key, const QVariant &)
715 {
716     primarySignalsEmission(key);
717 }
718 
719 /*!
720     \internal
721 */
cleanupDeletedCategories()722 void 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 */
getDetails()741 void QDeclarativePlace::getDetails()
742 {
743     QPlaceManager *placeManager = manager();
744     if (!placeManager)
745         return;
746 
747     m_reply = placeManager->getPlaceDetails(placeId());
748     connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
749     setStatus(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 */
save()770 void QDeclarativePlace::save()
771 {
772     QPlaceManager *placeManager = manager();
773     if (!placeManager)
774         return;
775 
776     m_reply = placeManager->savePlace(place());
777     connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
778     setStatus(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 */
remove()790 void QDeclarativePlace::remove()
791 {
792     QPlaceManager *placeManager = manager();
793     if (!placeManager)
794         return;
795 
796     m_reply = placeManager->removePlace(place().placeId());
797     connect(m_reply, SIGNAL(finished()), this, SLOT(finished()));
798     setStatus(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 */
errorString() const807 QString 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 */
primaryPhone() const821 QString QDeclarativePlace::primaryPhone() const
822 {
823     return primaryValue(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 */
primaryFax() const834 QString QDeclarativePlace::primaryFax() const
835 {
836     return primaryValue(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 */
primaryEmail() const847 QString QDeclarativePlace::primaryEmail() const
848 {
849     return primaryValue(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 
primaryWebsite() const861 QUrl QDeclarativePlace::primaryWebsite() const
862 {
863     return QUrl(primaryValue(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 */
extendedAttributes() const872 QQmlPropertyMap *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 */
contactDetails() const883 QDeclarativeContactDetails *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 */
categories()894 QQmlListProperty<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 */
category_append(QQmlListProperty<QDeclarativeCategory> * prop,QDeclarativeCategory * value)907 void 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(value))
913         object->m_categoriesToBeDeleted.removeAll(value);
914 
915     if (!object->m_categories.contains(value)) {
916         object->m_categories.append(value);
917         QList<QPlaceCategory> list = object->m_src.categories();
918         list.append(value->category());
919         object->m_src.setCategories(list);
920 
921         emit object->categoriesChanged();
922     }
923 }
924 
925 /*!
926     \internal
927 */
category_count(QQmlListProperty<QDeclarativeCategory> * prop)928 int QDeclarativePlace::category_count(QQmlListProperty<QDeclarativeCategory> *prop)
929 {
930     return static_cast<QDeclarativePlace *>(prop->object)->m_categories.count();
931 }
932 
933 /*!
934     \internal
935 */
category_at(QQmlListProperty<QDeclarativeCategory> * prop,int index)936 QDeclarativeCategory *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 */
category_clear(QQmlListProperty<QDeclarativeCategory> * prop)950 void 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(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(object, "cleanupDeletedCategories", Qt::QueuedConnection);
965 }
966 
967 /*!
968     \internal
969 */
synchronizeCategories()970 void QDeclarativePlace::synchronizeCategories()
971 {
972     qDeleteAll(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(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 */
visibility() const1007 QDeclarativePlace::Visibility QDeclarativePlace::visibility() const
1008 {
1009     return static_cast<QDeclarativePlace::Visibility>(m_src.visibility());
1010 }
1011 
setVisibility(Visibility visibility)1012 void 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 */
favorite() const1026 QDeclarativePlace *QDeclarativePlace::favorite() const
1027 {
1028     return m_favorite;
1029 }
1030 
setFavorite(QDeclarativePlace * favorite)1031 void 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 */
copyFrom(QDeclarativePlace * original)1050 void QDeclarativePlace::copyFrom(QDeclarativePlace *original)
1051 {
1052     QPlaceManager *placeManager = manager();
1053     if (!placeManager)
1054         return;
1055 
1056     setPlace(placeManager->compatiblePlace(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 */
initializeFavorite(QDeclarativeGeoServiceProvider * plugin)1066 void QDeclarativePlace::initializeFavorite(QDeclarativeGeoServiceProvider *plugin)
1067 {
1068     if (m_favorite == 0) {
1069         QDeclarativePlace *place = new QDeclarativePlace(this);
1070         place->setPlugin(plugin);
1071         place->copyFrom(this);
1072         setFavorite(place);
1073     }
1074 }
1075 
1076 /*!
1077     \internal
1078 */
pullExtendedAttributes()1079 void 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(attributeType,
1088             QVariant::fromValue(new QDeclarativePlaceAttribute(m_src.extendedAttribute(attributeType))));
1089     }
1090 
1091     emit extendedAttributesChanged();
1092 }
1093 
1094 /*!
1095     \internal
1096 */
synchronizeContacts()1097 void QDeclarativePlace::synchronizeContacts()
1098 {
1099     //clear out contact data
1100     foreach (const QString &contactType, m_contactDetails->keys()) {
1101         QList<QVariant> contacts = m_contactDetails->value(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(contactType, 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(QVariant::fromValue(qobject_cast<QObject *>(declContact)));
1118         }
1119         m_contactDetails->insert(contactType, 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 */
primarySignalsEmission(const QString & type)1130 void 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 */
manager()1173 QPlaceManager *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(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(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR)
1197                          .arg(m_plugin->name()).arg(serviceProvider->errorString()));
1198         return 0;
1199     }
1200 
1201     return placeManager;
1202 }
1203 
1204 /*!
1205     \internal
1206 */
primaryValue(const QString & contactType) const1207 QString QDeclarativePlace::primaryValue(const QString &contactType) const
1208 {
1209     QVariant value = m_contactDetails->value(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(contactType).toList();
1215         if (!detailList.isEmpty()) {
1216             QDeclarativeContactDetail *primaryDetail = qobject_cast<QDeclarativeContactDetail *>(detailList.at(0).value<QObject *>());
1217             if (primaryDetail)
1218                 return primaryDetail->value();
1219         }
1220     } else if (value.userType() == QMetaType::QObjectStar) {
1221         QDeclarativeContactDetail *primaryDetail = qobject_cast<QDeclarativeContactDetail *>(m_contactDetails->value(contactType).value<QObject *>());
1222         if (primaryDetail)
1223             return primaryDetail->value();
1224     }
1225 
1226     return QString();
1227 }
1228 
1229 QT_END_NAMESPACE
1230