diff options
Diffstat (limited to 'src/location/declarativeplaces/qdeclarativeplace.cpp')
-rw-r--r-- | src/location/declarativeplaces/qdeclarativeplace.cpp | 1229 |
1 files changed, 1229 insertions, 0 deletions
diff --git a/src/location/declarativeplaces/qdeclarativeplace.cpp b/src/location/declarativeplaces/qdeclarativeplace.cpp new file mode 100644 index 00000000..91ef2026 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplace.cpp @@ -0,0 +1,1229 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeplace_p.h" +#include "qdeclarativecontactdetail_p.h" +#include "qdeclarativegeoserviceprovider_p.h" +#include "qdeclarativeplaceattribute_p.h" +#include "qdeclarativeplaceicon_p.h" +#include "error_messages.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QMetaObject> +#include <QtQml/QQmlEngine> +#include <QtQml/QQmlInfo> +#include <QtLocation/QGeoServiceProvider> +#include <QtLocation/QPlaceManager> +#include <QtLocation/QPlaceDetailsReply> +#include <QtLocation/QPlaceReply> +#include <QtLocation/QPlaceIdReply> +#include <QtLocation/QPlaceContactDetail> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Place + \instantiates QDeclarativePlace + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief The Place type represents a location that is a position of interest. + + The Place type represents a physical location with additional metadata describing that + location. Contrasted with \l Location, \l Address, and + \l {coordinate} type which are used to describe where a location is. + The basic properties of a Place are its \l name and \l location. + + Place objects are typically obtained from a search model and will generally only have their + basic properties set. The \l detailsFetched property can be used to test if further property + values need to be fetched from the \l Plugin. This can be done by invoking the \l getDetails() + method. Progress of the fetching operation can be monitored with the \l status property, which + will be set to Place.Fetching when the details are being fetched. + + The Place type has many properties holding information about the location. Details on how to + contact the place are available from the \l contactDetails property. Convenience properties + for obtaining the primary \l {primaryPhone}{phone}, \l {primaryFax}{fax}, + \l {primaryEmail}{email} and \l {primaryWebsite}{website} are also available. + + Each place is assigned zero or more \l categories. Categories are typically used when + searching for a particular kind of place, such as a restaurant or hotel. Some places have a + \l ratings object, which gives an indication of the quality of the place. + + Place metadata is provided by a \l supplier who may require that an \l attribution message be + displayed to the user when the place details are viewed. + + Places have an associated \l icon which can be used to represent a place on a map or to + decorate a delegate in a view. + + Places may have additional rich content associated with them. The currently supported rich + content include editorial descriptions, reviews and images. These are exposed as a set of + models for retrieving the content. Editorial descriptions of the place are available from the + \l editorialModel property. Reviews of the place are available from the \l reviewModel + property. A gallery of pictures of the place can be accessed using the \l imageModel property. + + Places may have additional attributes which are not covered in the formal API. The + \l extendedAttributes property provides access to these. The type of extended attributes + available is specific to each \l Plugin. + + A Place is almost always tied to a \l plugin. The \l plugin property must be set before it is + possible to call \l save(), \l remove() or \l getDetails(). The \l reviewModel, \l imageModel + and \l editorialModel are only valid then the \l plugin property is set. + + \section2 Saving a Place + + If the \l Plugin supports it, the Place type can be used to save a place. First create a new + Place and set its properties: + + \snippet declarative/places.qml Place savePlace def + + Then invoke the \l save() method: + + \snippet declarative/places.qml Place savePlace + + The \l status property will change to Place.Saving and then to Place.Ready if the save was + successful or to Place.Error if an error occurs. + + If the \l placeId property is set, the backend will update an existing place otherwise it will + create a new place. On success the \l placeId property will be updated with the identifier of the newly + saved place. + + \section3 Caveats + \input place-caveats.qdocinc + + \section3 Saving Between Plugins + When saving places between plugins, there are a few things to be aware of. + Some fields of a place such as the id, categories and icons are plugin specific entities. For example + the categories in one manager may not be recognised in another. + Therefore trying to save a place directly from one plugin to another is not possible. + + It is generally recommended that saving across plugins be handled as saving \l {Favorites}{favorites} + as explained in the Favorites section. However there is another approach which is to create a new place, + set its (destination) plugin and then use the \l copyFrom() method to copy the details of the original place. + Using \l copyFrom() only copies data that is supported by the destination plugin, + plugin specific data such as the place identifier is not copied over. Once the copy is done, + the place is in a suitable state to be saved. + + The following snippet provides an example of saving a place to a different plugin + using the \l copyFrom method: + + \snippet declarative/places.qml Place save to different plugin + + \section2 Removing a Place + + To remove a place, ensure that a Place object with a valid \l placeId property exists and call + its \l remove() method. The \l status property will change to Place.Removing and then to + Place.Ready if the save was successful or to Place.Error if an error occurs. + + \section2 Favorites + The Places API supports the concept of favorites. Favorites are generally implemented + by using two plugins, the first plugin is typically a read-only source of places (origin plugin) and a second + read/write plugin (destination plugin) is used to store places from the origin as favorites. + + Each Place has a favorite property which is intended to contain the corresponding place + from the destination plugin (the place itself is sourced from the origin plugin). Because both the original + place and favorite instances are available, the developer can choose which + properties to show to the user. For example the favorite may have a modified name which should + be displayed rather than the original name. + + \snippet declarative/places.qml Place favorite + + The following demonstrates how to save a new favorite instance. A call is made + to create/initialize the favorite instance and then the instance is saved. + + \snippet declarative/places.qml Place saveFavorite + + The following demonstrates favorite removal: + + \snippet declarative/places.qml Place removeFavorite 1 + \dots + \snippet declarative/places.qml Place removeFavorite 2 + + The PlaceSearchModel has a favoritesPlugin property. If the property is set, any places found + during a search are checked against the favoritesPlugin to see if there is a corresponding + favorite place. If so, the favorite property of the Place is set, otherwise the favorite + property is remains null. + + \sa PlaceSearchModel +*/ + +QDeclarativePlace::QDeclarativePlace(QObject *parent) +: QObject(parent), m_location(0), m_ratings(0), m_supplier(0), m_icon(0), + m_reviewModel(0), m_imageModel(0), m_editorialModel(0), + m_extendedAttributes(new QQmlPropertyMap(this)), + m_contactDetails(new QDeclarativeContactDetails(this)), m_reply(0), m_plugin(0), + m_complete(false), m_favorite(0), m_status(QDeclarativePlace::Ready) +{ + connect(m_contactDetails, SIGNAL(valueChanged(QString,QVariant)), + this, SLOT(contactsModified(QString,QVariant))); + + setPlace(QPlace()); +} + +QDeclarativePlace::QDeclarativePlace(const QPlace &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent) +: QObject(parent), m_location(0), m_ratings(0), m_supplier(0), m_icon(0), + m_reviewModel(0), m_imageModel(0), m_editorialModel(0), + m_extendedAttributes(new QQmlPropertyMap(this)), + m_contactDetails(new QDeclarativeContactDetails(this)), m_reply(0), m_plugin(plugin), + m_complete(false), m_favorite(0), m_status(QDeclarativePlace::Ready) +{ + Q_ASSERT(plugin); + + connect(m_contactDetails, SIGNAL(valueChanged(QString,QVariant)), + this, SLOT(contactsModified(QString,QVariant))); + + setPlace(src); +} + +QDeclarativePlace::~QDeclarativePlace() +{ +} + +// From QQmlParserStatus +void QDeclarativePlace::componentComplete() +{ + m_complete = true; +} + +/*! + \qmlproperty Plugin Place::plugin + + This property holds the \l Plugin that provided this place which can be used to retrieve more information about the service. +*/ +void QDeclarativePlace::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + m_plugin = plugin; + if (m_complete) + emit pluginChanged(); + + if (m_plugin->isAttached()) { + pluginReady(); + } else { + connect(m_plugin, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +void QDeclarativePlace::pluginReady() +{ + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return; + } +} + +QDeclarativeGeoServiceProvider *QDeclarativePlace::plugin() const +{ + return m_plugin; +} + +/*! + \qmlproperty ReviewModel Place::reviewModel + + This property holds a model which can be used to retrieve reviews about the place. +*/ +QDeclarativeReviewModel *QDeclarativePlace::reviewModel() +{ + if (!m_reviewModel) { + m_reviewModel = new QDeclarativeReviewModel(this); + m_reviewModel->setPlace(this); + } + + return m_reviewModel; +} + +/*! + \qmlproperty ImageModel Place::imageModel + + This property holds a model which can be used to retrieve images of the place. +*/ +QDeclarativePlaceImageModel *QDeclarativePlace::imageModel() +{ + if (!m_imageModel) { + m_imageModel = new QDeclarativePlaceImageModel(this); + m_imageModel->setPlace(this); + } + + return m_imageModel; +} + +/*! + \qmlproperty EditorialModel Place::editorialModel + + This property holds a model which can be used to retrieve editorial descriptions of the place. +*/ +QDeclarativePlaceEditorialModel *QDeclarativePlace::editorialModel() +{ + if (!m_editorialModel) { + m_editorialModel = new QDeclarativePlaceEditorialModel(this); + m_editorialModel->setPlace(this); + } + + return m_editorialModel; +} + +/*! + \qmlproperty QPlace Place::place + + For details on how to use this property to interface between C++ and QML see + "\l {Place - QPlace} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativePlace::setPlace(const QPlace &src) +{ + QPlace previous = m_src; + m_src = src; + + if (previous.categories() != m_src.categories()) { + synchronizeCategories(); + emit categoriesChanged(); + } + + if (m_location && m_location->parent() == this) { + m_location->setLocation(m_src.location()); + } else if (!m_location || m_location->parent() != this) { + m_location = new QDeclarativeGeoLocation(m_src.location(), this); + emit locationChanged(); + } + + if (m_ratings && m_ratings->parent() == this) { + m_ratings->setRatings(m_src.ratings()); + } else if (!m_ratings || m_ratings->parent() != this) { + m_ratings = new QDeclarativeRatings(m_src.ratings(), this); + emit ratingsChanged(); + } + + if (m_supplier && m_supplier->parent() == this) { + m_supplier->setSupplier(m_src.supplier(), m_plugin); + } else if (!m_supplier || m_supplier->parent() != this) { + m_supplier = new QDeclarativeSupplier(m_src.supplier(), m_plugin, this); + emit supplierChanged(); + } + + if (m_icon && m_icon->parent() == this) { + m_icon->setPlugin(m_plugin); + m_icon->setIcon(m_src.icon()); + } else if (!m_icon || m_icon->parent() != this) { + m_icon = new QDeclarativePlaceIcon(m_src.icon(), m_plugin, this); + emit iconChanged(); + } + + if (previous.name() != m_src.name()) { + emit nameChanged(); + } + if (previous.placeId() != m_src.placeId()) { + emit placeIdChanged(); + } + if (previous.attribution() != m_src.attribution()) { + emit attributionChanged(); + } + if (previous.detailsFetched() != m_src.detailsFetched()) { + emit detailsFetchedChanged(); + } + if (previous.primaryPhone() != m_src.primaryPhone()) { + emit primaryPhoneChanged(); + } + if (previous.primaryFax() != m_src.primaryFax()) { + emit primaryFaxChanged(); + } + if (previous.primaryEmail() != m_src.primaryEmail()) { + emit primaryEmailChanged(); + } + if (previous.primaryWebsite() != m_src.primaryWebsite()) { + emit primaryWebsiteChanged(); + } + + if (m_reviewModel && m_src.totalContentCount(QPlaceContent::ReviewType) >= 0) { + m_reviewModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ReviewType), + m_src.content(QPlaceContent::ReviewType)); + } + if (m_imageModel && m_src.totalContentCount(QPlaceContent::ImageType) >= 0) { + m_imageModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ImageType), + m_src.content(QPlaceContent::ImageType)); + } + if (m_editorialModel && m_src.totalContentCount(QPlaceContent::EditorialType) >= 0) { + m_editorialModel->initializeCollection(m_src.totalContentCount(QPlaceContent::EditorialType), + m_src.content(QPlaceContent::EditorialType)); + } + + synchronizeExtendedAttributes(); + synchronizeContacts(); +} + +QPlace QDeclarativePlace::place() +{ + // The following properties are not stored in m_src but instead stored in QDeclarative* objects + + QPlace result = m_src; + + // Categories + QList<QPlaceCategory> categories; + foreach (QDeclarativeCategory *value, m_categories) + categories.append(value->category()); + + result.setCategories(categories); + + // Location + result.setLocation(m_location ? m_location->location() : QGeoLocation()); + + // Rating + result.setRatings(m_ratings ? m_ratings->ratings() : QPlaceRatings()); + + // Supplier + result.setSupplier(m_supplier ? m_supplier->supplier() : QPlaceSupplier()); + + // Icon + result.setIcon(m_icon ? m_icon->icon() : QPlaceIcon()); + + //contact details + QList<QPlaceContactDetail> cppDetails; + foreach (const QString &key, m_contactDetails->keys()) { + cppDetails.clear(); + if (m_contactDetails->value(key).type() == QVariant::List) { + QVariantList detailsVarList = m_contactDetails->value(key).toList(); + foreach (const QVariant &detailVar, detailsVarList) { + QDeclarativeContactDetail *detail = qobject_cast<QDeclarativeContactDetail *>(detailVar.value<QObject *>()); + if (detail) + cppDetails.append(detail->contactDetail()); + } + } else { + QDeclarativeContactDetail *detail = qobject_cast<QDeclarativeContactDetail *>(m_contactDetails->value(key).value<QObject *>()); + if (detail) + cppDetails.append(detail->contactDetail()); + } + result.setContactDetails(key, cppDetails); + } + + return result; +} + +/*! + \qmlproperty QtPositioning::Location Place::location + + This property holds the location of the place which can be used to retrieve the coordinate, + address and the bounding box. +*/ +void QDeclarativePlace::setLocation(QDeclarativeGeoLocation *location) +{ + if (m_location == location) + return; + + if (m_location && m_location->parent() == this) + delete m_location; + + m_location = location; + emit locationChanged(); +} + +QDeclarativeGeoLocation *QDeclarativePlace::location() +{ + return m_location; +} + +/*! + \qmlproperty Ratings Place::ratings + + This property holds ratings of the place. The ratings provide an indication of the quality of a + place. +*/ +void QDeclarativePlace::setRatings(QDeclarativeRatings *rating) +{ + if (m_ratings == rating) + return; + + if (m_ratings && m_ratings->parent() == this) + delete m_ratings; + + m_ratings = rating; + emit ratingsChanged(); +} + +QDeclarativeRatings *QDeclarativePlace::ratings() +{ + + return m_ratings; +} + +/*! + \qmlproperty Supplier Place::supplier + + This property holds the supplier of the place data. + The supplier is typically a business or organization that collected the data about the place. +*/ +void QDeclarativePlace::setSupplier(QDeclarativeSupplier *supplier) +{ + if (m_supplier == supplier) + return; + + if (m_supplier && m_supplier->parent() == this) + delete m_supplier; + + m_supplier = supplier; + emit supplierChanged(); +} + +QDeclarativeSupplier *QDeclarativePlace::supplier() const +{ + return m_supplier; +} + +/*! + \qmlproperty Icon Place::icon + + This property holds a graphical icon which can be used to represent the place. +*/ +QDeclarativePlaceIcon *QDeclarativePlace::icon() const +{ + return m_icon; +} + +void QDeclarativePlace::setIcon(QDeclarativePlaceIcon *icon) +{ + if (m_icon == icon) + return; + + if (m_icon && m_icon->parent() == this) + delete m_icon; + + m_icon = icon; + emit iconChanged(); +} + +/*! + \qmlproperty string Place::name + + This property holds the name of the place which can be used to represent the place. +*/ +void QDeclarativePlace::setName(const QString &name) +{ + if (m_src.name() != name) { + m_src.setName(name); + emit nameChanged(); + } +} + +QString QDeclarativePlace::name() const +{ + return m_src.name(); +} + +/*! + \qmlproperty string Place::placeId + + This property holds the unique identifier of the place. The place identifier is only meaningful to the + \l Plugin that generated it and is not transferable between \l {Plugin}{Plugins}. The place id + is not guaranteed to be universally unique, but unique within the \l Plugin that generated it. + + If only the place identifier is known, all other place data can fetched from the \l Plugin. + + \snippet declarative/places.qml Place placeId +*/ +void QDeclarativePlace::setPlaceId(const QString &placeId) +{ + if (m_src.placeId() != placeId) { + m_src.setPlaceId(placeId); + emit placeIdChanged(); + } +} + +QString QDeclarativePlace::placeId() const +{ + return m_src.placeId(); +} + +/*! + \qmlproperty string Place::attribution + + This property holds a rich text attribution string for the place. + Some providers may require that the attribution be shown to the user + whenever a place is displayed. The contents of this property should + be shown to the user if it is not empty. +*/ +void QDeclarativePlace::setAttribution(const QString &attribution) +{ + if (m_src.attribution() != attribution) { + m_src.setAttribution(attribution); + emit attributionChanged(); + } +} + +QString QDeclarativePlace::attribution() const +{ + return m_src.attribution(); +} + +/*! + \qmlproperty bool Place::detailsFetched + + This property indicates whether the details of the place have been fetched. If this property + is false, the place details have not yet been fetched. Fetching can be done by invoking the + \l getDetails() method. + + \sa getDetails() +*/ +bool QDeclarativePlace::detailsFetched() const +{ + return m_src.detailsFetched(); +} + +/*! + \qmlproperty enumeration Place::status + + This property holds the status of the place. It can be one of: + + \table + \row + \li Place.Ready + \li No error occurred during the last operation, further operations may be performed on + the place. + \row + \li Place.Saving + \li The place is currently being saved, no other operation may be performed until + complete. + \row + \li Place.Fetching + \li The place details are currently being fetched, no other operations may be performed + until complete. + \row + \li Place.Removing + \li The place is currently being removed, no other operations can be performed until + complete. + \row + \li Place.Error + \li An error occurred during the last operation, further operations can still be + performed on the place. + \endtable + + The status of a place can be checked by connecting the status property + to a handler function, and then have the handler function process the change + in status. + + \snippet declarative/places.qml Place checkStatus + \dots + \snippet declarative/places.qml Place checkStatus handler + +*/ +void QDeclarativePlace::setStatus(Status status, const QString &errorString) +{ + Status originalStatus = m_status; + m_status = status; + m_errorString = errorString; + + if (originalStatus != m_status) + emit statusChanged(); +} + +QDeclarativePlace::Status QDeclarativePlace::status() const +{ + return m_status; +} + +/*! + \internal +*/ +void QDeclarativePlace::finished() +{ + if (!m_reply) + return; + + if (m_reply->error() == QPlaceReply::NoError) { + switch (m_reply->type()) { + case (QPlaceReply::IdReply) : { + QPlaceIdReply *idReply = qobject_cast<QPlaceIdReply *>(m_reply); + + switch (idReply->operationType()) { + case QPlaceIdReply::SavePlace: + setPlaceId(idReply->id()); + break; + case QPlaceIdReply::RemovePlace: + break; + default: + //Other operation types shouldn't ever be received. + break; + } + break; + } + case (QPlaceReply::DetailsReply): { + QPlaceDetailsReply *detailsReply = qobject_cast<QPlaceDetailsReply *>(m_reply); + setPlace(detailsReply->place()); + break; + } + default: + //other types of replies shouldn't ever be received. + break; + } + + m_errorString.clear(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativePlace::Ready); + } else { + QString errorString = m_reply->errorString(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativePlace::Error, errorString); + } +} + +/*! + \internal +*/ +void QDeclarativePlace::contactsModified(const QString &key, const QVariant &) +{ + primarySignalsEmission(key); +} + +/*! + \internal +*/ +void QDeclarativePlace::cleanupDeletedCategories() +{ + foreach (QDeclarativeCategory * category, m_categoriesToBeDeleted) { + if (category->parent() == this) + delete category; + } + m_categoriesToBeDeleted.clear(); +} + +/*! + \qmlmethod void Place::getDetails() + + This method starts fetching place details. + + The \l status property will change to Place.Fetching while the fetch is in progress. On + success the object's properties will be updated, \l status will be set to Place.Ready and + \l detailsFetched will be set to true. On error \l status will be set to Place.Error. The + \l errorString() method can be used to get the details of the error. +*/ +void QDeclarativePlace::getDetails() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->getPlaceDetails(placeId()); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + setStatus(QDeclarativePlace::Fetching); +} + +/*! + \qmlmethod void Place::save() + + This method performs a save operation on the place. + + The \l status property will change to Place.Saving while the save operation is in progress. On + success the \l status will be set to Place.Ready. On error \l status will be set to Place.Error. + The \l errorString() method can be used to get the details of the error. + + If the \l placeId property was previously empty, it will be assigned a valid value automatically + during a successful save operation. + + Note that a \l PlaceSearchModel will call Place::getDetails on any place that it detects an update + on. A consequence of this is that whenever a Place from a \l PlaceSearchModel is successfully saved, + it will be followed by a fetch of place details, leading to a sequence of state changes + of \c Saving, \c Ready, \c Fetching, \c Ready. + +*/ +void QDeclarativePlace::save() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->savePlace(place()); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + setStatus(QDeclarativePlace::Saving); +} + +/*! + \qmlmethod void Place::remove() + + This method performs a remove operation on the place. + + The \l status property will change to Place.Removing while the save operation is in progress. + On success \l status will be set to Place.Ready. On error \l status will be set to + Place.Error. The \l errorString() method can be used to get the details of the error. +*/ +void QDeclarativePlace::remove() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->removePlace(place().placeId()); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + setStatus(QDeclarativePlace::Removing); +} + +/*! + \qmlmethod string Place::errorString() + + Returns a string description of the error of the last operation. If the last operation + completed successfully then the string is empty. +*/ +QString QDeclarativePlace::errorString() const +{ + return m_errorString; +} + +/*! + \qmlproperty string Place::primaryPhone + + This property holds the primary phone number of the place. If no "phone" contact detail is + defined for this place, this property will be an empty string. It is equivalent to: + + + \snippet declarative/places.qml Place primaryPhone +*/ +QString QDeclarativePlace::primaryPhone() const +{ + return primaryValue(QPlaceContactDetail::Phone); +} + +/*! + \qmlproperty string Place::primaryFax + + This property holds the primary fax number of the place. If no "fax" contact detail is + defined for this place this property will be an empty string. It is equivalent to + + \snippet declarative/places.qml Place primaryFax +*/ +QString QDeclarativePlace::primaryFax() const +{ + return primaryValue(QPlaceContactDetail::Fax); +} + +/*! + \qmlproperty string Place::primaryEmail + + This property holds the primary email address of the place. If no "email" contact detail is + defined for this place this property will be an empty string. It is equivalent to + + \snippet declarative/places.qml Place primaryEmail +*/ +QString QDeclarativePlace::primaryEmail() const +{ + return primaryValue(QPlaceContactDetail::Email); +} + +/*! + \qmlproperty string Place::primaryWebsite + + This property holds the primary website url of the place. If no "website" contact detail is + defined for this place this property will be an empty string. It is equivalent to + + \snippet declarative/places.qml Place primaryWebsite +*/ + +QUrl QDeclarativePlace::primaryWebsite() const +{ + return QUrl(primaryValue(QPlaceContactDetail::Website)); +} + +/*! + \qmlproperty ExtendedAttributes Place::extendedAttributes + + This property holds the extended attributes of a place. Extended attributes are additional + information about a place not covered by the place's properties. +*/ +QQmlPropertyMap *QDeclarativePlace::extendedAttributes() const +{ + return m_extendedAttributes; +} + +/*! + \qmlproperty ContactDetails Place::contactDetails + + This property holds the contact information for this place, for example a phone number or + a website URL. This property is a map of \l ContactDetail objects. +*/ +QDeclarativeContactDetails *QDeclarativePlace::contactDetails() const +{ + return m_contactDetails; +} + +/*! + \qmlproperty list<Category> Place::categories + + This property holds the list of categories this place is a member of. The categories that can + be assigned to a place are specific to each \l plugin. +*/ +QQmlListProperty<QDeclarativeCategory> QDeclarativePlace::categories() +{ + return QQmlListProperty<QDeclarativeCategory>(this, + 0, // opaque data parameter + category_append, + category_count, + category_at, + category_clear); +} + +/*! + \internal +*/ +void QDeclarativePlace::category_append(QQmlListProperty<QDeclarativeCategory> *prop, + QDeclarativeCategory *value) +{ + QDeclarativePlace *object = static_cast<QDeclarativePlace *>(prop->object); + + if (object->m_categoriesToBeDeleted.contains(value)) + object->m_categoriesToBeDeleted.removeAll(value); + + if (!object->m_categories.contains(value)) { + object->m_categories.append(value); + QList<QPlaceCategory> list = object->m_src.categories(); + list.append(value->category()); + object->m_src.setCategories(list); + + emit object->categoriesChanged(); + } +} + +/*! + \internal +*/ +int QDeclarativePlace::category_count(QQmlListProperty<QDeclarativeCategory> *prop) +{ + return static_cast<QDeclarativePlace *>(prop->object)->m_categories.count(); +} + +/*! + \internal +*/ +QDeclarativeCategory *QDeclarativePlace::category_at(QQmlListProperty<QDeclarativeCategory> *prop, + int index) +{ + QDeclarativePlace *object = static_cast<QDeclarativePlace *>(prop->object); + QDeclarativeCategory *res = NULL; + if (object->m_categories.count() > index && index > -1) { + res = object->m_categories[index]; + } + return res; +} + +/*! + \internal +*/ +void QDeclarativePlace::category_clear(QQmlListProperty<QDeclarativeCategory> *prop) +{ + QDeclarativePlace *object = static_cast<QDeclarativePlace *>(prop->object); + if (object->m_categories.isEmpty()) + return; + + for (int i = 0; i < object->m_categories.count(); ++i) { + if (object->m_categories.at(i)->parent() == object) + object->m_categoriesToBeDeleted.append(object->m_categories.at(i)); + } + + object->m_categories.clear(); + object->m_src.setCategories(QList<QPlaceCategory>()); + emit object->categoriesChanged(); + QMetaObject::invokeMethod(object, "cleanupDeletedCategories", Qt::QueuedConnection); +} + +/*! + \internal +*/ +void QDeclarativePlace::synchronizeCategories() +{ + qDeleteAll(m_categories); + m_categories.clear(); + foreach (const QPlaceCategory &value, m_src.categories()) { + QDeclarativeCategory *declarativeValue = new QDeclarativeCategory(value, m_plugin, this); + m_categories.append(declarativeValue); + } +} + +/*! + \qmlproperty enumeration Place::visibility + + This property holds the visibility of the place. It can be one of: + + \table + \row + \li Place.UnspecifiedVisibility + \li The visibility of the place is unspecified, the default visibility of the \l Plugin + will be used. + \row + \li Place.DeviceVisibility + \li The place is limited to the current device. The place will not be transferred off + of the device. + \row + \li Place.PrivateVisibility + \li The place is private to the current user. The place may be transferred to an online + service but is only ever visible to the current user. + \row + \li Place.PublicVisibility + \li The place is public. + \endtable + + Note that visibility does not affect how the place is displayed + in the user-interface of an application on the device. Instead, + it defines the sharing semantics of the place. +*/ +QDeclarativePlace::Visibility QDeclarativePlace::visibility() const +{ + return static_cast<QDeclarativePlace::Visibility>(m_src.visibility()); +} + +void QDeclarativePlace::setVisibility(Visibility visibility) +{ + if (static_cast<QDeclarativePlace::Visibility>(m_src.visibility()) == visibility) + return; + + m_src.setVisibility(static_cast<QLocation::Visibility>(visibility)); + emit visibilityChanged(); +} + +/*! + \qmlproperty Place Place::favorite + + This property holds the favorite instance of a place. +*/ +QDeclarativePlace *QDeclarativePlace::favorite() const +{ + return m_favorite; +} + +void QDeclarativePlace::setFavorite(QDeclarativePlace *favorite) +{ + + if (m_favorite == favorite) + return; + + if (m_favorite && m_favorite->parent() == this) + delete m_favorite; + + m_favorite = favorite; + emit favoriteChanged(); +} + +/*! + \qmlmethod void Place::copyFrom(Place original) + + Copies data from an \a original place into this place. Only data that is supported by this + place's plugin is copied over and plugin specific data such as place identifier is not copied over. +*/ +void QDeclarativePlace::copyFrom(QDeclarativePlace *original) +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + setPlace(placeManager->compatiblePlace(original->place())); +} + +/*! + \qmlmethod void Place::initializeFavorite(Plugin destinationPlugin) + + Creates a favorite instance for the place which is to be saved into the + \a destination plugin. This method does nothing if the favorite property is + not null. +*/ +void QDeclarativePlace::initializeFavorite(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_favorite == 0) { + QDeclarativePlace *place = new QDeclarativePlace(this); + place->setPlugin(plugin); + place->copyFrom(this); + setFavorite(place); + } +} + +/*! + \internal +*/ +void QDeclarativePlace::synchronizeExtendedAttributes() +{ + QStringList keys = m_extendedAttributes->keys(); + foreach (const QString &key, keys) + m_extendedAttributes->clear(key); + + QStringList attributeTypes = m_src.extendedAttributeTypes(); + foreach (const QString &attributeType, attributeTypes) { + m_extendedAttributes->insert(attributeType, + qVariantFromValue(new QDeclarativePlaceAttribute(m_src.extendedAttribute(attributeType)))); + } + + emit extendedAttributesChanged(); +} + +/*! + \internal +*/ +void QDeclarativePlace::synchronizeContacts() +{ + //clear out contact data + foreach (const QString &contactType, m_contactDetails->keys()) { + QList<QVariant> contacts = m_contactDetails->value(contactType).toList(); + foreach (const QVariant &var, contacts) { + QObject *obj = var.value<QObject *>(); + if (obj->parent() == this) + delete obj; + } + m_contactDetails->insert(contactType, QVariantList()); + } + + //insert new contact data from source place + foreach (const QString &contactType, m_src.contactTypes()) { + QList<QPlaceContactDetail> sourceContacts = m_src.contactDetails(contactType); + QVariantList declContacts; + foreach (const QPlaceContactDetail &sourceContact, sourceContacts) { + QDeclarativeContactDetail *declContact = new QDeclarativeContactDetail(this); + declContact->setContactDetail(sourceContact); + declContacts.append(QVariant::fromValue(qobject_cast<QObject *>(declContact))); + } + m_contactDetails->insert(contactType, declContacts); + } + primarySignalsEmission(); +} + +/*! + \internal + Helper function to emit the signals for the primary___() + fields. It is expected that the values of the primary___() + functions have already been modified to new values. +*/ +void QDeclarativePlace::primarySignalsEmission(const QString &type) +{ + if (type.isEmpty() || type == QPlaceContactDetail::Phone) { + if (m_prevPrimaryPhone != primaryPhone()) { + m_prevPrimaryPhone = primaryPhone(); + emit primaryPhoneChanged(); + } + if (!type.isEmpty()) + return; + } + + if (type.isEmpty() || type == QPlaceContactDetail::Email) { + if (m_prevPrimaryEmail != primaryEmail()) { + m_prevPrimaryEmail = primaryEmail(); + emit primaryEmailChanged(); + } + if (!type.isEmpty()) + return; + } + + if (type.isEmpty() || type == QPlaceContactDetail::Website) { + if (m_prevPrimaryWebsite != primaryWebsite()) { + m_prevPrimaryWebsite = primaryWebsite(); + emit primaryWebsiteChanged(); + } + if (!type.isEmpty()) + return; + } + + if (type.isEmpty() || type == QPlaceContactDetail::Fax) { + if (m_prevPrimaryFax != primaryFax()) { + m_prevPrimaryFax = primaryFax(); + emit primaryFaxChanged(); + } + } +} + +/*! + \internal + Helper function to return the manager, this manager is intended to be used + to perform the next operation. If a an operation is currently underway + then return a null pointer. +*/ +QPlaceManager *QDeclarativePlace::manager() +{ + if (m_status != QDeclarativePlace::Ready && m_status != QDeclarativePlace::Error) + return 0; + + if (m_reply) { + m_reply->abort(); + m_reply->deleteLater(); + m_reply = 0; + } + + if (!m_plugin) { + qmlWarning(this) << QStringLiteral("Plugin is not assigned to place."); + return 0; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider) + return 0; + + QPlaceManager *placeManager = serviceProvider->placeManager(); + + if (!placeManager) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return 0; + } + + return placeManager; +} + +/*! + \internal +*/ +QString QDeclarativePlace::primaryValue(const QString &contactType) const +{ + QVariant value = m_contactDetails->value(contactType); + if (value.userType() == qMetaTypeId<QJSValue>()) + value = value.value<QJSValue>().toVariant(); + + if (value.userType() == QVariant::List) { + QVariantList detailList = m_contactDetails->value(contactType).toList(); + if (!detailList.isEmpty()) { + QDeclarativeContactDetail *primaryDetail = qobject_cast<QDeclarativeContactDetail *>(detailList.at(0).value<QObject *>()); + if (primaryDetail) + return primaryDetail->value(); + } + } else if (value.userType() == QMetaType::QObjectStar) { + QDeclarativeContactDetail *primaryDetail = qobject_cast<QDeclarativeContactDetail *>(m_contactDetails->value(contactType).value<QObject *>()); + if (primaryDetail) + return primaryDetail->value(); + } + + return QString(); +} + +QT_END_NAMESPACE |