| /**************************************************************************** |
| ** |
| ** 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 "qdeclarativesearchresultmodel_p.h" |
| #include "qdeclarativeplace_p.h" |
| #include "qdeclarativeplaceicon_p.h" |
| |
| #include <QtQml/QQmlEngine> |
| #include <QtQml/QQmlInfo> |
| #include <QtLocation/QGeoServiceProvider> |
| #include <QtLocation/QPlaceSearchReply> |
| #include <QtLocation/QPlaceManager> |
| #include <QtLocation/QPlaceMatchRequest> |
| #include <QtLocation/QPlaceMatchReply> |
| #include <QtLocation/QPlaceResult> |
| #include <QtLocation/QPlaceProposedSearchResult> |
| #include <QtLocation/private/qplacesearchrequest_p.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| /*! |
| \qmltype PlaceSearchModel |
| \instantiates QDeclarativeSearchResultModel |
| \inqmlmodule QtLocation |
| \ingroup qml-QtLocation5-places |
| \ingroup qml-QtLocation5-places-models |
| \since QtLocation 5.5 |
| |
| \brief Provides access to place search results. |
| |
| PlaceSearchModel provides a model of place search results within the \l searchArea. The |
| \l searchTerm and \l categories properties can be set to restrict the search results to |
| places matching those criteria. |
| |
| The PlaceSearchModel returns both sponsored and |
| \l {http://en.wikipedia.org/wiki/Organic_search}{organic search results}. Sponsored search |
| results will have the \c sponsored role set to true. |
| |
| \target PlaceSearchModel Roles |
| The model returns data for the following roles: |
| |
| \table |
| \header |
| \li Role |
| \li Type |
| \li Description |
| \row |
| \li type |
| \li enum |
| \li The type of search result. |
| \row |
| \li title |
| \li string |
| \li A string describing the search result. |
| \row |
| \li icon |
| \li PlaceIcon |
| \li Icon representing the search result. |
| \row |
| \li distance |
| \li real |
| \li Valid only when the \c type role is \c PlaceResult, the distance to the place |
| from the center of the \l searchArea. If no \l searchArea |
| has been specified, the distance is NaN. |
| \row |
| \li place |
| \li \l Place |
| \li Valid only when the \c type role is \c PlaceResult, an object representing the |
| place. |
| \row |
| \li sponsored |
| \li bool |
| \li Valid only when the \c type role is \c PlaceResult, true if the search result is a |
| sponsored result. |
| \endtable |
| |
| \section2 Search Result Types |
| |
| The \c type role can take on the following values: |
| |
| \table |
| \row |
| \li PlaceSearchModel.UnknownSearchResult |
| \li The contents of the search result are unknown. |
| \row |
| \li PlaceSearchModel.PlaceResult |
| \li The search result contains a place. |
| \row |
| \li PlaceSearchModel.ProposedSearchResult |
| \li The search result contains a proposed search which may be relevant. |
| \endtable |
| |
| |
| It can often be helpful to use a \l Loader to create a delegate |
| that will choose different \l {Component}s based on the search result type. |
| |
| \snippet declarative/places_loader.qml Handle Result Types |
| |
| \section1 Detection of Updated and Removed Places |
| |
| The PlaceSearchModel listens for places that have been updated or removed from its plugin's backend. |
| If it detects that a place has been updated and that place is currently present in the model, then |
| it will call \l Place::getDetails to refresh the details. If it detects that a place has been |
| removed, then correspondingly the place will be removed from the model if it is currently |
| present. |
| |
| \section1 Example |
| |
| The following example shows how to use the PlaceSearchModel to search for Pizza restaurants in |
| close proximity of a given position. A \l searchTerm and \l searchArea are provided to the model |
| and \l update() is used to perform a lookup query. Note that the model does not incrementally |
| fetch search results, but rather performs a single fetch when \l update() is run. The \l count |
| is set to the number of search results returned during the fetch. |
| |
| \snippet places_list/places_list.qml Imports |
| \codeline |
| \snippet places_list/places_list.qml PlaceSearchModel |
| |
| \sa CategoryModel, {QPlaceManager} |
| |
| \section1 Paging |
| The PlaceSearchModel API has some limited support |
| for paging. The \l nextPage() and \l previousPage() functions as well as |
| the \l limit property can be used to access |
| paged search results. When the \l limit property is set |
| the search result page contains at most \l limit entries (of type place result). |
| For example, if the backend has 5 search results in total |
| [a,b,c,d,e], and assuming the first page is shown and limit of 3 has been set |
| then a,b,c is returned. The \l nextPage() would return d,e. The |
| \l nextPagesAvailable and \l previousPagesAvailable properties |
| can be used to check for further pages. At the moment the API does not |
| support the means to retrieve the total number of items available from the |
| backed. Note that support for \l nextPage(), previousPage() and \l limit can vary |
| according to the \l plugin. |
| */ |
| |
| /*! |
| \qmlproperty Plugin PlaceSearchModel::plugin |
| |
| This property holds the \l Plugin which will be used to perform the search. |
| */ |
| |
| /*! |
| \qmlproperty Plugin PlaceSearchModel::favoritesPlugin |
| |
| This property holds the \l Plugin which will be used to search for favorites. |
| Any places from the search which can be cross-referenced or matched |
| in the favoritesPlugin will have their \l {Place::favorite}{favorite} property |
| set to the corresponding \l Place from the favoritesPlugin. |
| |
| If the favoritesPlugin is not set, the \l {Place::favorite}{favorite} property |
| of the places in the results will always be null. |
| |
| \sa Favorites |
| */ |
| |
| /*! |
| \qmlproperty VariantMap PlaceSearchModel::favoritesMatchParameters |
| |
| This property holds a set of parameters used to specify how search result places |
| are matched to favorites in the favoritesPlugin. |
| |
| By default the parameter map is empty and implies that the favorites plugin |
| matches by \l {Alternative Identifier Cross-Referencing}{alternative identifiers}. Generally, |
| an application developer will not need to set this property. |
| |
| In cases where the favorites plugin does not support matching by alternative identifiers, |
| then the \l {Qt Location#Plugin References and Parameters}{plugin documentation} should |
| be consulted to see precisely what key-value parameters to set. |
| */ |
| |
| /*! |
| \qmlproperty variant PlaceSearchModel::searchArea |
| |
| This property holds the search area. The search result returned by the model will be within |
| the search area. |
| |
| If this property is set to a \l {geocircle} its |
| \l {geocircle}{radius} property may be left unset, in which case the \l Plugin |
| will choose an appropriate radius for the search. |
| |
| Support for specifying a search area can vary according to the \l plugin backend |
| implementation. For example, some may support a search center only while others may only |
| support geo rectangles. |
| */ |
| |
| /*! |
| \qmlproperty int PlaceSearchModel::limit |
| |
| This property holds the limit of the number of items that will be returned. |
| */ |
| |
| /*! |
| \qmlproperty bool PlaceSearchModel::previousPagesAvailable |
| |
| This property holds whether there is one or more previous pages of search results available. |
| |
| \sa previousPage() |
| */ |
| |
| /*! |
| \qmlproperty bool PlaceSearchModel::nextPagesAvailable |
| |
| This property holds whether there is one or more additional pages of search results available. |
| |
| \sa nextPage() |
| */ |
| |
| /*! |
| \qmlproperty enum PlaceSearchModel::status |
| |
| This property holds the status of the model. It can be one of: |
| |
| \table |
| \row |
| \li PlaceSearchModel.Null |
| \li No search query has been executed. The model is empty. |
| \row |
| \li PlaceSearchModel.Ready |
| \li The search query has completed, and the results are available. |
| \row |
| \li PlaceSearchModel.Loading |
| \li A search query is currently being executed. |
| \row |
| \li PlaceSearchModel.Error |
| \li An error occurred when executing the previous search query. |
| \endtable |
| */ |
| |
| /*! |
| \qmlproperty bool PlaceSearchModel::incremental |
| |
| This property controls how paging will affect the PlaceSearchModel. |
| If true, calling \l previousPage or \l nextPage will not reset the model, |
| but new results will instead be appended to the model. |
| Default is false. |
| |
| \since QtLocation 5.12 |
| */ |
| |
| |
| /*! |
| \qmlmethod void PlaceSearchModel::update() |
| |
| Updates the model based on the provided query parameters. The model will be populated with a |
| list of places matching the search parameters specified by the type's properties. Search |
| criteria is specified by setting properties such as the \l searchTerm, \l categories, \l searchArea and \l limit. |
| Support for these properties may vary according to \l plugin. \c update() then |
| submits the set of criteria to the \l plugin to process. |
| |
| While the model is updating the \l status of the model is set to |
| \c PlaceSearchModel.Loading. If the model is successfully updated the \l status is set to |
| \c PlaceSearchModel.Ready, while if it unsuccessfully completes, the \l status is set to |
| \c PlaceSearchModel.Error and the model cleared. |
| |
| \code |
| PlaceSearchModel { |
| id: model |
| plugin: backendPlugin |
| searchArea: QtPositioning.circle(QtPositioning.coordinate(10, 10)) |
| ... |
| } |
| |
| MouseArea { |
| ... |
| onClicked: { |
| model.searchTerm = "pizza"; |
| model.categories = null; //not searching by any category |
| model.searchArea.center.latitude = -27.5; |
| model.searchArea.center.longitude = 153; |
| model.update(); |
| } |
| } |
| \endcode |
| |
| \sa cancel(), status |
| */ |
| |
| /*! |
| \qmlmethod void PlaceSearchModel::cancel() |
| |
| Cancels an ongoing search operation immediately and sets the model |
| status to PlaceSearchModel.Ready. The model retains any search |
| results it had before the operation was started. |
| |
| If an operation is not ongoing, invoking cancel() has no effect. |
| |
| \sa update(), status |
| */ |
| |
| /*! |
| \qmlmethod void PlaceSearchModel::reset() |
| |
| Resets the model. All search results are cleared, any outstanding requests are aborted and |
| possible errors are cleared. Model status will be set to PlaceSearchModel.Null. |
| */ |
| |
| /*! |
| \qmlmethod string PlaceSearchModel::errorString() const |
| |
| This read-only property holds the textual presentation of the latest place search model error. |
| If no error has occurred or if the model was cleared, an empty string is returned. |
| |
| An empty string may also be returned if an error occurred which has no associated |
| textual representation. |
| */ |
| |
| /*! |
| \qmlmethod void PlaceSearchModel::previousPage() |
| |
| Updates the model to display the previous page of search results. If there is no previous page |
| then this method does nothing. |
| */ |
| |
| /*! |
| \qmlmethod void PlaceSearchModel::nextPage() |
| |
| Updates the model to display the next page of search results. If there is no next page then |
| this method does nothing. |
| */ |
| |
| QDeclarativeSearchResultModel::QDeclarativeSearchResultModel(QObject *parent) |
| : QDeclarativeSearchModelBase(parent), m_favoritesPlugin(0) |
| { |
| } |
| |
| QDeclarativeSearchResultModel::~QDeclarativeSearchResultModel() |
| { |
| } |
| |
| /*! |
| \qmlproperty string PlaceSearchModel::searchTerm |
| |
| This property holds search term used in query. The search term is a free-form text string. |
| */ |
| QString QDeclarativeSearchResultModel::searchTerm() const |
| { |
| return m_request.searchTerm(); |
| } |
| |
| void QDeclarativeSearchResultModel::setSearchTerm(const QString &searchTerm) |
| { |
| m_request.setSearchContext(QVariant()); |
| |
| if (m_request.searchTerm() == searchTerm) |
| return; |
| |
| m_request.setSearchTerm(searchTerm); |
| emit searchTermChanged(); |
| } |
| |
| /*! |
| \qmlproperty list<Category> PlaceSearchModel::categories |
| |
| This property holds a list of categories to be used when searching. Returned search results |
| will be for places that match at least one of the categories. |
| */ |
| QQmlListProperty<QDeclarativeCategory> QDeclarativeSearchResultModel::categories() |
| { |
| return QQmlListProperty<QDeclarativeCategory>(this, |
| 0, // opaque data parameter |
| categories_append, |
| categories_count, |
| category_at, |
| categories_clear); |
| } |
| |
| void QDeclarativeSearchResultModel::categories_append(QQmlListProperty<QDeclarativeCategory> *list, |
| QDeclarativeCategory *declCategory) |
| { |
| QDeclarativeSearchResultModel *searchModel = qobject_cast<QDeclarativeSearchResultModel *>(list->object); |
| if (searchModel && declCategory) { |
| searchModel->m_request.setSearchContext(QVariant()); |
| searchModel->m_categories.append(declCategory); |
| QList<QPlaceCategory> categories = searchModel->m_request.categories(); |
| categories.append(declCategory->category()); |
| searchModel->m_request.setCategories(categories); |
| emit searchModel->categoriesChanged(); |
| } |
| } |
| |
| int QDeclarativeSearchResultModel::categories_count(QQmlListProperty<QDeclarativeCategory> *list) |
| { |
| QDeclarativeSearchResultModel *searchModel = qobject_cast<QDeclarativeSearchResultModel *>(list->object); |
| if (searchModel) |
| return searchModel->m_categories.count(); |
| else |
| return -1; |
| } |
| |
| QDeclarativeCategory *QDeclarativeSearchResultModel::category_at(QQmlListProperty<QDeclarativeCategory> *list, |
| int index) |
| { |
| QDeclarativeSearchResultModel *searchModel = qobject_cast<QDeclarativeSearchResultModel *>(list->object); |
| if (searchModel && (searchModel->m_categories.count() > index) && (index > -1)) |
| return searchModel->m_categories.at(index); |
| else |
| return 0; |
| } |
| |
| void QDeclarativeSearchResultModel::categories_clear(QQmlListProperty<QDeclarativeCategory> *list) |
| { |
| QDeclarativeSearchResultModel *searchModel = qobject_cast<QDeclarativeSearchResultModel *>(list->object); |
| if (searchModel) { |
| //note: we do not need to delete each of the objects in m_categories since the search model |
| //should never be the parent of the categories anyway. |
| searchModel->m_request.setSearchContext(QVariant()); |
| searchModel->m_categories.clear(); |
| searchModel->m_request.setCategories(QList<QPlaceCategory>()); |
| emit searchModel->categoriesChanged(); |
| } |
| } |
| |
| /*! |
| \qmlproperty string PlaceSearchModel::recommendationId |
| |
| This property holds the placeId to be used in order to find recommendations |
| for similar places. |
| */ |
| QString QDeclarativeSearchResultModel::recommendationId() const |
| { |
| return m_request.recommendationId(); |
| } |
| |
| void QDeclarativeSearchResultModel::setRecommendationId(const QString &placeId) |
| { |
| if (m_request.recommendationId() == placeId) |
| return; |
| |
| m_request.setRecommendationId(placeId); |
| emit recommendationIdChanged(); |
| } |
| |
| /*! |
| \qmlproperty enumeration PlaceSearchModel::relevanceHint |
| |
| This property holds a relevance hint used in the search query. The hint is given to the |
| provider to help but not dictate the ranking of results. For example, the distance hint may |
| give closer places a higher ranking but it does not necessarily mean the results will be |
| strictly ordered according to distance. A provider may ignore the hint altogether. |
| |
| \table |
| \row |
| \li SearchResultModel.UnspecifiedHint |
| \li No relevance hint is given to the provider. |
| \row |
| \li SearchResultModel.DistanceHint |
| \li The distance of the place from the user's current location is important to the user. |
| This hint is only meaningful when a circular search area is used. |
| \row |
| \li SearchResultModel.LexicalPlaceNameHint |
| \li The lexical ordering of place names (in ascending alphabetical order) is relevant to |
| the user. This hint is useful for providers based on a local data store. |
| \endtable |
| */ |
| QDeclarativeSearchResultModel::RelevanceHint QDeclarativeSearchResultModel::relevanceHint() const |
| { |
| return static_cast<QDeclarativeSearchResultModel::RelevanceHint>(m_request.relevanceHint()); |
| } |
| |
| void QDeclarativeSearchResultModel::setRelevanceHint(QDeclarativeSearchResultModel::RelevanceHint hint) |
| { |
| if (m_request.relevanceHint() != static_cast<QPlaceSearchRequest::RelevanceHint>(hint)) { |
| m_request.setRelevanceHint(static_cast<QPlaceSearchRequest::RelevanceHint>(hint)); |
| emit relevanceHintChanged(); |
| } |
| } |
| |
| /*! |
| \qmlproperty enum PlaceSearchModel::visibilityScope |
| |
| This property holds the visibility scope of the places to search. Only places with the |
| specified visibility will be returned in the search results. |
| |
| The visibility scope can be one of: |
| |
| \table |
| \row |
| \li Place.UnspecifiedVisibility |
| \li No explicit visibility scope specified, places with any visibility may be part of |
| search results. |
| \row |
| \li Place.DeviceVisibility |
| \li Only places stored on the local device will be part of the search results. |
| \row |
| \li Place.PrivateVisibility |
| \li Only places that are private to the current user will be part of the search results. |
| \row |
| \li Place.PublicVisibility |
| \li Only places that are public will be part of the search results. |
| \endtable |
| */ |
| QDeclarativePlace::Visibility QDeclarativeSearchResultModel::visibilityScope() const |
| { |
| return QDeclarativePlace::Visibility(int(m_visibilityScope)); |
| } |
| |
| void QDeclarativeSearchResultModel::setVisibilityScope(QDeclarativePlace::Visibility visibilityScope) |
| { |
| QLocation::VisibilityScope scope = QLocation::VisibilityScope(visibilityScope); |
| |
| if (m_visibilityScope == scope) |
| return; |
| |
| m_visibilityScope = scope; |
| emit visibilityScopeChanged(); |
| } |
| |
| /*! |
| \internal |
| */ |
| QDeclarativeGeoServiceProvider *QDeclarativeSearchResultModel::favoritesPlugin() const |
| { |
| return m_favoritesPlugin; |
| } |
| |
| /*! |
| \internal |
| */ |
| void QDeclarativeSearchResultModel::setFavoritesPlugin(QDeclarativeGeoServiceProvider *plugin) |
| { |
| |
| if (m_favoritesPlugin == plugin) |
| return; |
| |
| m_favoritesPlugin = plugin; |
| |
| if (m_favoritesPlugin) { |
| QGeoServiceProvider *serviceProvider = m_favoritesPlugin->sharedGeoServiceProvider(); |
| if (serviceProvider) { |
| QPlaceManager *placeManager = serviceProvider->placeManager(); |
| if (placeManager) { |
| if (placeManager->childCategoryIds().isEmpty()) { |
| QPlaceReply *reply = placeManager->initializeCategories(); |
| connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); |
| } |
| } |
| } |
| } |
| |
| emit favoritesPluginChanged(); |
| } |
| |
| /*! |
| \internal |
| */ |
| QVariantMap QDeclarativeSearchResultModel::favoritesMatchParameters() const |
| { |
| return m_matchParameters; |
| } |
| |
| /*! |
| \internal |
| */ |
| void QDeclarativeSearchResultModel::setFavoritesMatchParameters(const QVariantMap ¶meters) |
| { |
| if (m_matchParameters == parameters) |
| return; |
| |
| m_matchParameters = parameters; |
| emit favoritesMatchParametersChanged(); |
| } |
| |
| /*! |
| \internal |
| */ |
| int QDeclarativeSearchResultModel::rowCount(const QModelIndex &parent) const |
| { |
| Q_UNUSED(parent); |
| |
| return m_results.count(); |
| } |
| |
| void QDeclarativeSearchResultModel::clearData(bool suppressSignal) |
| { |
| QDeclarativeSearchModelBase::clearData(suppressSignal); |
| |
| qDeleteAll(m_places); |
| m_places.clear(); |
| qDeleteAll(m_icons); |
| m_icons.clear(); |
| if (!m_results.isEmpty()) { |
| m_results.clear(); |
| |
| if (!suppressSignal) |
| emit rowCountChanged(); |
| } |
| } |
| |
| QVariant QDeclarativeSearchResultModel::data(const QModelIndex &index, int role) const |
| { |
| if (index.row() > m_results.count()) |
| return QVariant(); |
| |
| const QPlaceSearchResult &result = m_results.at(index.row()); |
| |
| switch (role) { |
| case SearchResultTypeRole: |
| return result.type(); |
| case Qt::DisplayRole: |
| case TitleRole: |
| return result.title(); |
| case IconRole: |
| return QVariant::fromValue(static_cast<QObject *>(m_icons.at(index.row()))); |
| case DistanceRole: |
| if (result.type() == QPlaceSearchResult::PlaceResult) { |
| QPlaceResult placeResult = result; |
| return placeResult.distance(); |
| } |
| break; |
| case PlaceRole: |
| if (result.type() == QPlaceSearchResult::PlaceResult) |
| return QVariant::fromValue(static_cast<QObject *>(m_places.at(index.row()))); |
| break; |
| case SponsoredRole: |
| if (result.type() == QPlaceSearchResult::PlaceResult) { |
| QPlaceResult placeResult = result; |
| return placeResult.isSponsored(); |
| } |
| break; |
| } |
| return QVariant(); |
| } |
| |
| /*! |
| \internal |
| */ |
| QVariant QDeclarativeSearchResultModel::data(int index, const QString &role) const |
| { |
| QModelIndex modelIndex = createIndex(index, 0); |
| return data(modelIndex, roleNames().key(role.toLatin1())); |
| } |
| |
| QHash<int, QByteArray> QDeclarativeSearchResultModel::roleNames() const |
| { |
| QHash<int, QByteArray> roles = QDeclarativeSearchModelBase::roleNames(); |
| roles.insert(SearchResultTypeRole, "type"); |
| roles.insert(TitleRole, "title"); |
| roles.insert(IconRole, "icon"); |
| roles.insert(DistanceRole, "distance"); |
| roles.insert(PlaceRole, "place"); |
| roles.insert(SponsoredRole, "sponsored"); |
| |
| return roles; |
| } |
| |
| /*! |
| \qmlmethod void PlaceSearchModel::updateWith(int proposedSearchIndex) |
| |
| Updates the model based on the ProposedSearchResult at index \a proposedSearchIndex. The model |
| will be populated with a list of places matching the proposed search. Model status will be set |
| to PlaceSearchModel.Loading. If the model is updated successfully status will be set to |
| PlaceSearchModel.Ready. If an error occurs status will be set to PlaceSearchModel.Error and the |
| model cleared. |
| |
| If \a proposedSearchIndex does not reference a ProposedSearchResult this method does nothing. |
| */ |
| void QDeclarativeSearchResultModel::updateWith(int proposedSearchIndex) |
| { |
| if (m_results.at(proposedSearchIndex).type() != QPlaceSearchResult::ProposedSearchResult) |
| return; |
| |
| m_request = QPlaceProposedSearchResult(m_results.at(proposedSearchIndex)).searchRequest(); |
| update(); |
| } |
| |
| QPlaceReply *QDeclarativeSearchResultModel::sendQuery(QPlaceManager *manager, |
| const QPlaceSearchRequest &request) |
| { |
| Q_ASSERT(manager); |
| return manager->search(request); |
| } |
| |
| /*! |
| \internal |
| */ |
| void QDeclarativeSearchResultModel::initializePlugin(QDeclarativeGeoServiceProvider *plugin) |
| { |
| //disconnect the manager of the old plugin if we have one |
| if (m_plugin) { |
| QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); |
| if (serviceProvider) { |
| QPlaceManager *placeManager = serviceProvider->placeManager(); |
| if (placeManager) { |
| disconnect(placeManager, SIGNAL(placeUpdated(QString)), this, SLOT(placeUpdated(QString))); |
| disconnect(placeManager, SIGNAL(placeRemoved(QString)), this, SLOT(placeRemoved(QString))); |
| connect(placeManager, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); |
| } |
| } |
| } |
| |
| //connect to the manager of the new plugin. |
| if (plugin) { |
| QGeoServiceProvider *serviceProvider = plugin->sharedGeoServiceProvider(); |
| if (serviceProvider) { |
| QPlaceManager *placeManager = serviceProvider->placeManager(); |
| if (placeManager) { |
| connect(placeManager, SIGNAL(placeUpdated(QString)), this, SLOT(placeUpdated(QString))); |
| connect(placeManager, SIGNAL(placeRemoved(QString)), this, SLOT(placeRemoved(QString))); |
| disconnect(placeManager, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); |
| } |
| } |
| } |
| QDeclarativeSearchModelBase::initializePlugin(plugin); |
| } |
| |
| /*! |
| \internal |
| */ |
| void QDeclarativeSearchResultModel::queryFinished() |
| { |
| if (!m_reply) |
| return; |
| QPlaceReply *reply = m_reply; |
| m_reply = 0; |
| reply->deleteLater(); |
| |
| if (!m_incremental) |
| m_pages.clear(); |
| |
| if (reply->error() != QPlaceReply::NoError) { |
| m_resultsBuffer.clear(); |
| updateLayout(); |
| setStatus(Error, reply->errorString()); |
| return; |
| } |
| |
| if (reply->type() == QPlaceReply::SearchReply) { |
| QPlaceSearchReply *searchReply = qobject_cast<QPlaceSearchReply *>(reply); |
| Q_ASSERT(searchReply); |
| |
| const QPlaceSearchRequestPrivate *rpimpl = QPlaceSearchRequestPrivate::get(searchReply->request()); |
| if (!rpimpl->related || !m_incremental) |
| m_pages.clear(); |
| m_resultsBuffer = searchReply->results(); |
| bool alreadyLoaded = false; |
| if (m_pages.contains(rpimpl->page) && m_resultsBuffer == m_pages.value(rpimpl->page)) |
| alreadyLoaded = true; |
| m_pages.insert(rpimpl->page, m_resultsBuffer); |
| setPreviousPageRequest(searchReply->previousPageRequest()); |
| setNextPageRequest(searchReply->nextPageRequest()); |
| |
| // Performing favorite matching only upon finished() |
| if (!m_favoritesPlugin) { |
| updateLayout(); |
| setStatus(Ready); |
| } else { |
| QGeoServiceProvider *serviceProvider = m_favoritesPlugin->sharedGeoServiceProvider(); |
| if (!serviceProvider) { |
| updateLayout(); |
| setStatus(Error, QStringLiteral("Favorites plugin returns a null QGeoServiceProvider instance")); |
| return; |
| } |
| |
| QPlaceManager *favoritesManager = serviceProvider->placeManager(); |
| if (!favoritesManager) { |
| updateLayout(); |
| setStatus(Error, QStringLiteral("Favorites plugin returns a null QPlaceManager")); |
| return; |
| } |
| |
| QPlaceMatchRequest request; |
| if (m_matchParameters.isEmpty()) { |
| if (!m_plugin) { |
| setStatus(Error, QStringLiteral("Plugin not assigned")); |
| return; |
| } |
| |
| QVariantMap params; |
| params.insert(QPlaceMatchRequest::AlternativeId, QVariant(QString::fromLatin1("x_id_") + m_plugin->name())); |
| request.setParameters(params); |
| } else { |
| request.setParameters(m_matchParameters); |
| } |
| |
| request.setResults(m_resultsBuffer); |
| if (alreadyLoaded) |
| m_resultsBuffer.clear(); |
| m_reply = favoritesManager->matchingPlaces(request); |
| connect(m_reply, SIGNAL(finished()), this, SLOT(queryFinished())); |
| connect(m_reply, SIGNAL(contentUpdated()), this, SLOT(onContentUpdated())); |
| } |
| } else if (reply->type() == QPlaceReply::MatchReply) { |
| QPlaceMatchReply *matchReply = qobject_cast<QPlaceMatchReply *>(reply); |
| Q_ASSERT(matchReply); |
| updateLayout(matchReply->places()); |
| setStatus(Ready); |
| } else { |
| setStatus(Error, QStringLiteral("Unknown reply type")); |
| } |
| } |
| |
| void QDeclarativeSearchResultModel::onContentUpdated() |
| { |
| if (!m_reply) |
| return; |
| |
| QPlaceReply *reply = m_reply; // not finished, don't delete. |
| |
| if (!m_incremental) |
| m_pages.clear(); |
| |
| if (reply->error() != QPlaceReply::NoError) { |
| m_resultsBuffer.clear(); |
| updateLayout(); |
| setStatus(Error, reply->errorString()); |
| return; |
| } |
| |
| if (reply->type() == QPlaceReply::SearchReply) { |
| QPlaceSearchReply *searchReply = qobject_cast<QPlaceSearchReply *>(reply); |
| Q_ASSERT(searchReply); |
| |
| const QPlaceSearchRequestPrivate *rpimpl = QPlaceSearchRequestPrivate::get(searchReply->request()); |
| if (!rpimpl->related || !m_incremental) |
| m_pages.clear(); |
| m_resultsBuffer = searchReply->results(); |
| if (!(m_pages.contains(rpimpl->page) && m_resultsBuffer == m_pages.value(rpimpl->page))) { |
| m_pages.insert(rpimpl->page, m_resultsBuffer); |
| updateLayout(); |
| } |
| } else if (reply->type() == QPlaceReply::MatchReply) { |
| // ToDo: handle incremental match replies |
| } else { |
| setStatus(Error, QStringLiteral("Unknown reply type")); |
| } |
| } |
| |
| /*! |
| \qmlmethod Variant PlaceSearchModel::data(int index, string role) |
| |
| Returns the data for a given \a role at the specified row \a index. |
| */ |
| |
| /*! |
| \qmlproperty int PlaceSearchModel::count |
| |
| This property holds the number of results the model has. |
| |
| Note that it does not refer to the total number of search results |
| available in the backend. The total number of search results |
| is not currently supported by the API. |
| */ |
| |
| /*! |
| \internal |
| Note: m_results buffer should be correctly populated before |
| calling this function |
| */ |
| void QDeclarativeSearchResultModel::updateLayout(const QList<QPlace> &favoritePlaces) |
| { |
| const int oldRowCount = rowCount(); |
| int start = 0; |
| |
| if (m_incremental) { |
| if (!m_resultsBuffer.size()) |
| return; |
| |
| beginInsertRows(QModelIndex(), oldRowCount , oldRowCount + m_resultsBuffer.size() - 1); |
| m_results = resultsFromPages(); |
| start = oldRowCount; |
| } else { |
| beginResetModel(); |
| clearData(true); |
| m_results = m_resultsBuffer; |
| } |
| |
| m_resultsBuffer.clear(); |
| for (int i = start; i < m_results.count(); ++i) { |
| const QPlaceSearchResult &result = m_results.at(i); |
| |
| if (result.type() == QPlaceSearchResult::PlaceResult) { |
| QPlaceResult placeResult = result; |
| QDeclarativePlace *place = new QDeclarativePlace(placeResult.place(), plugin(), this); |
| m_places.append(place); |
| |
| if ((favoritePlaces.count() == m_results.count()) && favoritePlaces.at(i) != QPlace()) |
| m_places[i]->setFavorite(new QDeclarativePlace(favoritePlaces.at(i), |
| m_favoritesPlugin, m_places[i])); |
| } else if (result.type() == QPlaceSearchResult::ProposedSearchResult) { |
| m_places.append(0); |
| } |
| |
| QDeclarativePlaceIcon *icon = 0; |
| if (!result.icon().isEmpty()) |
| icon = new QDeclarativePlaceIcon(result.icon(), plugin(), this); |
| m_icons.append(icon); |
| } |
| |
| if (m_incremental) |
| endInsertRows(); |
| else |
| endResetModel(); |
| if (m_results.count() != oldRowCount) |
| emit rowCountChanged(); |
| } |
| |
| /*! |
| \internal |
| */ |
| void QDeclarativeSearchResultModel::placeUpdated(const QString &placeId) |
| { |
| int row = getRow(placeId); |
| if (row < 0 || row > m_places.count()) |
| return; |
| |
| if (m_places.at(row)) |
| m_places.at(row)->getDetails(); |
| } |
| |
| /*! |
| \internal |
| */ |
| void QDeclarativeSearchResultModel::placeRemoved(const QString &placeId) |
| { |
| int row = getRow(placeId); |
| if (row < 0 || row > m_places.count()) |
| return; |
| |
| beginRemoveRows(QModelIndex(), row, row); |
| delete m_places.at(row); |
| m_places.removeAt(row); |
| m_results.removeAt(row); |
| removePageRow(row); |
| endRemoveRows(); |
| |
| emit rowCountChanged(); |
| } |
| |
| QList<QPlaceSearchResult> QDeclarativeSearchResultModel::resultsFromPages() const |
| { |
| QList<QPlaceSearchResult> res; |
| for (const auto &e : m_pages) |
| res.append(e); |
| return res; |
| } |
| |
| void QDeclarativeSearchResultModel::removePageRow(int row) |
| { |
| int scanned = 0; |
| for (auto i = m_pages.begin(), end = m_pages.end(); i != end; ++i) { |
| QList<QPlaceSearchResult> &page = i.value(); |
| scanned += page.size(); |
| if (row >= scanned) |
| continue; |
| page.removeAt(row - scanned + page.size()); |
| return; |
| } |
| } |
| |
| /*! |
| \internal |
| */ |
| int QDeclarativeSearchResultModel::getRow(const QString &placeId) const |
| { |
| for (int i = 0; i < m_places.count(); ++i) { |
| if (!m_places.at(i)) |
| continue; |
| else if (m_places.at(i)->placeId() == placeId) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| /*! |
| \qmlsignal PlaceSearchResultModel::dataChanged() |
| |
| This signal is emitted when significant changes have been made to the underlying datastore. |
| |
| Applications should act on this signal at their own discretion. The data |
| provided by the model could be out of date and so the model should be reupdated |
| sometime, however an immediate reupdate may be disconcerting to users if the results |
| change without any action on their part. |
| |
| The corresponding handler is \c onDataChanged. |
| */ |
| |
| QT_END_NAMESPACE |