summaryrefslogtreecommitdiff
path: root/src/location/quickmapitems/qdeclarativegeomap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/location/quickmapitems/qdeclarativegeomap.cpp')
-rw-r--r--src/location/quickmapitems/qdeclarativegeomap.cpp2641
1 files changed, 2641 insertions, 0 deletions
diff --git a/src/location/quickmapitems/qdeclarativegeomap.cpp b/src/location/quickmapitems/qdeclarativegeomap.cpp
new file mode 100644
index 00000000..38c554f4
--- /dev/null
+++ b/src/location/quickmapitems/qdeclarativegeomap.cpp
@@ -0,0 +1,2641 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativegeomap_p.h"
+#include "qdeclarativegeomapquickitem_p.h"
+#include "qdeclarativegeomapcopyrightsnotice_p.h"
+#include "qdeclarativegeoserviceprovider_p.h"
+#include "qdeclarativegeomaptype_p.h"
+#include "qgeomappingmanager_p.h"
+#include "qgeocameracapabilities_p.h"
+#include "qgeomap_p.h"
+#include "qdeclarativegeomapparameter_p.h"
+#include "qgeomapobject_p.h"
+#include "qgeoprojection_p.h"
+#include <QtPositioning/QGeoCircle>
+#include <QtPositioning/QGeoRectangle>
+#include <QtPositioning/QGeoPath>
+#include <QtPositioning/QGeoPolygon>
+#include <QtQuick/QQuickWindow>
+#include <QtQuick/QSGRectangleNode>
+#include <QtQml/qqmlinfo.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <cmath>
+
+#ifndef M_PI
+#define M_PI 3.141592653589793238463
+#endif
+
+
+QT_BEGIN_NAMESPACE
+
+static qreal sanitizeBearing(qreal bearing)
+{
+ bearing = std::fmod(bearing, qreal(360.0));
+ if (bearing < 0.0)
+ bearing += 360.0;
+
+ return bearing;
+}
+
+/*!
+ \qmltype Map
+ \instantiates QDeclarativeGeoMap
+ \inqmlmodule QtLocation
+ \ingroup qml-QtLocation5-maps
+ \since QtLocation 5.0
+
+ \brief The Map type displays a map.
+
+ The Map type is used to display a map or image of the Earth, with
+ the capability to also display interactive objects tied to the map's
+ surface.
+
+ There are a variety of different ways to visualize the Earth's surface
+ in a 2-dimensional manner, but all of them involve some kind of projection:
+ a mathematical relationship between the 3D coordinates (latitude, longitude
+ and altitude) and 2D coordinates (X and Y in pixels) on the screen.
+
+ Different sources of map data can use different projections, and from the
+ point of view of the Map type, we treat these as one replaceable unit:
+ the Map plugin. A Map plugin consists of a data source, as well as all other
+ details needed to display its data on-screen.
+
+ The current Map plugin in use is contained in the \l plugin property of
+ the Map item. In order to display any image in a Map item, you will need
+ to set this property. See the \l Plugin type for a description of how
+ to retrieve an appropriate plugin for use.
+
+ The geographic region displayed in the Map item is referred to as its
+ viewport, and this is defined by the properties \l center, and
+ \l zoomLevel. The \l center property contains a \l {coordinate}
+ specifying the center of the viewport, while \l zoomLevel controls the scale of the
+ map. See each of these properties for further details about their values.
+
+ When the map is displayed, each possible geographic coordinate that is
+ visible will map to some pixel X and Y coordinate on the screen. To perform
+ conversions between these two, Map provides the \l toCoordinate and
+ \l fromCoordinate functions, which are of general utility.
+
+ \section2 Map Objects
+
+ Map related objects can be declared within the body of a Map object in Qt Quick and will
+ automatically appear on the Map. To add an object programmatically, first be
+ sure it is created with the Map as its parent (for example in an argument to
+ Component::createObject).
+ Then call the \l addMapItem method on the Map, if the type of this object is one of
+ \l MapCircle, \l MapRectangle, \l MapPolyline, \l MapPolygon, \l MapRoute or \l MapQuickItem.
+ A corresponding \l removeMapItem method also exists to do the opposite and
+ remove any of the above types of map objects from the Map.
+
+ Moving Map objects around, resizing them or changing their shape normally
+ does not involve any special interaction with Map itself -- changing these
+ properties in a map object will automatically update the display.
+
+ \section2 Interaction
+
+ The Map type includes support for pinch and flick gestures to control
+ zooming and panning. These are enabled by default, and available at any
+ time by using the \l gesture object. The actual GestureArea is constructed
+ specially at startup and cannot be replaced or destroyed. Its properties
+ can be altered, however, to control its behavior.
+
+ \section2 Performance
+
+ Maps are rendered using OpenGL (ES) and the Qt Scene Graph stack, and as
+ a result perform quite well where GL accelerated hardware is available.
+
+ For "online" Map plugins, network bandwidth and latency can be major
+ contributors to the user's perception of performance. Extensive caching is
+ performed to mitigate this, but such mitigation is not always perfect. For
+ "offline" plugins, the time spent retrieving the stored geographic data
+ and rendering the basic map features can often play a dominant role. Some
+ offline plugins may use hardware acceleration themselves to (partially)
+ avert this.
+
+ In general, large and complex Map items such as polygons and polylines with
+ large numbers of vertices can have an adverse effect on UI performance.
+ Further, more detailed notes on this are in the documentation for each
+ map item type.
+
+ \section2 Example Usage
+
+ The following snippet shows a simple Map and the necessary Plugin type
+ to use it. The map is centered over Oslo, Norway, with zoom level 14.
+
+ \quotefromfile minimal_map/main.qml
+ \skipto import
+ \printuntil }
+ \printline }
+ \skipto Map
+ \printuntil }
+ \printline }
+
+ \image minimal_map.png
+*/
+
+/*!
+ \qmlsignal QtLocation::Map::copyrightLinkActivated(string link)
+
+ This signal is emitted when the user clicks on a \a link in the copyright notice. The
+ application should open the link in a browser or display its contents to the user.
+*/
+
+QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent)
+ : QQuickItem(parent)
+{
+ m_gestureArea = new QQuickGeoMapGestureArea(this);
+
+ setAcceptHoverEvents(false);
+ setAcceptTouchEvents(true);
+ setAcceptedMouseButtons(Qt::LeftButton);
+ setFlags(QQuickItem::ItemHasContents | QQuickItem::ItemClipsChildrenToShape);
+ setFiltersChildMouseEvents(true); // needed for childMouseEventFilter to work.
+
+ m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap,
+ tr("No Map"),
+ tr("No Map"),
+ false, false,
+ 0,
+ QByteArrayLiteral(""),
+ QGeoCameraCapabilities()), this);
+ m_cameraData.setCenter(QGeoCoordinate(51.5073,-0.1277)); //London city center
+ m_cameraData.setZoomLevel(8.0);
+
+ m_cameraCapabilities.setTileSize(256);
+ m_cameraCapabilities.setSupportsBearing(true);
+ m_cameraCapabilities.setSupportsTilting(true);
+ m_cameraCapabilities.setMinimumZoomLevel(0);
+ m_cameraCapabilities.setMaximumZoomLevel(30);
+ m_cameraCapabilities.setMinimumTilt(0);
+ m_cameraCapabilities.setMaximumTilt(89.5);
+ m_cameraCapabilities.setMinimumFieldOfView(1);
+ m_cameraCapabilities.setMaximumFieldOfView(179);
+
+ m_minimumTilt = m_cameraCapabilities.minimumTilt();
+ m_maximumTilt = m_cameraCapabilities.maximumTilt();
+ m_minimumFieldOfView = m_cameraCapabilities.minimumFieldOfView();
+ m_maximumFieldOfView = m_cameraCapabilities.maximumFieldOfView();
+}
+
+QDeclarativeGeoMap::~QDeclarativeGeoMap()
+{
+ // Removing map parameters and map items from m_map
+ if (m_map) {
+ m_map->clearParameters();
+ m_map->clearMapItems();
+ }
+
+ // Remove the items from the map, making them deletable.
+ // Go in the same order as in removeMapChild: views, groups, then items
+ if (!m_mapViews.isEmpty()) {
+ const auto mapViews = m_mapViews;
+ for (QDeclarativeGeoMapItemView *v : mapViews) { // so that removeMapItemView_real can safely modify m_mapViews;
+ if (!v)
+ continue;
+
+ QQuickItem *parent = v->parentItem();
+ QDeclarativeGeoMapItemGroup *group = qobject_cast<QDeclarativeGeoMapItemGroup *>(parent);
+ if (group)
+ continue; // Ignore non-top-level MIVs. They will be recursively processed.
+ // Identify them as being parented by a MapItemGroup.
+
+ removeMapItemView_real(v);
+ }
+ }
+
+ if (!m_mapItemGroups.isEmpty()) {
+ const auto mapGroups = m_mapItemGroups;
+ for (QDeclarativeGeoMapItemGroup *g : mapGroups) {
+ if (!g)
+ continue;
+
+ QQuickItem *parent =g->parentItem();
+ QDeclarativeGeoMapItemGroup *group = qobject_cast<QDeclarativeGeoMapItemGroup *>(parent);
+ if (group)
+ continue; // Ignore non-top-level Groups. They will be recursively processed.
+ // Identify them as being parented by a MapItemGroup.
+
+ removeMapItemGroup_real(g);
+ }
+ }
+
+ // remove any remaining map items associations
+ const auto mapItems = m_mapItems;
+ for (auto mi: mapItems)
+ removeMapItem_real(mi.data());
+
+ if (m_copyrights.data())
+ delete m_copyrights.data();
+ m_copyrights.clear();
+
+ for (auto obj: qAsConst(m_pendingMapObjects))
+ obj->setMap(nullptr); // worst case: going to be setMap(nullptr)'d twice
+
+ delete m_map; // map objects get reset here
+}
+
+static QDeclarativeGeoMapType *findMapType(const QList<QDeclarativeGeoMapType *> &types, const QGeoMapType &type)
+{
+ for (int i = 0; i < types.size(); ++i)
+ if (types[i]->mapType() == type)
+ return types[i];
+ return nullptr;
+}
+
+void QDeclarativeGeoMap::onSupportedMapTypesChanged()
+{
+ QList<QDeclarativeGeoMapType *> supportedMapTypes;
+ QList<QGeoMapType> types = m_mappingManager->supportedMapTypes();
+ for (int i = 0; i < types.size(); ++i) {
+ // types that are present and get removed will be deleted at QObject destruction
+ QDeclarativeGeoMapType *type = findMapType(m_supportedMapTypes, types[i]);
+ if (!type)
+ type = new QDeclarativeGeoMapType(types[i], this);
+ supportedMapTypes.append(type);
+ }
+ m_supportedMapTypes.swap(supportedMapTypes);
+ if (m_supportedMapTypes.isEmpty()) {
+ m_map->setActiveMapType(QGeoMapType()); // no supported map types: setting an invalid one
+ } else {
+ bool hasMapType = false;
+ for (const auto *declarativeType : qAsConst(m_supportedMapTypes)) {
+ if (declarativeType->mapType() == m_map->activeMapType())
+ hasMapType = true;
+ }
+ if (!hasMapType) {
+ QDeclarativeGeoMapType *type = m_supportedMapTypes.at(0);
+ m_activeMapType = type;
+ m_map->setActiveMapType(type->mapType());
+ }
+ }
+
+ emit supportedMapTypesChanged();
+}
+
+void QDeclarativeGeoMap::setError(QGeoServiceProvider::Error error, const QString &errorString)
+{
+ if (m_error == error && m_errorString == errorString)
+ return;
+ m_error = error;
+ m_errorString = errorString;
+ emit errorChanged();
+}
+
+/*!
+ \internal
+ Called when the mapping manager is initialized AND the declarative element has a valid size > 0
+*/
+void QDeclarativeGeoMap::initialize()
+{
+ // try to keep change signals in the end
+ bool visibleAreaHasChanged = false;
+
+ QGeoCoordinate center = m_cameraData.center();
+
+ if (!qIsFinite(m_userMinimumZoomLevel))
+ setMinimumZoomLevel(m_map->minimumZoom(), false);
+ else
+ setMinimumZoomLevel(qMax<qreal>(m_map->minimumZoom(), m_userMinimumZoomLevel), false);
+
+ double bearing = m_cameraData.bearing();
+ double tilt = m_cameraData.tilt();
+ double fov = m_cameraData.fieldOfView(); // Must be 45.0
+ QGeoCameraData cameraData = m_cameraData;
+
+ if (!m_cameraCapabilities.supportsBearing() && bearing != 0.0)
+ cameraData.setBearing(0);
+
+ if (!m_cameraCapabilities.supportsTilting() && tilt != 0.0)
+ cameraData.setTilt(0);
+
+ m_map->setVisibleArea(m_visibleArea);
+ if (m_map->visibleArea() != m_visibleArea)
+ visibleAreaHasChanged = true;
+
+ cameraData.setFieldOfView(qBound(m_cameraCapabilities.minimumFieldOfView(),
+ fov,
+ m_cameraCapabilities.maximumFieldOfView()));
+
+ // set latitude boundary check
+ m_maximumViewportLatitude = m_map->maximumCenterLatitudeAtZoom(cameraData);
+ m_minimumViewportLatitude = m_map->minimumCenterLatitudeAtZoom(cameraData);
+
+ center.setLatitude(qBound(m_minimumViewportLatitude, center.latitude(), m_maximumViewportLatitude));
+ cameraData.setCenter(center);
+
+ connect(m_map.data(), &QGeoMap::cameraDataChanged,
+ this, &QDeclarativeGeoMap::onCameraDataChanged);
+ m_map->setCameraData(cameraData); // This normally triggers property changed signals.
+ // BUT not in this case, since m_cameraData is already == cameraData.
+ // So, emit visibleRegionChanged() separately, as
+ // the effective visible region becomes available only now.
+
+ for (const auto &obj : qAsConst(m_pendingMapObjects))
+ obj->setMap(m_map);
+
+ m_initialized = true;
+
+ if (visibleAreaHasChanged)
+ emit visibleAreaChanged();
+ connect(m_map.data(), &QGeoMap::visibleAreaChanged, this, &QDeclarativeGeoMap::visibleAreaChanged);
+
+ emit mapReadyChanged(true);
+ emit visibleRegionChanged();
+
+ if (m_copyrights) // To not update during initialize()
+ update();
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::pluginReady()
+{
+ QGeoServiceProvider *provider = m_plugin->sharedGeoServiceProvider();
+ m_mappingManager = provider->mappingManager();
+
+ if (provider->mappingError() != QGeoServiceProvider::NoError) {
+ setError(provider->mappingError(), provider->mappingErrorString());
+ return;
+ }
+
+ if (!m_mappingManager) {
+ //TODO Should really be EngineNotSetError (see QML GeoCodeModel)
+ setError(QGeoServiceProvider::NotSupportedError, tr("Plugin does not support mapping."));
+ return;
+ }
+
+ if (!m_mappingManager->isInitialized()) {
+ connect(m_mappingManager, &QGeoMappingManager::initialized,
+ this, &QDeclarativeGeoMap::mappingManagerInitialized);
+ } else {
+ mappingManagerInitialized();
+ }
+
+ // make sure this is only called once
+ disconnect(m_plugin, &QDeclarativeGeoServiceProvider::attached,
+ this, &QDeclarativeGeoMap::pluginReady);
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::componentComplete()
+{
+ m_componentCompleted = true;
+ populateParameters();
+ populateMap();
+ QQuickItem::componentComplete();
+}
+
+/*!
+ \qmlproperty MapGestureArea QtLocation::Map::gesture
+
+ Contains the MapGestureArea created with the Map. This covers pan, flick and pinch gestures.
+ Use \c{gesture.enabled: true} to enable basic gestures, or see \l{MapGestureArea} for
+ further details.
+*/
+
+QQuickGeoMapGestureArea *QDeclarativeGeoMap::gesture()
+{
+ return m_gestureArea;
+}
+
+/*!
+ \internal
+
+ This may happen before mappingManagerInitialized()
+*/
+void QDeclarativeGeoMap::populateMap()
+{
+ QSet<QObject *> kids(children().cbegin(), children().cend());
+ const QList<QQuickItem *> quickKids = childItems();
+ for (QQuickItem *ite: quickKids)
+ kids.insert(ite);
+
+ for (QObject *k : qAsConst(kids)) {
+ addMapChild(k);
+ }
+}
+
+void QDeclarativeGeoMap::populateParameters()
+{
+ QObjectList kids = children();
+ const QList<QQuickItem *> quickKids = childItems();
+ for (const auto &quickKid : quickKids)
+ kids.append(quickKid);
+ for (auto *kid : qAsConst(kids)) {
+ if (auto *mapParameter = qobject_cast<QDeclarativeGeoMapParameter *>(kid))
+ addMapParameter(mapParameter);
+ }
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::setupMapView(QDeclarativeGeoMapItemView *view)
+{
+ view->setMap(this);
+}
+
+/*!
+ * \internal
+ */
+QSGNode *QDeclarativeGeoMap::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ if (!m_map) {
+ delete oldNode;
+ return nullptr;
+ }
+
+ QSGRectangleNode *root = static_cast<QSGRectangleNode *>(oldNode);
+ if (!root)
+ root = window()->createRectangleNode();
+
+ root->setRect(boundingRect());
+ root->setColor(m_color);
+
+ QSGNode *content = root->childCount() ? root->firstChild() : 0;
+ content = m_map->updateSceneGraph(content, window());
+ if (content && root->childCount() == 0)
+ root->appendChildNode(content);
+
+ return root;
+}
+
+/*!
+ \qmlproperty Plugin QtLocation::Map::plugin
+
+ This property holds the plugin which provides the mapping functionality.
+
+ This is a write-once property. Once the map has a plugin associated with
+ it, any attempted modifications of the plugin will be ignored.
+*/
+
+void QDeclarativeGeoMap::setPlugin(QDeclarativeGeoServiceProvider *plugin)
+{
+ if (m_plugin) {
+ qmlWarning(this) << QStringLiteral("Plugin is a write-once property, and cannot be set again.");
+ return;
+ }
+ m_plugin = plugin;
+ emit pluginChanged(m_plugin);
+
+ if (m_plugin->isAttached()) {
+ pluginReady();
+ } else {
+ connect(m_plugin, &QDeclarativeGeoServiceProvider::attached,
+ this, &QDeclarativeGeoMap::pluginReady);
+ }
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::onCameraCapabilitiesChanged(const QGeoCameraCapabilities &oldCameraCapabilities)
+{
+ if (m_map->cameraCapabilities() == oldCameraCapabilities)
+ return;
+ m_cameraCapabilities = m_map->cameraCapabilities();
+
+ //The zoom level limits are only restricted by the plugins values, if the user has set a more
+ //strict zoom level limit before initialization nothing is done here.
+ //minimum zoom level might be changed to limit gray bundaries
+ //This code assumes that plugins' maximum zoom level will never exceed 30.0
+ if (m_cameraCapabilities.maximumZoomLevelAt256() < m_gestureArea->maximumZoomLevel()) {
+ setMaximumZoomLevel(m_cameraCapabilities.maximumZoomLevelAt256(), false);
+ } else if (m_cameraCapabilities.maximumZoomLevelAt256() > m_gestureArea->maximumZoomLevel()) {
+ if (!qIsFinite(m_userMaximumZoomLevel)) {
+ // If the user didn't set anything
+ setMaximumZoomLevel(m_cameraCapabilities.maximumZoomLevelAt256(), false);
+ } else { // Try to set what the user requested
+ // Else if the user set something larger, but that got clamped by the previous camera caps
+ setMaximumZoomLevel(qMin<qreal>(m_cameraCapabilities.maximumZoomLevelAt256(),
+ m_userMaximumZoomLevel), false);
+ }
+ }
+
+ if (m_cameraCapabilities.minimumZoomLevelAt256() > m_gestureArea->minimumZoomLevel()) {
+ setMinimumZoomLevel(m_cameraCapabilities.minimumZoomLevelAt256(), false);
+ } else if (m_cameraCapabilities.minimumZoomLevelAt256() < m_gestureArea->minimumZoomLevel()) {
+ if (!qIsFinite(m_userMinimumZoomLevel)) {
+ // If the user didn't set anything, trying to set the new caps.
+ setMinimumZoomLevel(m_cameraCapabilities.minimumZoomLevelAt256(), false);
+ } else { // Try to set what the user requested
+ // Else if the user set a minimum, m_gestureArea->minimumZoomLevel() might be larger
+ // because of different reasons. Resetting it, as if it ends to be the same,
+ // no signal will be emitted.
+ setMinimumZoomLevel(qMax<qreal>(m_cameraCapabilities.minimumZoomLevelAt256(),
+ m_userMinimumZoomLevel), false);
+ }
+ }
+
+ // Tilt
+ if (m_cameraCapabilities.minimumTilt() > m_minimumTilt) {
+ setMinimumTilt(m_cameraCapabilities.minimumTilt(), false);
+ } else if (m_cameraCapabilities.minimumTilt() < m_minimumTilt) {
+ if (!qIsFinite(m_userMinimumTilt))
+ setMinimumTilt(m_cameraCapabilities.minimumTilt(), false);
+ else // Try to set what the user requested
+ setMinimumTilt(qMax<qreal>(m_cameraCapabilities.minimumTilt(), m_userMinimumTilt), false);
+ }
+
+ if (m_cameraCapabilities.maximumTilt() < m_maximumTilt) {
+ setMaximumTilt(m_cameraCapabilities.maximumTilt(), false);
+ } else if (m_cameraCapabilities.maximumTilt() > m_maximumTilt) {
+ if (!qIsFinite(m_userMaximumTilt))
+ setMaximumTilt(m_cameraCapabilities.maximumTilt(), false);
+ else // Try to set what the user requested
+ setMaximumTilt(qMin<qreal>(m_cameraCapabilities.maximumTilt(), m_userMaximumTilt), false);
+ }
+
+ // FoV
+ if (m_cameraCapabilities.minimumFieldOfView() > m_minimumFieldOfView) {
+ setMinimumFieldOfView(m_cameraCapabilities.minimumFieldOfView(), false);
+ } else if (m_cameraCapabilities.minimumFieldOfView() < m_minimumFieldOfView) {
+ if (!qIsFinite(m_userMinimumFieldOfView))
+ setMinimumFieldOfView(m_cameraCapabilities.minimumFieldOfView(), false);
+ else // Try to set what the user requested
+ setMinimumFieldOfView(qMax<qreal>(m_cameraCapabilities.minimumFieldOfView(), m_userMinimumFieldOfView), false);
+ }
+
+ if (m_cameraCapabilities.maximumFieldOfView() < m_maximumFieldOfView) {
+ setMaximumFieldOfView(m_cameraCapabilities.maximumFieldOfView(), false);
+ } else if (m_cameraCapabilities.maximumFieldOfView() > m_maximumFieldOfView) {
+ if (!qIsFinite(m_userMaximumFieldOfView))
+ setMaximumFieldOfView(m_cameraCapabilities.maximumFieldOfView(), false);
+ else // Try to set what the user requested
+ setMaximumFieldOfView(qMin<qreal>(m_cameraCapabilities.maximumFieldOfView(), m_userMaximumFieldOfView), false);
+ }
+}
+
+/*!
+ \internal
+ this function will only be ever called once
+*/
+void QDeclarativeGeoMap::mappingManagerInitialized()
+{
+ m_map = m_mappingManager->createMap(this);
+
+ if (!m_map)
+ return;
+
+ // Any map items that were added before the plugin was ready
+ // need to have setMap called again
+ for (const QPointer<QDeclarativeGeoMapItemBase> &item : qAsConst(m_mapItems)) {
+ if (item) {
+ item->setMap(this, m_map);
+ m_map->addMapItem(item.data()); // m_map filters out what is not supported.
+ }
+ }
+
+ /* COPY NOTICE SETUP */
+ m_copyrights = new QDeclarativeGeoMapCopyrightNotice(this);
+ m_copyrights->setCopyrightsZ(m_maxChildZ + 1);
+ m_copyrights->setCopyrightsVisible(m_copyrightsVisible);
+ m_copyrights->setMapSource(this);
+
+ m_gestureArea->setMap(m_map);
+
+ QList<QGeoMapType> types = m_mappingManager->supportedMapTypes();
+ for (int i = 0; i < types.size(); ++i) {
+ QDeclarativeGeoMapType *type = new QDeclarativeGeoMapType(types[i], this);
+ m_supportedMapTypes.append(type);
+ }
+
+ if (m_activeMapType && m_plugin->name().toLatin1() == m_activeMapType->mapType().pluginName()) {
+ m_map->setActiveMapType(m_activeMapType->mapType());
+ } else {
+ if (m_activeMapType)
+ m_activeMapType->deleteLater();
+
+ if (!m_supportedMapTypes.isEmpty()) {
+ m_activeMapType = m_supportedMapTypes.at(0);
+ m_map->setActiveMapType(m_activeMapType->mapType());
+ } else {
+ m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap,
+ tr("No Map"),
+ tr("No Map"),
+ false,
+ false,
+ 0,
+ QByteArrayLiteral(""),
+ QGeoCameraCapabilities()), this);
+ }
+ }
+
+ // Update camera capabilities
+ onCameraCapabilitiesChanged(m_cameraCapabilities);
+
+ // Map tiles are built in this call. m_map->minimumZoom() becomes operational
+ // after this has been called at least once, after creation.
+ // However, getting into the following block may fire a copyrightsChanged that would get lost,
+ // as the connections are set up after.
+ QString copyrightString;
+ QImage copyrightImage;
+ if (!m_initialized && width() > 0 && height() > 0) {
+ QMetaObject::Connection copyrightStringCatcherConnection =
+ connect(m_map.data(), &QGeoMap::copyrightsChanged,
+ [&copyrightString](const QString &copy){ copyrightString = copy; });
+ QMetaObject::Connection copyrightImageCatcherConnection =
+ connect(m_map.data(), &QGeoMap::copyrightsImageChanged,
+ [&copyrightImage](const QImage &copy){ copyrightImage = copy; });
+ m_map->setViewportSize(QSize(width(), height()));
+ initialize(); // This emits the caught signals above
+ QObject::disconnect(copyrightStringCatcherConnection);
+ QObject::disconnect(copyrightImageCatcherConnection);
+ }
+
+
+ /* COPYRIGHT SIGNALS REWIRING */
+ connect(m_map.data(), &QGeoMap::copyrightsImageChanged,
+ this, &QDeclarativeGeoMap::copyrightsImageChanged);
+ connect(m_map.data(), &QGeoMap::copyrightsChanged,
+ this, &QDeclarativeGeoMap::copyrightsChanged);
+ if (!copyrightString.isEmpty())
+ emit m_map->copyrightsChanged(copyrightString);
+ else if (!copyrightImage.isNull())
+ emit m_map->copyrightsImageChanged(copyrightImage);
+
+ m_window = window();
+ if (m_window) {
+ connect(m_window, &QQuickWindow::beforeSynchronizing,
+ this, &QDeclarativeGeoMap::updateItemToWindowTransform, Qt::DirectConnection);
+ }
+ connect(m_map.data(), &QGeoMap::sgNodeChanged, this, &QDeclarativeGeoMap::onSGNodeChanged);
+ connect(m_map.data(), &QGeoMap::cameraCapabilitiesChanged,
+ this, &QDeclarativeGeoMap::onCameraCapabilitiesChanged);
+
+ // This prefetches a buffer around the map
+ m_map->prefetchData();
+
+ connect(m_mappingManager, &QGeoMappingManager::supportedMapTypesChanged,
+ this, &QDeclarativeGeoMap::onSupportedMapTypesChanged);
+ emit minimumZoomLevelChanged();
+ emit maximumZoomLevelChanged();
+ emit supportedMapTypesChanged();
+ emit activeMapTypeChanged();
+
+ // Any map item groups that were added before the plugin was ready
+ // DO NOT need to have setMap called again on their children map items
+ // because they have been added to m_mapItems, which is processed right above.
+
+
+ // All map parameters that were added before the plugin was ready
+ // need to be added to m_map
+ for (QDeclarativeGeoMapParameter *p : qAsConst(m_mapParameters))
+ m_map->addParameter(p);
+
+ if (m_initialized)
+ update();
+}
+
+/*!
+ \internal
+*/
+QDeclarativeGeoServiceProvider *QDeclarativeGeoMap::plugin() const
+{
+ return m_plugin;
+}
+
+/*!
+ \internal
+ Sets the gesture areas minimum zoom level. If the camera capabilities
+ has been set this method honors the boundaries set by it.
+ The minimum zoom level will also have a lower bound dependent on the size
+ of the canvas, effectively preventing to display out of bounds areas.
+*/
+void QDeclarativeGeoMap::setMinimumZoomLevel(qreal minimumZoomLevel, bool userSet)
+{
+ if (minimumZoomLevel >= 0) {
+ qreal oldUserMinimumZoomLevel = m_userMinimumZoomLevel;
+ if (userSet)
+ m_userMinimumZoomLevel = minimumZoomLevel;
+ qreal oldMinimumZoomLevel = this->minimumZoomLevel();
+
+ minimumZoomLevel = qBound(qreal(m_cameraCapabilities.minimumZoomLevelAt256()), minimumZoomLevel, maximumZoomLevel());
+ if (m_map)
+ minimumZoomLevel = qMax<qreal>(minimumZoomLevel, m_map->minimumZoom());
+
+ // minimumZoomLevel is, at this point, the implicit minimum zoom level
+ m_gestureArea->setMinimumZoomLevel(minimumZoomLevel);
+
+ if (zoomLevel() < minimumZoomLevel && (m_gestureArea->enabled() || !m_cameraCapabilities.overzoomEnabled()))
+ setZoomLevel(minimumZoomLevel);
+
+ if (qIsNaN(m_userMinimumZoomLevel) && oldMinimumZoomLevel != minimumZoomLevel)
+ emit minimumZoomLevelChanged();
+ else if (userSet && oldUserMinimumZoomLevel != m_userMinimumZoomLevel)
+ emit minimumZoomLevelChanged();
+ }
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::minimumZoomLevel
+
+ This property holds the minimum valid zoom level for the map.
+
+ The minimum zoom level defined by the \l plugin used is a lower bound for
+ this property. However, the returned value is also canvas-size-dependent, and
+ can be higher than the user-specified value, or than the minimum zoom level
+ defined by the plugin used, to prevent the map from being smaller than the
+ viewport in either dimension.
+
+ If the \l plugin property is not set or the plugin does not support mapping, this property is \c 0.
+*/
+
+qreal QDeclarativeGeoMap::minimumZoomLevel() const
+{
+ if (!qIsNaN(m_userMinimumZoomLevel))
+ return m_userMinimumZoomLevel;
+ else
+ return m_gestureArea->minimumZoomLevel();
+}
+
+/*!
+ \internal
+*/
+qreal QDeclarativeGeoMap::implicitMinimumZoomLevel() const
+{
+ return m_gestureArea->minimumZoomLevel();
+}
+
+/*!
+ \internal
+*/
+qreal QDeclarativeGeoMap::effectiveMinimumZoomLevel() const
+{
+ return qMax<qreal>(minimumZoomLevel(), implicitMinimumZoomLevel());
+}
+
+/*!
+ \internal
+ Sets the gesture areas maximum zoom level. If the camera capabilities
+ has been set this method honors the boundaries set by it.
+*/
+void QDeclarativeGeoMap::setMaximumZoomLevel(qreal maximumZoomLevel, bool userSet)
+{
+ if (maximumZoomLevel >= 0) {
+ if (userSet)
+ m_userMaximumZoomLevel = maximumZoomLevel;
+ qreal oldMaximumZoomLevel = this->maximumZoomLevel();
+
+ maximumZoomLevel = qBound(minimumZoomLevel(), maximumZoomLevel, qreal(m_cameraCapabilities.maximumZoomLevelAt256()));
+
+ m_gestureArea->setMaximumZoomLevel(maximumZoomLevel);
+
+ if (zoomLevel() > maximumZoomLevel && (m_gestureArea->enabled() || !m_cameraCapabilities.overzoomEnabled()))
+ setZoomLevel(maximumZoomLevel);
+
+ if (oldMaximumZoomLevel != maximumZoomLevel)
+ emit maximumZoomLevelChanged();
+ }
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::maximumZoomLevel
+
+ This property holds the maximum valid zoom level for the map.
+
+ The maximum zoom level is defined by the \l plugin used.
+ If the \l plugin property is not set or the plugin does not support mapping, this property is \c 30.
+*/
+
+qreal QDeclarativeGeoMap::maximumZoomLevel() const
+{
+ return m_gestureArea->maximumZoomLevel();
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::zoomLevel
+
+ This property holds the zoom level for the map.
+
+ Larger values for the zoom level provide more detail. Zoom levels
+ are always non-negative. The default value is 8.0. Depending on the plugin in use,
+ values outside the [minimumZoomLevel, maximumZoomLevel] range, which represent the range for which
+ tiles are available, may be accepted, or clamped.
+*/
+void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel)
+{
+ return setZoomLevel(zoomLevel, m_cameraCapabilities.overzoomEnabled());
+}
+
+/*!
+ \internal
+
+ Sets the zoom level.
+ Larger values for the zoom level provide more detail. Zoom levels
+ are always non-negative. The default value is 8.0. Values outside the
+ [minimumZoomLevel, maximumZoomLevel] range, which represent the range for which
+ tiles are available, can be accepted or clamped by setting the overzoom argument
+ to true or false respectively.
+*/
+void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel, bool overzoom)
+{
+ if (zoomLevel < 0)
+ return;
+
+ if (m_initialized) {
+ QGeoCameraData cameraData = m_map->cameraData();
+ if (cameraData.zoomLevel() == zoomLevel)
+ return;
+
+ cameraData.setZoomLevel(qBound<qreal>(overzoom ? m_map->minimumZoom() : effectiveMinimumZoomLevel(),
+ zoomLevel,
+ overzoom ? 30 : maximumZoomLevel()));
+ m_maximumViewportLatitude = m_map->maximumCenterLatitudeAtZoom(cameraData);
+ m_minimumViewportLatitude = m_map->minimumCenterLatitudeAtZoom(cameraData);
+ QGeoCoordinate coord = cameraData.center();
+ coord.setLatitude(qBound(m_minimumViewportLatitude, coord.latitude(), m_maximumViewportLatitude));
+ cameraData.setCenter(coord);
+ m_map->setCameraData(cameraData);
+ } else {
+ const bool zlHasChanged = zoomLevel != m_cameraData.zoomLevel();
+ m_cameraData.setZoomLevel(zoomLevel);
+ if (zlHasChanged) {
+ emit zoomLevelChanged(zoomLevel);
+ // do not emit visibleRegionChanged() here, because, if the map isn't initialized,
+ // the getter won't return anything updated
+ }
+ }
+}
+
+bool QDeclarativeGeoMap::addMapChild(QObject *child)
+{
+ // dispatch items appropriately
+ QDeclarativeGeoMapItemView *mapView = qobject_cast<QDeclarativeGeoMapItemView *>(child);
+ if (mapView)
+ return addMapItemView_real(mapView);
+
+ QDeclarativeGeoMapItemGroup *itemGroup = qobject_cast<QDeclarativeGeoMapItemGroup *>(child);
+ if (itemGroup) // addMapItemView calls addMapItemGroup
+ return addMapItemGroup_real(itemGroup);
+
+ QDeclarativeGeoMapItemBase *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(child);
+ if (mapItem)
+ return addMapItem_real(mapItem);
+
+ QGeoMapObject *mapObject = qobject_cast<QGeoMapObject *>(child);
+ if (mapObject)
+ addMapObject(mapObject); // this emits mapObjectsChanged, != mapItemsChanged
+ return false;
+}
+
+bool QDeclarativeGeoMap::removeMapChild(QObject *child)
+{
+ // dispatch items appropriately
+ QDeclarativeGeoMapItemView *mapView = qobject_cast<QDeclarativeGeoMapItemView *>(child);
+ if (mapView)
+ return removeMapItemView_real(mapView);
+
+ QDeclarativeGeoMapItemGroup *itemGroup = qobject_cast<QDeclarativeGeoMapItemGroup *>(child);
+ if (itemGroup) // removeMapItemView calls removeMapItemGroup for itself.
+ return removeMapItemGroup_real(itemGroup);
+
+ QDeclarativeGeoMapItemBase *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(child);
+ if (mapItem)
+ return removeMapItem_real(mapItem);
+
+ QGeoMapObject *mapObject = qobject_cast<QGeoMapObject *>(child);
+ if (mapObject)
+ removeMapObject(mapObject); // this emits mapObjectsChanged, != mapItemsChanged
+ return false;
+}
+
+bool QDeclarativeGeoMap::isGroupNested(QDeclarativeGeoMapItemGroup *group) const
+{
+ QObject *parent = group->parent();
+ // Nested groups have parent set in parent's componentComplete()
+ // Those instantiated by MapItemView's delegateModel, however, do not,
+ // but have setParentItem set.
+ return qobject_cast<QDeclarativeGeoMapItemGroup *>(parent)
+ || qobject_cast<QDeclarativeGeoMapItemGroup *>(group->parentItem());
+}
+
+qreal QDeclarativeGeoMap::zoomLevel() const
+{
+ if (m_initialized)
+ return m_map->cameraData().zoomLevel();
+ return m_cameraData.zoomLevel();
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::bearing
+
+ This property holds the bearing for the map.
+ The default value is 0.
+ If the Plugin used for the Map supports bearing, the valid range for this value is between 0 and 360.
+ If the Plugin used for the Map does not support bearing, changing this property will have no effect.
+
+ \since QtLocation 5.9
+*/
+void QDeclarativeGeoMap::setBearing(qreal bearing)
+{
+ bearing = sanitizeBearing(bearing);
+ if (m_initialized) {
+ QGeoCameraData cameraData = m_map->cameraData();
+ cameraData.setBearing(bearing);
+ m_map->setCameraData(cameraData);
+ } else {
+ const bool bearingHasChanged = bearing != m_cameraData.bearing();
+ m_cameraData.setBearing(bearing);
+ if (bearingHasChanged) {
+ emit bearingChanged(bearing);
+ // do not emit visibleRegionChanged() here, because, if the map isn't initialized,
+ // the getter won't return anything updated
+ }
+ }
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::setBearing(real bearing, coordinate coordinate)
+
+ Sets the bearing for the map to \a bearing, rotating it around \a coordinate.
+ If the Plugin used for the Map supports bearing, the valid range for \a bearing is between 0 and 360.
+ If the Plugin used for the Map does not support bearing, or if the map is tilted and \a coordinate happens
+ to be behind the camera, or if the map is not ready (see \l mapReady), calling this method will have no effect.
+
+ The release of this API with Qt 5.10 is a Technology Preview.
+
+ \since 5.10
+*/
+void QDeclarativeGeoMap::setBearing(qreal bearing, const QGeoCoordinate &coordinate)
+{
+ if (!m_initialized)
+ return;
+
+ const QGeoCoordinate currentCenter = center();
+ const qreal currentBearing = QDeclarativeGeoMap::bearing();
+ bearing = sanitizeBearing(bearing);
+
+ if (!coordinate.isValid()
+ || !qIsFinite(bearing)
+ || (coordinate == currentCenter && bearing == currentBearing))
+ return;
+
+ if (m_map->capabilities() & QGeoMap::SupportsSetBearing)
+ m_map->setBearing(bearing, coordinate);
+}
+
+qreal QDeclarativeGeoMap::bearing() const
+{
+ if (m_initialized)
+ return m_map->cameraData().bearing();
+ return m_cameraData.bearing();
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::tilt
+
+ This property holds the tilt for the map, in degrees.
+ The default value is 0.
+ The valid range for this value is [ minimumTilt, maximumTilt ].
+ If the Plugin used for the Map does not support tilting, changing this property will have no effect.
+
+ \sa minimumTilt, maximumTilt
+
+ \since QtLocation 5.9
+*/
+void QDeclarativeGeoMap::setTilt(qreal tilt)
+{
+ tilt = qBound(minimumTilt(), tilt, maximumTilt());
+
+ if (m_initialized) {
+ QGeoCameraData cameraData = m_map->cameraData();
+ cameraData.setTilt(tilt);
+ m_map->setCameraData(cameraData);
+ } else {
+ const bool tiltHasChanged = tilt != m_cameraData.tilt();
+ m_cameraData.setTilt(tilt);
+ if (tiltHasChanged) {
+ emit tiltChanged(tilt);
+ // do not emit visibleRegionChanged() here, because, if the map isn't initialized,
+ // the getter won't return anything updated
+ }
+ }
+}
+
+qreal QDeclarativeGeoMap::tilt() const
+{
+ if (m_initialized)
+ return m_map->cameraData().tilt();
+ return m_cameraData.tilt();
+}
+
+void QDeclarativeGeoMap::setMinimumTilt(qreal minimumTilt, bool userSet)
+{
+ if (minimumTilt >= 0) {
+ if (userSet)
+ m_userMinimumTilt = minimumTilt;
+ qreal oldMinimumTilt = this->minimumTilt();
+
+ m_minimumTilt = qBound<double>(m_cameraCapabilities.minimumTilt(),
+ minimumTilt,
+ m_cameraCapabilities.maximumTilt());
+
+ if (tilt() < m_minimumTilt)
+ setTilt(m_minimumTilt);
+
+ if (oldMinimumTilt != m_minimumTilt)
+ emit minimumTiltChanged(m_minimumTilt);
+ }
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::fieldOfView
+
+ This property holds the field of view of the camera used to look at the map, in degrees.
+ If the plugin property of the map is not set, or the plugin does not support mapping, the value is 45 degrees.
+
+ Note that changing this value implicitly changes also the distance between the camera and the map,
+ so that, at a tilting angle of 0 degrees, the resulting image is identical for any value used for this property.
+
+ For more information about this parameter, consult the Wikipedia articles about \l {https://en.wikipedia.org/wiki/Field_of_view} {Field of view} and
+ \l {https://en.wikipedia.org/wiki/Angle_of_view} {Angle of view}.
+
+ \sa minimumFieldOfView, maximumFieldOfView
+
+ \since QtLocation 5.9
+*/
+void QDeclarativeGeoMap::setFieldOfView(qreal fieldOfView)
+{
+ fieldOfView = qBound(minimumFieldOfView(), fieldOfView, maximumFieldOfView());
+
+ if (m_initialized) {
+ QGeoCameraData cameraData = m_map->cameraData();
+ cameraData.setFieldOfView(fieldOfView);
+ m_map->setCameraData(cameraData);
+ } else {
+ const bool fovChanged = fieldOfView != m_cameraData.fieldOfView();
+ m_cameraData.setFieldOfView(fieldOfView);
+ if (fovChanged) {
+ emit fieldOfViewChanged(fieldOfView);
+ // do not emit visibleRegionChanged() here, because, if the map isn't initialized,
+ // the getter won't return anything updated
+ }
+ }
+}
+
+qreal QDeclarativeGeoMap::fieldOfView() const
+{
+ if (m_initialized)
+ return m_map->cameraData().fieldOfView();
+ return m_cameraData.fieldOfView();
+}
+
+void QDeclarativeGeoMap::setMinimumFieldOfView(qreal minimumFieldOfView, bool userSet)
+{
+ if (minimumFieldOfView > 0 && minimumFieldOfView < 180.0) {
+ if (userSet)
+ m_userMinimumFieldOfView = minimumFieldOfView;
+ qreal oldMinimumFoV = this->minimumFieldOfView();
+
+ m_minimumFieldOfView = qBound<double>(m_cameraCapabilities.minimumFieldOfView(),
+ minimumFieldOfView,
+ m_cameraCapabilities.maximumFieldOfView());
+
+ if (fieldOfView() < m_minimumFieldOfView)
+ setFieldOfView(m_minimumFieldOfView);
+
+ if (oldMinimumFoV != m_minimumFieldOfView)
+ emit minimumFieldOfViewChanged(m_minimumFieldOfView);
+ }
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::minimumFieldOfView
+
+ This property holds the minimum valid field of view for the map, in degrees.
+
+ The minimum tilt field of view by the \l plugin used is a lower bound for
+ this property.
+ If the \l plugin property is not set or the plugin does not support mapping, this property is \c 1.
+
+ \sa fieldOfView, maximumFieldOfView
+
+ \since QtLocation 5.9
+*/
+qreal QDeclarativeGeoMap::minimumFieldOfView() const
+{
+ return m_minimumFieldOfView;
+}
+
+void QDeclarativeGeoMap::setMaximumFieldOfView(qreal maximumFieldOfView, bool userSet)
+{
+ if (maximumFieldOfView > 0 && maximumFieldOfView < 180.0) {
+ if (userSet)
+ m_userMaximumFieldOfView = maximumFieldOfView;
+ qreal oldMaximumFoV = this->maximumFieldOfView();
+
+ m_maximumFieldOfView = qBound<double>(m_cameraCapabilities.minimumFieldOfView(),
+ maximumFieldOfView,
+ m_cameraCapabilities.maximumFieldOfView());
+
+ if (fieldOfView() > m_maximumFieldOfView)
+ setFieldOfView(m_maximumFieldOfView);
+
+ if (oldMaximumFoV != m_maximumFieldOfView)
+ emit maximumFieldOfViewChanged(m_maximumFieldOfView);
+ }
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::maximumFieldOfView
+
+ This property holds the maximum valid field of view for the map, in degrees.
+
+ The minimum tilt field of view by the \l plugin used is an upper bound for
+ this property.
+ If the \l plugin property is not set or the plugin does not support mapping, this property is \c 179.
+
+ \sa fieldOfView, minimumFieldOfView
+
+ \since QtLocation 5.9
+*/
+qreal QDeclarativeGeoMap::maximumFieldOfView() const
+{
+ return m_maximumFieldOfView;
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::minimumTilt
+
+ This property holds the minimum valid tilt for the map, in degrees.
+
+ The minimum tilt defined by the \l plugin used is a lower bound for
+ this property.
+ If the \l plugin property is not set or the plugin does not support mapping, this property is \c 0.
+
+ Since QtLocation 5.12, plugins can additionally restrict this value depending on the current zoom level.
+
+ \sa tilt, maximumTilt
+
+ \since QtLocation 5.9
+*/
+qreal QDeclarativeGeoMap::minimumTilt() const
+{
+ return m_minimumTilt;
+}
+
+void QDeclarativeGeoMap::setMaximumTilt(qreal maximumTilt, bool userSet)
+{
+ if (maximumTilt >= 0) {
+ if (userSet)
+ m_userMaximumTilt = maximumTilt;
+ qreal oldMaximumTilt = this->maximumTilt();
+
+ m_maximumTilt = qBound<double>(m_cameraCapabilities.minimumTilt(),
+ maximumTilt,
+ m_cameraCapabilities.maximumTilt());
+
+ if (tilt() > m_maximumTilt)
+ setTilt(m_maximumTilt);
+
+ if (oldMaximumTilt != m_maximumTilt)
+ emit maximumTiltChanged(m_maximumTilt);
+ }
+}
+
+/*!
+ \qmlproperty real QtLocation::Map::maximumTilt
+
+ This property holds the maximum valid tilt for the map, in degrees.
+
+ The maximum tilt defined by the \l plugin used is an upper bound for
+ this property.
+ If the \l plugin property is not set or the plugin does not support mapping, this property is \c 89.5.
+
+ Since QtLocation 5.12, plugins can additionally restrict this value depending on the current zoom level.
+
+ \sa tilt, minimumTilt
+
+ \since QtLocation 5.9
+*/
+qreal QDeclarativeGeoMap::maximumTilt() const
+{
+ return m_maximumTilt;
+}
+
+/*!
+ \qmlproperty coordinate QtLocation::Map::center
+
+ This property holds the coordinate which occupies the center of the
+ mapping viewport. Invalid center coordinates are ignored.
+
+ The default value is an arbitrary valid coordinate.
+*/
+void QDeclarativeGeoMap::setCenter(const QGeoCoordinate &center)
+{
+ if (!center.isValid())
+ return;
+
+ if (m_initialized) {
+ QGeoCoordinate coord(center);
+ coord.setLatitude(qBound(m_minimumViewportLatitude, center.latitude(), m_maximumViewportLatitude));
+ QGeoCameraData cameraData = m_map->cameraData();
+ cameraData.setCenter(coord);
+ m_map->setCameraData(cameraData);
+ } else {
+ const bool centerHasChanged = center != m_cameraData.center();
+ m_cameraData.setCenter(center);
+ if (centerHasChanged) {
+ emit centerChanged(center);
+ // do not emit visibleRegionChanged() here, because, if the map isn't initialized,
+ // the getter won't return anything updated
+ }
+ }
+}
+
+QGeoCoordinate QDeclarativeGeoMap::center() const
+{
+ if (m_initialized)
+ return m_map->cameraData().center();
+ return m_cameraData.center();
+}
+
+
+/*!
+ \qmlproperty geoshape QtLocation::Map::visibleRegion
+
+ This property holds the region which occupies the viewport of
+ the map. The camera is positioned in the center of the shape, and
+ at the largest integral zoom level possible which allows the
+ whole shape to be visible on the screen. This implies that
+ reading this property back shortly after having been set the
+ returned area is equal or larger than the set area.
+
+ Setting this property implicitly changes the \l center and
+ \l zoomLevel of the map. Any previously set value to those
+ properties will be overridden.
+
+ \note Since Qt 5.14 This property provides change notifications.
+
+ \since 5.6
+*/
+void QDeclarativeGeoMap::setVisibleRegion(const QGeoShape &shape)
+{
+ if (shape.boundingGeoRectangle() == visibleRegion())
+ return;
+
+ m_visibleRegion = shape.boundingGeoRectangle();
+ if (!m_visibleRegion.isValid()
+ || (m_visibleRegion.bottomRight().latitude() >= 85.0) // rect entirely outside web mercator
+ || (m_visibleRegion.topLeft().latitude() <= -85.0)) {
+ // shape invalidated -> nothing to fit anymore
+ m_visibleRegion = QGeoRectangle();
+ m_pendingFitViewport = false;
+ emit visibleRegionChanged();
+ return;
+ }
+
+ if (!m_map || !width() || !height()) {
+ m_pendingFitViewport = true;
+ emit visibleRegionChanged();
+ return;
+ }
+
+ fitViewportToGeoShape(m_visibleRegion);
+ emit visibleRegionChanged();
+}
+
+QGeoShape QDeclarativeGeoMap::visibleRegion() const
+{
+ if (!m_map || !width() || !height())
+ return m_visibleRegion;
+
+ if (m_map->capabilities() & QGeoMap::SupportsVisibleRegion) {
+ return m_map->visibleRegion();
+ } else {
+ // ToDo: handle projections not supporting visible region in a better way.
+ // This approach will fail when horizon is in the view or the map is greatly zoomed out.
+ QList<QGeoCoordinate> visiblePoly;
+ visiblePoly << m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(0,0), false);
+ visiblePoly << m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_map->viewportWidth() - 1,
+ 0), false);
+ visiblePoly << m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_map->viewportWidth() - 1,
+ m_map->viewportHeight() - 1), false);
+ visiblePoly << m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(0,
+ m_map->viewportHeight() - 1), false);
+ QGeoPath path;
+ path.setPath(visiblePoly);
+ return path.boundingGeoRectangle();
+ }
+}
+
+/*!
+ \qmlproperty bool QtLocation::Map::copyrightsVisible
+
+ This property holds the visibility of the copyrights notice. The notice is usually
+ displayed in the bottom left corner. By default, this property is set to \c true.
+
+ \note Many map providers require the notice to be visible as part of the terms and conditions.
+ Please consult the relevant provider documentation before turning this notice off.
+
+ \since 5.7
+*/
+void QDeclarativeGeoMap::setCopyrightsVisible(bool visible)
+{
+ if (m_copyrightsVisible == visible)
+ return;
+
+ if (!m_copyrights.isNull())
+ m_copyrights->setCopyrightsVisible(visible);
+
+ m_copyrightsVisible = visible;
+ emit copyrightsVisibleChanged(visible);
+}
+
+bool QDeclarativeGeoMap::copyrightsVisible() const
+{
+ return m_copyrightsVisible;
+}
+
+
+
+/*!
+ \qmlproperty color QtLocation::Map::color
+
+ This property holds the background color of the map element.
+
+ \since 5.6
+*/
+void QDeclarativeGeoMap::setColor(const QColor &color)
+{
+ if (color != m_color) {
+ m_color = color;
+ update();
+ emit colorChanged(m_color);
+ }
+}
+
+QColor QDeclarativeGeoMap::color() const
+{
+ return m_color;
+}
+
+/*!
+ \qmlproperty rect QtLocation::Map::visibleArea
+
+ This property holds the visible area inside the Map QML element.
+ It is a rect whose coordinates are relative to the Map element.
+ Its size will be clamped to the size of the Map element.
+ A null visibleArea means that the whole Map is visible.
+
+ \since 5.12
+*/
+QRectF QDeclarativeGeoMap::visibleArea() const
+{
+ if (m_initialized)
+ return m_map->visibleArea();
+ return m_visibleArea;
+}
+
+void QDeclarativeGeoMap::setVisibleArea(const QRectF &visibleArea)
+{
+ const QRectF oldVisibleArea = QDeclarativeGeoMap::visibleArea();
+ if (visibleArea == oldVisibleArea)
+ return;
+
+ if (!visibleArea.isValid() && !visibleArea.isEmpty()) // values < 0
+ return;
+
+ if (m_initialized) {
+ m_map->setVisibleArea(visibleArea);
+ const QRectF newVisibleArea = QDeclarativeGeoMap::visibleArea();
+ if (newVisibleArea != oldVisibleArea) {
+ // polish map items
+ for (const QPointer<QDeclarativeGeoMapItemBase> &i: qAsConst(m_mapItems)) {
+ if (i)
+ i->visibleAreaChanged();
+ }
+ }
+ } else {
+ m_visibleArea = visibleArea;
+ const QRectF newVisibleArea = QDeclarativeGeoMap::visibleArea();
+ if (newVisibleArea != oldVisibleArea)
+ emit visibleAreaChanged();
+ }
+}
+
+/*!
+ \qmlproperty bool QtLocation::Map::mapReady
+
+ This property holds whether the map has been successfully initialized and is ready to be used.
+ Some methods, such as \l fromCoordinate and \l toCoordinate, will not work before the map is ready.
+ Due to the architecture of the \l Map, it's advised to use the signal emitted for this property
+ in place of \l {QtQml::Component::completed()}{Component.onCompleted}, to make sure that everything behaves as expected.
+
+ \since 5.9
+*/
+bool QDeclarativeGeoMap::mapReady() const
+{
+ return m_initialized;
+}
+
+QMargins QDeclarativeGeoMap::mapMargins() const
+{
+ const QRectF va = m_map->visibleArea();
+ if (va.isEmpty())
+ return QMargins();
+ return QMargins( va.x()
+ , va.y()
+ , width() - va.width() - va.x()
+ , height() - va.height() - va.y());
+}
+
+/*!
+ \qmlproperty list<MapType> QtLocation::Map::supportedMapTypes
+
+ This read-only property holds the set of \l{MapType}{map types} supported by this map.
+
+ \sa activeMapType
+*/
+QQmlListProperty<QDeclarativeGeoMapType> QDeclarativeGeoMap::supportedMapTypes()
+{
+ return QQmlListProperty<QDeclarativeGeoMapType>(this, &m_supportedMapTypes);
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::alignCoordinateToPoint(coordinate coordinate, QPointF point)
+
+ Aligns \a coordinate to \a point.
+ This method effectively extends the functionality offered by the \l center qml property, allowing
+ to align a coordinate to point of the Map element other than its center.
+ This is useful in those applications where the center of the scene (e.g., a cursor) is not to be
+ placed exactly in the center of the map.
+
+ If the map is tilted, and \a coordinate happens to be behind the camera, or if the map is not ready
+ (see \l mapReady), calling this method will have no effect.
+
+ The release of this API with Qt 5.10 is a Technology Preview.
+
+ \sa center
+
+ \since 5.10
+*/
+void QDeclarativeGeoMap::alignCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &point)
+{
+ if (!m_map || !(m_map->capabilities() & QGeoMap::SupportsAnchoringCoordinate))
+ return;
+
+ if (!coordinate.isValid()
+ || !qIsFinite(point.x())
+ || !qIsFinite(point.y()))
+ return;
+
+ m_map->anchorCoordinateToPoint(coordinate, point);
+}
+
+/*!
+ \qmlmethod coordinate QtLocation::Map::toCoordinate(QPointF position, bool clipToViewPort)
+
+ Returns the coordinate which corresponds to the \a position relative to the map item.
+
+ If \a clipToViewPort is \c true, or not supplied then returns an invalid coordinate if
+ \a position is not within the current viewport.
+*/
+QGeoCoordinate QDeclarativeGeoMap::toCoordinate(const QPointF &position, bool clipToViewPort) const
+{
+ if (m_map)
+ return m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(position), clipToViewPort);
+ else
+ return QGeoCoordinate();
+}
+
+/*!
+ \qmlmethod point QtLocation::Map::fromCoordinate(coordinate coordinate, bool clipToViewPort)
+
+ Returns the position relative to the map item which corresponds to the \a coordinate.
+
+ If \a clipToViewPort is \c true, or not supplied then returns an invalid QPointF if
+ \a coordinate is not within the current viewport.
+*/
+QPointF QDeclarativeGeoMap::fromCoordinate(const QGeoCoordinate &coordinate, bool clipToViewPort) const
+{
+ if (m_map)
+ return m_map->geoProjection().coordinateToItemPosition(coordinate, clipToViewPort).toPointF();
+ else
+ return QPointF(qQNaN(), qQNaN());
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::pan(int dx, int dy)
+
+ Starts panning the map by \a dx pixels along the x-axis and
+ by \a dy pixels along the y-axis.
+
+ Positive values for \a dx move the map right, negative values left.
+ Positive values for \a dy move the map down, negative values up.
+
+ During panning the \l center, and \l zoomLevel may change.
+*/
+void QDeclarativeGeoMap::pan(int dx, int dy)
+{
+ if (!m_map)
+ return;
+ if (dx == 0 && dy == 0)
+ return;
+
+ QGeoCoordinate coord = m_map->geoProjection().itemPositionToCoordinate(
+ QDoubleVector2D(m_map->viewportWidth() / 2 + dx,
+ m_map->viewportHeight() / 2 + dy));
+ setCenter(coord);
+}
+
+
+/*!
+ \qmlmethod void QtLocation::Map::prefetchData()
+
+ Optional hint that allows the map to prefetch during this idle period
+*/
+void QDeclarativeGeoMap::prefetchData()
+{
+ if (!m_map)
+ return;
+ m_map->prefetchData();
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::clearData()
+
+ Clears map data collected by the currently selected plugin.
+ \note This method will delete cached files.
+ \sa plugin
+*/
+void QDeclarativeGeoMap::clearData()
+{
+ if (m_map)
+ m_map->clearData();
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::fitViewportToGeoShape(geoShape, margins)
+
+ Fits the viewport to a specific geo shape \a geoShape.
+ The \a margins are in screen pixels.
+
+ \note If the projection used by the plugin is not WebMercator, and the plugin does not have fitting to
+ shape capability, this method will do nothing.
+
+ \sa visibleRegion
+ \since 5.13
+*/
+void QDeclarativeGeoMap::fitViewportToGeoShape(const QGeoShape &shape, QVariant margins)
+{
+ QMargins m(10, 10, 10, 10); // lets defaults to 10 if margins is invalid
+ switch (margins.typeId()) {
+ case QMetaType::Int:
+ case QMetaType::Double: {
+ const int value = int(margins.toDouble());
+ m = QMargins(value, value, value, value);
+ }
+ break;
+ // ToDo: Support distinct margins in some QML form. Perhaps QRect?
+ default:
+ break;
+ }
+ fitViewportToGeoShape(shape, m);
+}
+
+void QDeclarativeGeoMap::fitViewportToGeoShape(const QGeoShape &shape, const QMargins &borders)
+{
+ if (!m_map || !shape.isValid())
+ return;
+
+ if (m_map->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) {
+ // This case remains handled here, and not inside QGeoMap*::fitViewportToGeoRectangle,
+ // in order to honor animations on center and zoomLevel
+ const QMargins margins = borders + mapMargins();
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection());
+ const QPair<QGeoCoordinate, qreal> fitData = p.fitViewportToGeoRectangle(shape.boundingGeoRectangle(),
+ margins);
+ if (!fitData.first.isValid())
+ return;
+
+ // position camera to the center of bounding box
+ setProperty("center", QVariant::fromValue(fitData.first)); // not using setCenter(centerCoordinate) to honor a possible animation set on the center property
+
+ if (!qIsFinite(fitData.second))
+ return;
+ double newZoom = qMax<double>(minimumZoomLevel(), fitData.second);
+ setProperty("zoomLevel", QVariant::fromValue(newZoom)); // not using setZoomLevel(newZoom) to honor a possible animation set on the zoomLevel property
+ } else if (m_map->capabilities() & QGeoMap::SupportsFittingViewportToGeoRectangle) {
+ // Animations cannot be honored in this case, as m_map acts as a black box
+ m_map->fitViewportToGeoRectangle(m_visibleRegion, borders);
+ }
+ // else out of luck
+}
+
+/*!
+ \qmlproperty string QtLocation::Map::errorString
+
+ This read-only property holds the textual presentation of the latest mapping provider error.
+ If no error has occurred, an empty string is returned.
+
+ An empty string may also be returned if an error occurred which has no associated
+ textual representation.
+
+ \sa QGeoServiceProvider::errorString()
+*/
+
+QString QDeclarativeGeoMap::errorString() const
+{
+ return m_errorString;
+}
+
+/*!
+ \qmlproperty enumeration QtLocation::Map::error
+
+ This read-only property holds the last occurred mapping service provider error.
+
+ \list
+ \li Map.NoError - No error has occurred.
+ \li Map.NotSupportedError -The maps plugin property was not set or there is no mapping manager associated with the plugin.
+ \li Map.UnknownParameterError -The plugin did not recognize one of the parameters it was given.
+ \li Map.MissingRequiredParameterError - The plugin did not find one of the parameters it was expecting.
+ \li Map.ConnectionError - The plugin could not connect to its backend service or database.
+ \endlist
+
+ \sa QGeoServiceProvider::Error
+*/
+
+QGeoServiceProvider::Error QDeclarativeGeoMap::error() const
+{
+ return m_error;
+}
+
+QGeoMap *QDeclarativeGeoMap::map() const
+{
+ return m_map;
+}
+
+void QDeclarativeGeoMap::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ if (change == ItemChildAddedChange) {
+ QQuickItem *child = value.item;
+ QQuickItem *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(child);
+ if (!mapItem)
+ mapItem = qobject_cast<QDeclarativeGeoMapItemGroup *>(child);
+
+ if (mapItem) {
+ qreal z = mapItem->z();
+ if (z > m_maxChildZ) { // Ignore children removal
+ m_maxChildZ = z;
+ // put the copyrights notice object at the highest z order
+ if (m_copyrights)
+ m_copyrights->setCopyrightsZ(m_maxChildZ + 1);
+ }
+ }
+ } else if (change == ItemSceneChange) {
+ if (m_window) {
+ disconnect(m_window, &QQuickWindow::beforeSynchronizing,
+ this, &QDeclarativeGeoMap::updateItemToWindowTransform);
+ }
+ m_window = value.window;
+ if (m_window) {
+ connect(m_window, &QQuickWindow::beforeSynchronizing,
+ this, &QDeclarativeGeoMap::updateItemToWindowTransform, Qt::DirectConnection);
+ }
+ }
+ QQuickItem::itemChange(change, value);
+}
+
+bool QDeclarativeGeoMap::isInteractive() const
+{
+ return (m_gestureArea->enabled() && m_gestureArea->acceptedGestures()) || m_gestureArea->isActive();
+}
+
+void QDeclarativeGeoMap::attachCopyrightNotice(bool initialVisibility)
+{
+ if (initialVisibility) {
+ ++m_copyNoticesVisible;
+ if (m_map)
+ m_map->setCopyrightVisible(m_copyNoticesVisible > 0);
+ }
+}
+
+void QDeclarativeGeoMap::detachCopyrightNotice(bool currentVisibility)
+{
+ if (currentVisibility) {
+ --m_copyNoticesVisible;
+ if (m_map)
+ m_map->setCopyrightVisible(m_copyNoticesVisible > 0);
+ }
+}
+
+void QDeclarativeGeoMap::onAttachedCopyrightNoticeVisibilityChanged()
+{
+ QDeclarativeGeoMapCopyrightNotice *copy = static_cast<QDeclarativeGeoMapCopyrightNotice *>(sender());
+ m_copyNoticesVisible += ( int(copy->copyrightsVisible()) * 2 - 1);
+ if (m_map)
+ m_map->setCopyrightVisible(m_copyNoticesVisible > 0);
+}
+
+void QDeclarativeGeoMap::onCameraDataChanged(const QGeoCameraData &cameraData)
+{
+ bool centerHasChanged = cameraData.center() != m_cameraData.center();
+ bool bearingHasChanged = cameraData.bearing() != m_cameraData.bearing();
+ bool tiltHasChanged = cameraData.tilt() != m_cameraData.tilt();
+ bool fovHasChanged = cameraData.fieldOfView() != m_cameraData.fieldOfView();
+ bool zoomHasChanged = cameraData.zoomLevel() != m_cameraData.zoomLevel();
+
+ m_cameraData = cameraData;
+ // polish map items
+ for (const QPointer<QDeclarativeGeoMapItemBase> &i: qAsConst(m_mapItems)) {
+ if (i)
+ i->baseCameraDataChanged(m_cameraData); // Consider optimizing this further, removing the contained duplicate if conditions.
+ }
+
+ if (centerHasChanged)
+ emit centerChanged(m_cameraData.center());
+ if (zoomHasChanged)
+ emit zoomLevelChanged(m_cameraData.zoomLevel());
+ if (bearingHasChanged)
+ emit bearingChanged(m_cameraData.bearing());
+ if (tiltHasChanged)
+ emit tiltChanged(m_cameraData.tilt());
+ if (fovHasChanged)
+ emit fieldOfViewChanged(m_cameraData.fieldOfView());
+ if (centerHasChanged || zoomHasChanged || bearingHasChanged
+ || tiltHasChanged || fovHasChanged)
+ emit visibleRegionChanged();
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::addMapParameter(MapParameter parameter)
+
+ Adds the \a parameter object to the map. The effect of this call is dependent
+ on the combination of the content of the MapParameter and the type of
+ underlying QGeoMap. If a MapParameter that is not supported by the underlying
+ QGeoMap gets added, the call has no effect.
+
+ The release of this API with Qt 5.9 is a Technology Preview.
+
+ \sa MapParameter, removeMapParameter, mapParameters, clearMapParameters
+
+ \since 5.9
+*/
+void QDeclarativeGeoMap::addMapParameter(QDeclarativeGeoMapParameter *parameter)
+{
+ if (!parameter->isComponentComplete()) {
+ connect(parameter, &QDeclarativeGeoMapParameter::completed, this, &QDeclarativeGeoMap::addMapParameter);
+ return;
+ }
+
+ disconnect(parameter);
+ if (m_mapParameters.contains(parameter))
+ return;
+ parameter->setParent(this);
+ m_mapParameters.append(parameter); // parameter now owned by QDeclarativeGeoMap
+ if (m_map)
+ m_map->addParameter(parameter);
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::removeMapParameter(MapParameter parameter)
+
+ Removes the given \a parameter object from the map.
+
+ The release of this API with Qt 5.9 is a Technology Preview.
+
+ \sa MapParameter, addMapParameter, mapParameters, clearMapParameters
+
+ \since 5.9
+*/
+void QDeclarativeGeoMap::removeMapParameter(QDeclarativeGeoMapParameter *parameter)
+{
+ if (!m_mapParameters.contains(parameter))
+ return;
+ if (m_map)
+ m_map->removeParameter(parameter);
+ m_mapParameters.removeOne(parameter);
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::clearMapParameters()
+
+ Removes all map parameters from the map.
+
+ The release of this API with Qt 5.9 is a Technology Preview.
+
+ \sa MapParameter, mapParameters, addMapParameter, removeMapParameter, clearMapParameters
+
+ \since 5.9
+*/
+void QDeclarativeGeoMap::clearMapParameters()
+{
+ if (m_map)
+ m_map->clearParameters();
+ m_mapParameters.clear();
+}
+
+/*!
+ \qmlproperty list<MapParameters> QtLocation::Map::mapParameters
+
+ Returns the list of all map parameters in no particular order.
+ These items include map parameters that were declared statically as part of
+ the type declaration, as well as dynamical map parameters (\l addMapParameter).
+
+ The release of this API with Qt 5.9 is a Technology Preview.
+
+ \sa MapParameter, addMapParameter, removeMapParameter, clearMapParameters
+
+ \since 5.9
+*/
+QList<QObject *> QDeclarativeGeoMap::mapParameters()
+{
+ QList<QObject *> ret;
+ for (QDeclarativeGeoMapParameter *p : qAsConst(m_mapParameters))
+ ret << p;
+ return ret;
+}
+
+/*
+ \internal
+*/
+void QDeclarativeGeoMap::addMapObject(QGeoMapObject *object)
+{
+ if (!object || object->map())
+ return;
+
+ if (!m_initialized) {
+ m_pendingMapObjects.append(object);
+ return;
+ }
+
+ int curObjects = m_map->mapObjects().size();
+ // object adds itself to the map
+ object->setMap(m_map);
+
+ if (curObjects != m_map->mapObjects().size())
+ emit mapObjectsChanged();
+}
+
+/*
+ \internal
+*/
+void QDeclarativeGeoMap::removeMapObject(QGeoMapObject *object)
+{
+ if (!object || object->map() != m_map) // if !initialized this is fine, since both object and m_map are supposed to be NULL
+ return;
+
+ if (!m_initialized) {
+ m_pendingMapObjects.removeOne(object);
+ return;
+ }
+
+ int curObjects = m_map->mapObjects().size();
+ // object adds itself to the map
+ object->setMap(nullptr);
+
+ if (curObjects != m_map->mapObjects().size())
+ emit mapObjectsChanged();
+}
+
+/*
+ \internal
+*/
+void QDeclarativeGeoMap::clearMapObjects()
+{
+ if (!m_initialized) {
+ m_pendingMapObjects.clear();
+ } else {
+ const QList<QGeoMapObject *> objs = m_map->mapObjects();
+ for (QGeoMapObject *o: objs)
+ o->setMap(nullptr);
+ if (objs.size())
+ emit mapObjectsChanged();
+ }
+}
+
+/*
+ \internal
+*/
+QList<QGeoMapObject *> QDeclarativeGeoMap::mapObjects()
+{
+ if (!m_initialized)
+ return m_pendingMapObjects;
+ else
+ return m_map->mapObjects();
+}
+
+/*!
+ \qmlproperty list<MapItem> QtLocation::Map::mapItems
+
+ Returns the list of all map items in no particular order.
+ These items include items that were declared statically as part of
+ the type declaration, as well as dynamical items (\l addMapItem,
+ \l MapItemView).
+
+ \sa addMapItem, removeMapItem, clearMapItems
+*/
+
+QList<QObject *> QDeclarativeGeoMap::mapItems()
+{
+ QList<QObject *> ret;
+ for (const auto &ptr : m_mapItems) {
+ if (ptr)
+ ret << ptr.data();
+ }
+ return ret;
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::addMapItem(MapItem item)
+
+ Adds the given \a item to the Map (for example MapQuickItem, MapCircle). If the object
+ already is on the Map, it will not be added again.
+
+ As an example, consider the case where you have a MapCircle representing your current position:
+
+ \snippet declarative/maps.qml QtQuick import
+ \snippet declarative/maps.qml QtLocation import
+ \codeline
+ \snippet declarative/maps.qml Map addMapItem MapCircle at current position
+
+ \note MapItemViews cannot be added with this method.
+
+ \sa mapItems, removeMapItem, clearMapItems
+*/
+
+void QDeclarativeGeoMap::addMapItem(QDeclarativeGeoMapItemBase *item)
+{
+ if (addMapItem_real(item))
+ emit mapItemsChanged();
+}
+
+bool QDeclarativeGeoMap::addMapItem_real(QDeclarativeGeoMapItemBase *item)
+{
+ if (!item || item->quickMap())
+ return false;
+ // If the item comes from a MapItemGroup, do not reparent it.
+ if (!qobject_cast<QDeclarativeGeoMapItemGroup *>(item->parentItem()))
+ item->setParentItem(this);
+ m_mapItems.append(item);
+ if (m_map) {
+ item->setMap(this, m_map);
+ m_map->addMapItem(item);
+ }
+ return true;
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::removeMapItem(MapItem item)
+
+ Removes the given \a item from the Map (for example MapQuickItem, MapCircle). If
+ the MapItem does not exist or was not previously added to the map, the
+ method does nothing.
+
+ \sa mapItems, addMapItem, clearMapItems
+*/
+void QDeclarativeGeoMap::removeMapItem(QDeclarativeGeoMapItemBase *ptr)
+{
+ if (removeMapItem_real(ptr))
+ emit mapItemsChanged();
+}
+
+bool QDeclarativeGeoMap::removeMapItem_real(QDeclarativeGeoMapItemBase *ptr)
+{
+ if (!ptr)
+ return false;
+ QPointer<QDeclarativeGeoMapItemBase> item(ptr);
+ if (!m_mapItems.contains(item))
+ return false;
+ if (m_map)
+ m_map->removeMapItem(ptr);
+ if (item->parentItem() == this)
+ item->setParentItem(0);
+ item->setMap(0, 0);
+ // these can be optimized for perf, as we already check the 'contains' above
+ m_mapItems.removeOne(item);
+ return true;
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::clearMapItems()
+
+ Removes all items and item groups from the map.
+
+ \sa mapItems, addMapItem, removeMapItem, addMapItemGroup, removeMapItemGroup
+*/
+void QDeclarativeGeoMap::clearMapItems()
+{
+ if (m_mapItems.isEmpty())
+ return;
+
+ qsizetype removed = 0;
+ for (qsizetype i = 0; i < m_mapItemGroups.count(); ++i) {
+ auto item = m_mapItemGroups.at(i);
+ // Processing only top-level groups (!views)
+ if (qobject_cast<QDeclarativeGeoMapItemView *>(item))
+ continue;
+
+
+ if (item->parentItem() != this)
+ continue;
+
+ if (removeMapItemGroup_real(item)) {
+ removed++;
+ --i;
+ }
+ }
+
+ while (!m_mapItems.isEmpty())
+ removed += removeMapItem_real(m_mapItems.first());
+
+ if (removed)
+ emit mapItemsChanged();
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::addMapItemGroup(MapItemGroup itemGroup)
+
+ Adds the map items contained in the given \a itemGroup to the Map
+ (for example MapQuickItem, MapCircle).
+
+ \sa MapItemGroup, removeMapItemGroup
+
+ \since 5.9
+*/
+void QDeclarativeGeoMap::addMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup)
+{
+ if (addMapItemGroup_real(itemGroup))
+ emit mapItemsChanged();
+}
+
+bool QDeclarativeGeoMap::addMapItemGroup_real(QDeclarativeGeoMapItemGroup *itemGroup)
+{
+ if (!itemGroup || itemGroup->quickMap()) // Already added to some map
+ return false;
+
+ itemGroup->setQuickMap(this);
+
+ if (!isGroupNested(itemGroup))
+ itemGroup->setParentItem(this);
+
+ QPointer<QDeclarativeGeoMapItemGroup> g(itemGroup);
+ m_mapItemGroups.append(g);
+
+ const QList<QQuickItem *> quickKids = itemGroup->childItems();
+ int count = 0;
+ for (auto c: quickKids) {
+ count += addMapChild(c); // this calls addMapItemGroup recursively, if needed
+ }
+ return count;
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::removeMapItemGroup(MapItemGroup itemGroup)
+
+ Removes \a itemGroup and the items contained therein from the Map.
+
+ \sa MapItemGroup, addMapItemGroup
+
+ \since 5.9
+*/
+void QDeclarativeGeoMap::removeMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup)
+{
+ if (removeMapItemGroup_real(itemGroup))
+ emit mapItemsChanged();
+}
+
+bool QDeclarativeGeoMap::removeMapItemGroup_real(QDeclarativeGeoMapItemGroup *itemGroup)
+{
+ if (!itemGroup || itemGroup->quickMap() != this) // cant remove an itemGroup added to another map
+ return false;
+
+ QPointer<QDeclarativeGeoMapItemGroup> g(itemGroup);
+ if (!m_mapItemGroups.removeOne(g))
+ return false;
+
+ const QList<QQuickItem *> quickKids = itemGroup->childItems();
+ int count = 0;
+ for (auto c: quickKids) {
+ count += removeMapChild(c);
+ }
+ itemGroup->setQuickMap(nullptr);
+ if (itemGroup->parentItem() == this)
+ itemGroup->setParentItem(0);
+ return count;
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::removeMapItemView(MapItemView itemView)
+
+ Removes \a itemView and the items instantiated by it from the Map.
+
+ \sa MapItemView, addMapItemView
+
+ \since 5.10
+*/
+void QDeclarativeGeoMap::removeMapItemView(QDeclarativeGeoMapItemView *itemView)
+{
+ if (removeMapItemView_real(itemView))
+ emit mapItemsChanged();
+}
+
+bool QDeclarativeGeoMap::removeMapItemView_real(QDeclarativeGeoMapItemView *itemView)
+{
+ if (!itemView || itemView->m_map != this) // can't remove a view that is already added to another map
+ return false;
+
+ itemView->removeInstantiatedItems(false); // remove the items without using transitions AND abort ongoing ones
+ itemView->m_map = 0;
+ m_mapViews.removeOne(itemView);
+ return removeMapItemGroup_real(itemView); // at this point, all delegate instances have been removed.
+}
+
+void QDeclarativeGeoMap::updateItemToWindowTransform()
+{
+ if (!m_initialized)
+ return;
+
+ // Update itemToWindowTransform into QGeoProjection
+ const QTransform item2WindowOld = m_map->geoProjection().itemToWindowTransform();
+ QTransform item2Window = QQuickItemPrivate::get(this)->itemToWindowTransform();
+ if (!property("layer").isNull() && property("layer").value<QObject *>()->property("enabled").toBool())
+ item2Window.reset(); // When layer is enabled, the item is rendered offscreen with no transformation, then the layer is applied
+
+ m_map->setItemToWindowTransform(item2Window);
+
+ // This method is called at every redraw, including those redraws not generated by
+ // sgNodeChanged.
+ // In these cases, *if* the item2windowTransform has changed (e.g., if transformation of
+ // the item or one of its ancestors changed), a forced update of the map items using accelerated
+ // GL implementation has to be performed in order to have them pulling the updated itemToWindowTransform.
+ if (!m_sgNodeHasChanged && item2WindowOld != item2Window) {
+ for (auto i: qAsConst(m_mapItems))
+ i->setMaterialDirty();
+ }
+
+ m_sgNodeHasChanged = false;
+}
+
+void QDeclarativeGeoMap::onSGNodeChanged()
+{
+ m_sgNodeHasChanged = true;
+ update();
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::addMapItemView(MapItemView itemView)
+
+ Adds \a itemView to the Map.
+
+ \sa MapItemView, removeMapItemView
+
+ \since 5.10
+*/
+void QDeclarativeGeoMap::addMapItemView(QDeclarativeGeoMapItemView *itemView)
+{
+ if (addMapItemView_real(itemView))
+ emit mapItemsChanged();
+}
+
+bool QDeclarativeGeoMap::addMapItemView_real(QDeclarativeGeoMapItemView *itemView)
+{
+ if (!itemView || itemView->m_map) // can't add a view twice
+ return false;
+
+ int count = addMapItemGroup_real(itemView); // at this point, delegates aren't yet incubated.
+ // Not appending it to m_mapViews because it seems unnecessary even if the
+ // itemView is a child of this (in which case it would be destroyed
+ m_mapViews.append(itemView);
+ setupMapView(itemView);
+ return count;
+}
+
+/*!
+ \qmlproperty MapType QtLocation::Map::activeMapType
+
+ \brief Access to the currently active \l{MapType}{map type}.
+
+ This property can be set to change the active \l{MapType}{map type}.
+ See the \l{Map::supportedMapTypes}{supportedMapTypes} property for possible values.
+
+ \sa MapType
+*/
+void QDeclarativeGeoMap::setActiveMapType(QDeclarativeGeoMapType *mapType)
+{
+ if (m_activeMapType->mapType() != mapType->mapType()) {
+ if (m_map) {
+ if (mapType->mapType().pluginName() == m_plugin->name().toLatin1()) {
+ m_map->setActiveMapType(mapType->mapType());
+ m_activeMapType = mapType;
+ emit activeMapTypeChanged();
+ }
+ } else {
+ m_activeMapType = mapType;
+ emit activeMapTypeChanged();
+ }
+ }
+}
+
+QDeclarativeGeoMapType * QDeclarativeGeoMap::activeMapType() const
+{
+ return m_activeMapType;
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ m_gestureArea->setSize(newGeometry.size());
+ QQuickItem::geometryChange(newGeometry, oldGeometry);
+
+ if (!m_map || newGeometry.size().isEmpty())
+ return;
+
+ m_map->setViewportSize(newGeometry.size().toSize());
+
+ if (!m_initialized) {
+ initialize();
+ } else {
+ setMinimumZoomLevel(m_map->minimumZoom(), false);
+
+ // Update the center latitudinal threshold
+ QGeoCameraData cameraData = m_map->cameraData();
+ const double maximumCenterLatitudeAtZoom = m_map->maximumCenterLatitudeAtZoom(cameraData);
+ const double minimumCenterLatitudeAtZoom = m_map->minimumCenterLatitudeAtZoom(cameraData);
+ if (maximumCenterLatitudeAtZoom != m_maximumViewportLatitude
+ || minimumCenterLatitudeAtZoom != m_minimumViewportLatitude) {
+ m_maximumViewportLatitude = maximumCenterLatitudeAtZoom;
+ m_minimumViewportLatitude = minimumCenterLatitudeAtZoom;
+ QGeoCoordinate coord = cameraData.center();
+ coord.setLatitude(qBound(m_minimumViewportLatitude, coord.latitude(), m_maximumViewportLatitude));
+ cameraData.setCenter(coord);
+ m_map->setCameraData(cameraData); // this polishes map items
+ } else if (oldGeometry.size() != newGeometry.size()) {
+ // polish map items
+ for (const QPointer<QDeclarativeGeoMapItemBase> &i: qAsConst(m_mapItems)) {
+ if (i)
+ i->polishAndUpdate();
+ }
+ }
+ }
+
+ /*
+ The fitViewportTo*() functions depend on a valid map geometry.
+ If they were called prior to the first resize they cause
+ the zoomlevel to jump to 0 (showing the world). Therefore the
+ calls were queued up until now.
+
+ Multiple fitViewportTo*() calls replace each other.
+ */
+ if (m_pendingFitViewport && width() && height()) {
+ fitViewportToGeoShape(m_visibleRegion);
+ m_pendingFitViewport = false;
+ }
+
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::fitViewportToMapItems(list<MapItems> items = {})
+
+ If no argument is provided, fits the current viewport to the boundary of all map items.
+ The camera is positioned in the center of the map items, and at the largest integral zoom level
+ possible which allows all map items to be visible on screen.
+ If \a items is provided, fits the current viewport to the boundary of the specified map items only.
+
+ \note This method gained the optional \a items argument since Qt 5.15.
+ In previous releases, this method fitted the map to all map items.
+
+ \sa fitViewportToVisibleMapItems
+*/
+void QDeclarativeGeoMap::fitViewportToMapItems(const QVariantList &items)
+{
+ if (items.size()) {
+ QList<QPointer<QDeclarativeGeoMapItemBase> > itms;
+ for (const QVariant &i: items) {
+ QDeclarativeGeoMapItemBase *itm = qobject_cast<QDeclarativeGeoMapItemBase *>(i.value<QObject *>());
+ if (itm)
+ itms.append(itm);
+ }
+ fitViewportToMapItemsRefine(itms, true, false);
+ } else {
+ fitViewportToMapItemsRefine(m_mapItems, true, false);
+ }
+}
+
+/*!
+ \qmlmethod void QtLocation::Map::fitViewportToVisibleMapItems()
+
+ Fits the current viewport to the boundary of all \b visible map items.
+ The camera is positioned in the center of the map items, and at the largest integral
+ zoom level possible which allows all map items to be visible on screen.
+
+ \sa fitViewportToMapItems
+*/
+void QDeclarativeGeoMap::fitViewportToVisibleMapItems()
+{
+ fitViewportToMapItemsRefine(m_mapItems, true, true);
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::fitViewportToMapItemsRefine(const QList<QPointer<QDeclarativeGeoMapItemBase> > &mapItems,
+ bool refine,
+ bool onlyVisible)
+{
+ if (!m_map)
+ return;
+
+ if (mapItems.size() == 0)
+ return;
+
+ double minX = qInf();
+ double maxX = -qInf();
+ double minY = qInf();
+ double maxY = -qInf();
+ double topLeftX = 0;
+ double topLeftY = 0;
+ double bottomRightX = 0;
+ double bottomRightY = 0;
+ bool haveQuickItem = false;
+
+ // find bounds of all map items
+ qsizetype itemCount = 0;
+ for (qsizetype i = 0; i < mapItems.count(); ++i) {
+ if (!mapItems.at(i))
+ continue;
+ QDeclarativeGeoMapItemBase *item = mapItems.at(i).data();
+ if (!item || (onlyVisible && (!item->isVisible() || item->mapItemOpacity() <= 0.0)))
+ continue;
+
+ // skip quick items in the first pass and refine the fit later
+ QDeclarativeGeoMapQuickItem *quickItem =
+ qobject_cast<QDeclarativeGeoMapQuickItem*>(item);
+ if (refine && quickItem) {
+ haveQuickItem = true;
+ continue;
+ }
+ // Force map items to update immediately. Needed to ensure correct item size and positions
+ // when recursively calling this function.
+ // TODO: See if we really need updatePolish on delegated items, in particular
+ // in relation to
+ // a) fitViewportToMapItems
+ // b) presence of MouseArea
+ //
+ // This is also legacy code. It must be updated to not operate on screen sizes.
+ if (item->isPolishScheduled())
+ item->updatePolish();
+
+ if (quickItem && quickItem->matrix_ && !quickItem->matrix_->m_matrix.isIdentity()) {
+ // TODO: recalculate the center/zoom level so that the item becomes projectable again
+ if (quickItem->zoomLevel() == 0.0) // the item is unprojectable, should be skipped.
+ continue;
+
+ QRectF brect = item->boundingRect();
+ brect = quickItem->matrix_->m_matrix.mapRect(brect);
+ QPointF transformedPosition = quickItem->matrix_->m_matrix.map(item->position());
+ topLeftX = transformedPosition.x();
+ topLeftY = transformedPosition.y();
+ bottomRightX = topLeftX + brect.width();
+ bottomRightY = topLeftY + brect.height();
+ } else {
+ topLeftX = item->position().x();
+ topLeftY = item->position().y();
+ bottomRightX = topLeftX + item->width();
+ bottomRightY = topLeftY + item->height();
+ }
+
+ minX = qMin(minX, topLeftX);
+ maxX = qMax(maxX, bottomRightX);
+ minY = qMin(minY, topLeftY);
+ maxY = qMax(maxY, bottomRightY);
+
+ ++itemCount;
+ }
+
+ if (itemCount == 0) {
+ if (haveQuickItem)
+ fitViewportToMapItemsRefine(mapItems, false, onlyVisible);
+ return;
+ }
+ double bboxWidth = maxX - minX;
+ double bboxHeight = maxY - minY;
+ double bboxCenterX = minX + (bboxWidth / 2.0);
+ double bboxCenterY = minY + (bboxHeight / 2.0);
+
+ // position camera to the center of bounding box
+ QGeoCoordinate coordinate;
+ coordinate = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(bboxCenterX, bboxCenterY), false);
+ setProperty("center", QVariant::fromValue(coordinate));
+
+ // adjust zoom
+ double bboxWidthRatio = bboxWidth / (bboxWidth + bboxHeight);
+ double mapWidthRatio = width() / (width() + height());
+ double zoomRatio;
+
+ if (bboxWidthRatio > mapWidthRatio)
+ zoomRatio = bboxWidth / width();
+ else
+ zoomRatio = bboxHeight / height();
+
+ qreal newZoom = std::log10(zoomRatio) / std::log10(0.5);
+ newZoom = std::floor(qMax(minimumZoomLevel(), (zoomLevel() + newZoom)));
+ setProperty("zoomLevel", QVariant::fromValue(newZoom));
+
+ // as map quick items retain the same screen size after the camera zooms in/out
+ // we refine the viewport again to achieve better results
+ if (refine)
+ fitViewportToMapItemsRefine(mapItems, false, onlyVisible);
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::mousePressEvent(QMouseEvent *event)
+{
+ if (isInteractive() && event->source() == Qt::MouseEventNotSynthesized)
+ m_gestureArea->handleMousePressEvent(event);
+ else
+ QQuickItem::mousePressEvent(event);
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::mouseMoveEvent(QMouseEvent *event)
+{
+ if (isInteractive() && event->source() == Qt::MouseEventNotSynthesized)
+ m_gestureArea->handleMouseMoveEvent(event);
+ else
+ QQuickItem::mouseMoveEvent(event);
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::mouseReleaseEvent(QMouseEvent *event)
+{
+ if (isInteractive() && event->source() == Qt::MouseEventNotSynthesized)
+ m_gestureArea->handleMouseReleaseEvent(event);
+ else
+ QQuickItem::mouseReleaseEvent(event);
+}
+
+void QDeclarativeGeoMap::touchUngrabEvent()
+{
+ if (isInteractive())
+ m_gestureArea->handleTouchUngrabEvent();
+ else
+ QQuickItem::touchUngrabEvent();
+}
+
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::touchEvent(QTouchEvent *event)
+{
+ if (isInteractive()) {
+ m_gestureArea->handleTouchEvent(event);
+ } else {
+ //ignore event so sythesized event is generated;
+ QQuickItem::touchEvent(event);
+ }
+}
+
+#if QT_CONFIG(wheelevent)
+/*!
+ \internal
+*/
+void QDeclarativeGeoMap::wheelEvent(QWheelEvent *event)
+{
+ if (isInteractive())
+ m_gestureArea->handleWheelEvent(event);
+ else
+ QQuickItem::wheelEvent(event);
+
+}
+#endif
+
+/*!
+ \internal
+*/
+bool QDeclarativeGeoMap::childMouseEventFilter(QQuickItem *item, QEvent *event)
+{
+ Q_UNUSED(item);
+ if (!isVisible() || !isEnabled() || !isInteractive())
+ return QQuickItem::childMouseEventFilter(item, event);
+
+ switch (event->type()) {
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ case QEvent::TouchCancel:
+ return sendTouchEvent(static_cast<QTouchEvent *>(event));
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonRelease:
+ {
+ auto mEvent = static_cast<QMouseEvent*>(event);
+ if (mEvent->source() == Qt::MouseEventNotSynthesized){
+ return sendTouchEvent(mEvent);
+ }
+ }
+ break;
+ case QEvent::UngrabMouse:
+ Q_ASSERT(event->isSinglePointEvent());
+ return sendTouchEvent(static_cast<QSinglePointEvent*>(event));
+ default:
+ break;
+ }
+ return QQuickItem::childMouseEventFilter(item, event);
+}
+
+bool QDeclarativeGeoMap::sendMouseEvent(QMouseEvent *event)
+{
+ bool stealEvent = m_gestureArea->isActive();
+
+ if ((stealEvent || contains(mapFromScene(event->scenePosition())))) {
+ switch (event->type()) {
+ case QEvent::MouseMove:
+ m_gestureArea->handleMouseMoveEvent(event);
+ break;
+ case QEvent::MouseButtonPress:
+ m_gestureArea->handleMousePressEvent(event);
+ break;
+ case QEvent::MouseButtonRelease:
+ m_gestureArea->handleMouseReleaseEvent(event);
+ break;
+ default:
+ break;
+ }
+
+ stealEvent = m_gestureArea->isActive();
+
+ if (stealEvent) {
+ //do not deliver
+ event->setAccepted(true);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+
+bool QDeclarativeGeoMap::sendTouchEvent(QPointerEvent *event)
+{
+ const QTouchEvent::TouchPoint &point = event->points().first();
+
+ bool stealEvent = m_gestureArea->isActive();
+ bool containsPoint = contains(mapFromScene(point.scenePosition()));
+
+ if ((stealEvent || containsPoint)) {
+
+ m_gestureArea->handleTouchEvent(event);
+ stealEvent = m_gestureArea->isActive();
+
+ if (stealEvent) {
+ //event->setAccepted(true);
+ //return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+
+QT_END_NAMESPACE