diff options
Diffstat (limited to 'src/location')
83 files changed, 23392 insertions, 5 deletions
diff --git a/src/location/declarativemaps/declarativemaps.pri b/src/location/declarativemaps/declarativemaps.pri new file mode 100644 index 00000000..52e61049 --- /dev/null +++ b/src/location/declarativemaps/declarativemaps.pri @@ -0,0 +1,62 @@ +QT += quick-private network positioning-private qml-private core-private gui-private + +INCLUDEPATH += declarativemaps + +PUBLIC_HEADERS += \ + declarativemaps/error_messages.h + +PRIVATE_HEADERS += \ + declarativemaps/qdeclarativegeomapitemview_p.h \ + declarativemaps/qdeclarativegeomapitemview_p_p.h \ + declarativemaps/qdeclarativegeoserviceprovider_p.h \ + declarativemaps/qdeclarativegeocodemodel_p.h \ + declarativemaps/qdeclarativegeoroutemodel_p.h \ + declarativemaps/qdeclarativegeoroute_p.h \ + declarativemaps/qdeclarativegeoroutesegment_p.h \ + declarativemaps/qdeclarativegeomaneuver_p.h \ + declarativemaps/qdeclarativegeomap_p.h \ + declarativemaps/qdeclarativegeomaptype_p.h \ + declarativemaps/qdeclarativegeomapitembase_p.h \ + declarativemaps/qdeclarativegeomapquickitem_p.h \ + declarativemaps/qdeclarativecirclemapitem_p.h \ + declarativemaps/qdeclarativerectanglemapitem_p.h \ + declarativemaps/qdeclarativepolygonmapitem_p.h \ + declarativemaps/qdeclarativepolylinemapitem_p.h \ + declarativemaps/qdeclarativeroutemapitem_p.h \ + declarativemaps/qdeclarativegeomapparameter_p.h \ + declarativemaps/qgeomapitemgeometry_p.h \ + declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h \ + declarativemaps/locationvaluetypehelper_p.h \ + declarativemaps/qquickgeomapgesturearea_p.h \ + declarativemaps/mapitemviewdelegateincubator_p.h \ + ../imports/positioning/qquickgeocoordinateanimation_p.h + +SOURCES += \ + declarativemaps/qdeclarativegeomapitemview.cpp \ + declarativemaps/qdeclarativegeoserviceprovider.cpp \ + declarativemaps/qdeclarativegeocodemodel.cpp \ + declarativemaps/qdeclarativegeoroutemodel.cpp \ + declarativemaps/qdeclarativegeoroute.cpp \ + declarativemaps/qdeclarativegeoroutesegment.cpp \ + declarativemaps/qdeclarativegeomaneuver.cpp \ + declarativemaps/qdeclarativegeomap.cpp \ + declarativemaps/qdeclarativegeomaptype.cpp \ + declarativemaps/qdeclarativegeomapitembase.cpp \ + declarativemaps/qdeclarativegeomapquickitem.cpp \ + declarativemaps/qdeclarativecirclemapitem.cpp \ + declarativemaps/qdeclarativerectanglemapitem.cpp \ + declarativemaps/qdeclarativepolygonmapitem.cpp \ + declarativemaps/qdeclarativepolylinemapitem.cpp \ + declarativemaps/qdeclarativeroutemapitem.cpp \ + declarativemaps/qdeclarativegeomapparameter.cpp \ + declarativemaps/qgeomapitemgeometry.cpp \ + declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp \ + declarativemaps/error_messages.cpp \ + declarativemaps/locationvaluetypehelper.cpp \ + declarativemaps/qquickgeomapgesturearea.cpp \ + ../imports/positioning/qquickgeocoordinateanimation.cpp \ + declarativemaps/mapitemviewdelegateincubator.cpp + +LIBS_PRIVATE += -L$$MODULE_BASE_OUTDIR/lib -lpoly2tri$$qtPlatformTargetSuffix() -lclip2tri$$qtPlatformTargetSuffix() + + diff --git a/src/location/declarativemaps/error_messages.cpp b/src/location/declarativemaps/error_messages.cpp new file mode 100644 index 00000000..a2557f79 --- /dev/null +++ b/src/location/declarativemaps/error_messages.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** +** +** 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 "error_messages.h" + +QT_BEGIN_NAMESPACE + +const char CONTEXT_NAME[] = "QtLocationQML"; + +//to-be-translated error string + +const char PLUGIN_PROPERTY_NOT_SET[] = QT_TRANSLATE_NOOP("QtLocationQML", "Plugin property is not set."); +const char PLUGIN_ERROR[] = QT_TRANSLATE_NOOP("QtLocationQML", "Plugin Error (%1): %2"); +const char PLUGIN_PROVIDER_ERROR[] = QT_TRANSLATE_NOOP("QtLocationQML", "Plugin Error (%1): Could not instantiate provider"); +const char PLUGIN_NOT_VALID[] = QT_TRANSLATE_NOOP("QtLocationQML", "Plugin is not valid"); +const char CATEGORIES_NOT_INITIALIZED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Unable to initialize categories"); +const char UNABLE_TO_MAKE_REQUEST[] = QT_TRANSLATE_NOOP("QtLocationQML", "Unable to create request"); +const char INDEX_OUT_OF_RANGE[] = QT_TRANSLATE_NOOP("QtLocationQML", "Index '%1' out of range"); + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/error_messages.h b/src/location/declarativemaps/error_messages.h new file mode 100644 index 00000000..81c43b34 --- /dev/null +++ b/src/location/declarativemaps/error_messages.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef ERROR_MESSAGES_H +#define ERROR_MESSAGES_H + +#include <QtCore/qglobal.h> + +QT_BEGIN_NAMESPACE + +extern const char CONTEXT_NAME[]; +extern const char PLUGIN_PROPERTY_NOT_SET[]; +extern const char PLUGIN_ERROR[]; +extern const char PLUGIN_PROVIDER_ERROR[]; +extern const char PLUGIN_NOT_VALID[]; +extern const char CATEGORIES_NOT_INITIALIZED[]; +extern const char UNABLE_TO_MAKE_REQUEST[]; +extern const char INDEX_OUT_OF_RANGE[]; + +QT_END_NAMESPACE + +#endif // ERROR_MESSAGES_H diff --git a/src/location/declarativemaps/locationvaluetypehelper.cpp b/src/location/declarativemaps/locationvaluetypehelper.cpp new file mode 100644 index 00000000..4f39e0b4 --- /dev/null +++ b/src/location/declarativemaps/locationvaluetypehelper.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** 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 "locationvaluetypehelper_p.h" + + +QGeoCoordinate parseCoordinate(const QJSValue &value, bool *ok) +{ + QGeoCoordinate c; + + if (value.isObject()) { + if (value.hasProperty(QStringLiteral("latitude"))) + c.setLatitude(value.property(QStringLiteral("latitude")).toNumber()); + if (value.hasProperty(QStringLiteral("longitude"))) + c.setLongitude(value.property(QStringLiteral("longitude")).toNumber()); + if (value.hasProperty(QStringLiteral("altitude"))) + c.setAltitude(value.property(QStringLiteral("altitude")).toNumber()); + + if (ok) + *ok = true; + } + + return c; +} + +QGeoRectangle parseRectangle(const QJSValue &value, bool *ok) +{ + QGeoRectangle r; + + *ok = false; + + if (value.isObject()) { + if (value.hasProperty(QStringLiteral("bottomLeft"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("bottomLeft")), ok); + if (*ok) + r.setBottomLeft(c); + } + if (value.hasProperty(QStringLiteral("bottomRight"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("bottomRight")), ok); + if (*ok) + r.setBottomRight(c); + } + if (value.hasProperty(QStringLiteral("topLeft"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("topLeft")), ok); + if (*ok) + r.setTopLeft(c); + } + if (value.hasProperty(QStringLiteral("topRight"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("topRight")), ok); + if (*ok) + r.setTopRight(c); + } + if (value.hasProperty(QStringLiteral("center"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("center")), ok); + if (*ok) + r.setCenter(c); + } + if (value.hasProperty(QStringLiteral("height"))) + r.setHeight(value.property(QStringLiteral("height")).toNumber()); + if (value.hasProperty(QStringLiteral("width"))) + r.setWidth(value.property(QStringLiteral("width")).toNumber()); + } + + return r; +} + +QGeoCircle parseCircle(const QJSValue &value, bool *ok) +{ + QGeoCircle c; + + *ok = false; + + if (value.isObject()) { + if (value.hasProperty(QStringLiteral("center"))) { + QGeoCoordinate coord = parseCoordinate(value.property(QStringLiteral("center")), ok); + if (*ok) + c.setCenter(coord); + } + if (value.hasProperty(QStringLiteral("radius"))) + c.setRadius(value.property(QStringLiteral("radius")).toNumber()); + } + + return c; +} diff --git a/src/location/declarativemaps/locationvaluetypehelper_p.h b/src/location/declarativemaps/locationvaluetypehelper_p.h new file mode 100644 index 00000000..50038e88 --- /dev/null +++ b/src/location/declarativemaps/locationvaluetypehelper_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef LOCATION_VALUE_TYPE_HELPER +#define LOCATION_VALUE_TYPE_HELPER + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QJSValue> +#include <QGeoCoordinate> +#include <QGeoRectangle> +#include <QGeoCircle> + +QGeoCoordinate parseCoordinate(const QJSValue &value, bool *ok); +QGeoRectangle parseRectangle(const QJSValue &value, bool *ok); +QGeoCircle parseCircle(const QJSValue &value, bool *ok); + +#endif diff --git a/src/location/declarativemaps/mapitemviewdelegateincubator.cpp b/src/location/declarativemaps/mapitemviewdelegateincubator.cpp new file mode 100644 index 00000000..c8500e4b --- /dev/null +++ b/src/location/declarativemaps/mapitemviewdelegateincubator.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Jolla Ltd, author: Aaron McCarthy <aaron.mccarthy@jollamobile.com> +** 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 "mapitemviewdelegateincubator_p.h" +#include "qdeclarativegeomapitemview_p.h" +#include "qdeclarativegeomapitemview_p_p.h" + +QT_BEGIN_NAMESPACE + +MapItemViewDelegateIncubator::MapItemViewDelegateIncubator(QDeclarativeGeoMapItemView *view, QDeclarativeGeoMapItemViewItemData *itemData, bool batched) +: m_view(view), m_itemData(itemData), m_batched(batched) +{ +} + +void MapItemViewDelegateIncubator::statusChanged(QQmlIncubator::Status status) +{ + m_view->incubatorStatusChanged(this, status, m_batched); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/mapitemviewdelegateincubator_p.h b/src/location/declarativemaps/mapitemviewdelegateincubator_p.h new file mode 100644 index 00000000..b559c944 --- /dev/null +++ b/src/location/declarativemaps/mapitemviewdelegateincubator_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Jolla Ltd, author: Aaron McCarthy <aaron.mccarthy@jollamobile.com> +** 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$ +** +****************************************************************************/ +#ifndef MAPITEMVIEWDELEGATEINCUBATOR_H +#define MAPITEMVIEWDELEGATEINCUBATOR_H + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtQml/QQmlIncubator> + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoMapItemView; +class QDeclarativeGeoMapItemViewItemData; + +class Q_LOCATION_PRIVATE_EXPORT MapItemViewDelegateIncubator : public QQmlIncubator +{ +public: + MapItemViewDelegateIncubator(QDeclarativeGeoMapItemView *view, QDeclarativeGeoMapItemViewItemData *itemData, bool batched = true); + +protected: + void statusChanged(Status status) Q_DECL_OVERRIDE; + +private: + QDeclarativeGeoMapItemView *m_view; + QDeclarativeGeoMapItemViewItemData *m_itemData; + bool m_batched; + + friend class QDeclarativeGeoMapItemView; +}; + +QT_END_NAMESPACE + +#endif // MAPITEMVIEWDELEGATEINCUBATOR_H diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp new file mode 100644 index 00000000..39581dce --- /dev/null +++ b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp @@ -0,0 +1,728 @@ +/*************************************************************************** +** +** 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 "qdeclarativecirclemapitem_p.h" +#include "qdeclarativepolygonmapitem_p.h" +#include "qgeocameracapabilities_p.h" + +#include "qwebmercator_p.h" +#include <QtLocation/private/qgeomap_p.h> + +#include <cmath> +#include <algorithm> + +#include <QtCore/QScopedValueRollback> +#include <QPen> +#include <QPainter> +#include <QtGui/private/qtriangulator_p.h> + +#include "qdoublevector2d_p.h" +#include "qlocationutils_p.h" +#include "qgeocircle.h" + +/* poly2tri triangulator includes */ +#include <common/shapes.h> +#include <sweep/cdt.h> + +#include <QtPositioning/private/qclipperutils_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapCircle + \instantiates QDeclarativeCircleMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.5 + + \brief The MapCircle type displays a geographic circle on a Map. + + The MapCircle type displays a geographic circle on a Map, which + consists of all points that are within a set distance from one + central point. Depending on map projection, a geographic circle + may not always be a perfect circle on the screen: for instance, in + the Mercator projection, circles become ovoid in shape as they near + the poles. To display a perfect screen circle around a point, use a + MapQuickItem containing a relevant Qt Quick type instead. + + By default, the circle is displayed as a 1 pixel black border with + no fill. To change its appearance, use the color, border.color + and border.width properties. + + Internally, a MapCircle is implemented as a many-sided polygon. To + calculate the radius points it uses a spherical model of the Earth, + similar to the atDistanceAndAzimuth method of the \l {coordinate} + type. These two things can occasionally have implications for the + accuracy of the circle's shape, depending on position and map + projection. + + \note Dragging a MapCircle (through the use of \l MouseArea) + causes new points to be generated at the same distance (in meters) + from the center. This is in contrast to other map items which store + their dimensions in terms of latitude and longitude differences between + vertices. + + \section2 Performance + + MapCircle performance is almost equivalent to that of a MapPolygon with + the same number of vertices. There is a small amount of additional + overhead with respect to calculating the vertices first. + + Like the other map objects, MapCircle is normally drawn without a smooth + appearance. Setting the opacity property will force the object to be + blended, which decreases performance considerably depending on the graphics + hardware in use. + + \section2 Example Usage + + The following snippet shows a map containing a MapCircle, centered at + the coordinate (-27, 153) with a radius of 5km. The circle is + filled in green, with a 3 pixel black border. + + \code + Map { + MapCircle { + center { + latitude: -27.5 + longitude: 153.0 + } + radius: 5000.0 + color: 'green' + border.width: 3 + } + } + \endcode + + \image api-mapcircle.png +*/ + +#ifdef M_PI +#undef M_PI +#endif +#define M_PI 3.14159265358979323846264338327950288 + +static const int CircleSamples = 128; + +struct Vertex +{ + QVector2D position; +}; + +QGeoMapCircleGeometry::QGeoMapCircleGeometry() +{ +} + +/*! + \internal +*/ +void QGeoMapCircleGeometry::updateScreenPointsInvert(const QList<QGeoCoordinate> &circlePath, const QGeoMap &map) +{ + // Not checking for !screenDirty anymore, as everything is now recalculated. + clear(); + if (map.viewportWidth() == 0 || map.viewportHeight() == 0 || circlePath.size() < 3) // a circle requires at least 3 points; + return; + + /* + * No special case for no tilting as these items are very rare, and usually at most one per map. + * + * Approach: + * 1) subtract the circle from a rectangle filling the whole map, *in wrapped mercator space* + * 2) clip the resulting geometries against the visible region, *in wrapped mercator space* + * 3) create a QPainterPath with each of the resulting polygons projected to screen + * 4) use qTriangulate() to triangulate the painter path + */ + + // 1) + double topLati = QLocationUtils::mercatorMaxLatitude(); + double bottomLati = -(QLocationUtils::mercatorMaxLatitude()); + double leftLongi = QLocationUtils::mapLeftLongitude(map.cameraData().center().longitude()); + double rightLongi = QLocationUtils::mapRightLongitude(map.cameraData().center().longitude()); + + srcOrigin_ = QGeoCoordinate(topLati,leftLongi); + QDoubleVector2D tl = map.geoProjection().geoToWrappedMapProjection(QGeoCoordinate(topLati,leftLongi)); + QDoubleVector2D tr = map.geoProjection().geoToWrappedMapProjection(QGeoCoordinate(topLati,rightLongi)); + QDoubleVector2D br = map.geoProjection().geoToWrappedMapProjection(QGeoCoordinate(bottomLati,rightLongi)); + QDoubleVector2D bl = map.geoProjection().geoToWrappedMapProjection(QGeoCoordinate(bottomLati,leftLongi)); + + QList<QDoubleVector2D> fill; + fill << tl << tr << br << bl; + + QList<QDoubleVector2D> hole; + for (const QGeoCoordinate &c: circlePath) + hole << map.geoProjection().geoToWrappedMapProjection(c); + + c2t::clip2tri clipper; + clipper.addSubjectPath(QClipperUtils::qListToPath(fill), true); + clipper.addClipPolygon(QClipperUtils::qListToPath(hole)); + Paths difference = clipper.execute(c2t::clip2tri::Difference, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); + + // 2) + QDoubleVector2D lb = map.geoProjection().geoToWrappedMapProjection(srcOrigin_); + QList<QList<QDoubleVector2D> > clippedPaths; + const QList<QDoubleVector2D> &visibleRegion = map.geoProjection().visibleRegion(); + if (visibleRegion.size()) { + clipper.clearClipper(); + for (const Path &p: difference) + clipper.addSubjectPath(p, true); + clipper.addClipPolygon(QClipperUtils::qListToPath(visibleRegion)); + Paths res = clipper.execute(c2t::clip2tri::Intersection, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); + clippedPaths = QClipperUtils::pathsToQList(res); + + // 2.1) update srcOrigin_ with the point with minimum X/Y + lb = QDoubleVector2D(qInf(), qInf()); + for (const QList<QDoubleVector2D> &path: clippedPaths) { + for (const QDoubleVector2D &p: path) { + if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) { + lb = p; + } + } + } + if (qIsInf(lb.x())) + return; + + // Prevent the conversion to and from clipper from introducing negative offsets which + // in turn will make the geometry wrap around. + lb.setX(qMax(tl.x(), lb.x())); + srcOrigin_ = map.geoProjection().mapProjectionToGeo(map.geoProjection().unwrapMapProjection(lb)); + } else { + clippedPaths = QClipperUtils::pathsToQList(difference); + } + + //3) + QDoubleVector2D origin = map.geoProjection().wrappedMapProjectionToItemPosition(lb); + + QPainterPath ppi; + for (const QList<QDoubleVector2D> &path: clippedPaths) { + QDoubleVector2D lastAddedPoint; + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D point = map.geoProjection().wrappedMapProjectionToItemPosition(path.at(i)); + //point = point - origin; // Do this using ppi.translate() + + if (i == 0) { + ppi.moveTo(point.toPointF()); + lastAddedPoint = point; + } else { + if ((point - lastAddedPoint).manhattanLength() > 3 || + i == path.size() - 1) { + ppi.lineTo(point.toPointF()); + lastAddedPoint = point; + } + } + } + ppi.closeSubpath(); + } + ppi.translate(-1 * origin.toPointF()); + +#if 0 // old poly2tri code, has to be ported to clip2tri in order to work with tilted projections + std::vector<p2t::Point*> borderPts; + borderPts.reserve(4); + + std::vector<p2t::Point*> curPts; + curPts.reserve(ppi.elementCount()); + for (int i = 0; i < ppi.elementCount(); ++i) { + const QPainterPath::Element e = ppi.elementAt(i); + if (e.isMoveTo() || i == ppi.elementCount() - 1 + || (qAbs(e.x - curPts.front()->x) < 0.1 + && qAbs(e.y - curPts.front()->y) < 0.1)) { + if (curPts.size() > 2) { + for (int j = 0; j < 4; ++j) { + const QPainterPath::Element e2 = ppiBorder.elementAt(j); + borderPts.push_back(new p2t::Point(e2.x, e2.y)); + } + p2t::CDT *cdt = new p2t::CDT(borderPts); + cdt->AddHole(curPts); + cdt->Triangulate(); + std::vector<p2t::Triangle*> tris = cdt->GetTriangles(); + screenVertices_.reserve(screenVertices_.size() + int(tris.size())); + for (size_t i = 0; i < tris.size(); ++i) { + p2t::Triangle *t = tris.at(i); + for (int j = 0; j < 3; ++j) { + p2t::Point *p = t->GetPoint(j); + screenVertices_ << QPointF(p->x, p->y); + } + } + delete cdt; + } + curPts.clear(); + curPts.reserve(ppi.elementCount() - i); + curPts.push_back(new p2t::Point(e.x, e.y)); + } else if (e.isLineTo()) { + curPts.push_back(new p2t::Point(e.x, e.y)); + } else { + qWarning("Unhandled element type in circle painterpath"); + } + } + + if (curPts.size() > 0) { + qDeleteAll(curPts.begin(), curPts.end()); + curPts.clear(); + } + + if (borderPts.size() > 0) { + qDeleteAll(borderPts.begin(), borderPts.end()); + borderPts.clear(); + } +#else // Using qTriangulate as this case is not frequent, and not many circles including both poles are usually used + QTriangleSet ts = qTriangulate(ppi); + qreal *vx = ts.vertices.data(); + + screenIndices_.reserve(ts.indices.size()); + screenVertices_.reserve(ts.vertices.size()); + + if (ts.indices.type() == QVertexIndexVector::UnsignedInt) { + const quint32 *ix = reinterpret_cast<const quint32 *>(ts.indices.data()); + for (int i = 0; i < (ts.indices.size()/3*3); ++i) + screenIndices_ << ix[i]; + } else { + const quint16 *ix = reinterpret_cast<const quint16 *>(ts.indices.data()); + for (int i = 0; i < (ts.indices.size()/3*3); ++i) + screenIndices_ << ix[i]; + } + for (int i = 0; i < (ts.vertices.size()/2*2); i += 2) + screenVertices_ << QPointF(vx[i], vx[i + 1]); +#endif + + screenBounds_ = ppi.boundingRect(); + sourceBounds_ = screenBounds_; +} + +static bool crossEarthPole(const QGeoCoordinate ¢er, qreal distance) +{ + qreal poleLat = 90; + QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude()); + QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude()); + // approximate using great circle distance + qreal distanceToNorthPole = center.distanceTo(northPole); + qreal distanceToSouthPole = center.distanceTo(southPole); + if (distanceToNorthPole < distance || distanceToSouthPole < distance) + return true; + return false; +} + +static void calculatePeripheralPoints(QList<QGeoCoordinate> &path, + const QGeoCoordinate ¢er, + qreal distance, + int steps) +{ + // Calculate points based on great-circle distance + // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function + // but tweaked here for computing multiple points + + // pre-calculations + steps = qMax(steps, 3); + qreal centerLon = center.longitude(); + qreal latRad = QLocationUtils::radians(center.latitude()); + qreal lonRad = QLocationUtils::radians(centerLon); + qreal cosLatRad = std::cos(latRad); + qreal sinLatRad = std::sin(latRad); + qreal ratio = (distance / (QLocationUtils::earthMeanRadius())); + qreal cosRatio = std::cos(ratio); + qreal sinRatio = std::sin(ratio); + qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio; + qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio; + for (int i = 0; i < steps; ++i) { + qreal azimuthRad = 2 * M_PI * i / steps; + qreal resultLatRad = std::asin(sinLatRad_x_cosRatio + + cosLatRad_x_sinRatio * std::cos(azimuthRad)); + qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio, + cosRatio - sinLatRad * std::sin(resultLatRad)); + qreal lat2 = QLocationUtils::degrees(resultLatRad); + qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad)); + + path << QGeoCoordinate(lat2, lon2, center.altitude()); + } +} + +QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), color_(Qt::transparent), dirtyMaterial_(true), + updatingGeometry_(false) +{ + setFlag(ItemHasContents, true); + QObject::connect(&border_, SIGNAL(colorChanged(QColor)), + this, SLOT(markSourceDirtyAndUpdate())); + QObject::connect(&border_, SIGNAL(widthChanged(qreal)), + this, SLOT(markSourceDirtyAndUpdate())); + + // assume that circles are not self-intersecting + // to speed up processing + // FIXME: unfortunately they self-intersect at the poles due to current drawing method + // so the line is commented out until fixed + //geometry_.setAssumeSimple(true); + +} + +QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem() +{ +} + +/*! + \qmlpropertygroup Location::MapCircle::border + \qmlproperty int MapCircle::border.width + \qmlproperty color MapCircle::border.color + + This property is part of the border group property. + The border property holds the width and color used to draw the border of the circle. + The width is in pixels and is independent of the zoom level of the map. + + The default values correspond to a black border with a width of 1 pixel. + For no line, use a width of 0 or a transparent color. +*/ +QDeclarativeMapLineProperties *QDeclarativeCircleMapItem::border() +{ + return &border_; +} + +void QDeclarativeCircleMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +void QDeclarativeCircleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (map) + markSourceDirtyAndUpdate(); +} + +/*! + \qmlproperty coordinate MapCircle::center + + This property holds the central point about which the circle is defined. + + \sa radius +*/ +void QDeclarativeCircleMapItem::setCenter(const QGeoCoordinate ¢er) +{ + if (circle_.center() == center) + return; + + circle_.setCenter(center); + markSourceDirtyAndUpdate(); + emit centerChanged(center); +} + +QGeoCoordinate QDeclarativeCircleMapItem::center() +{ + return circle_.center(); +} + +/*! + \qmlproperty color MapCircle::color + + This property holds the fill color of the circle when drawn. For no fill, + use a transparent color. +*/ +void QDeclarativeCircleMapItem::setColor(const QColor &color) +{ + if (color_ == color) + return; + color_ = color; + dirtyMaterial_ = true; + update(); + emit colorChanged(color_); +} + +QColor QDeclarativeCircleMapItem::color() const +{ + return color_; +} + +/*! + \qmlproperty real MapCircle::radius + + This property holds the radius of the circle, in meters on the ground. + + \sa center +*/ +void QDeclarativeCircleMapItem::setRadius(qreal radius) +{ + if (circle_.radius() == radius) + return; + + circle_.setRadius(radius); + markSourceDirtyAndUpdate(); + emit radiusChanged(radius); +} + +qreal QDeclarativeCircleMapItem::radius() const +{ + return circle_.radius(); +} + +/*! + \qmlproperty real MapCircle::opacity + + This property holds the opacity of the item. Opacity is specified as a + number between 0 (fully transparent) and 1 (fully opaque). The default is 1. + + An item with 0 opacity will still receive mouse events. To stop mouse events, set the + visible property of the item to false. +*/ + +/*! + \internal +*/ +QSGNode *QDeclarativeCircleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + + MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode); + + if (!node) + node = new MapPolygonNode(); + + //TODO: update only material + if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { + node->update(color_, border_.color(), &geometry_, &borderGeometry_); + geometry_.setPreserveGeometry(false); + borderGeometry_.setPreserveGeometry(false); + geometry_.markClean(); + borderGeometry_.markClean(); + dirtyMaterial_ = false; + } + return node; +} + +/*! + \internal +*/ +void QDeclarativeCircleMapItem::updatePolish() +{ + if (!map() || !circle_.isValid()) + return; + + QScopedValueRollback<bool> rollback(updatingGeometry_); + updatingGeometry_ = true; + + if (geometry_.isSourceDirty()) { + circlePath_.clear(); + calculatePeripheralPoints(circlePath_, circle_.center(), circle_.radius(), CircleSamples); + } + + QList<QGeoCoordinate> originalCirclePath = circlePath_; + + int pathCount = circlePath_.size(); + bool preserve = preserveCircleGeometry(circlePath_, circle_.center(), circle_.radius()); + geometry_.setPreserveGeometry(true, circle_.boundingGeoRectangle().topLeft()); // to set the geoLeftBound_ + geometry_.setPreserveGeometry(preserve, circle_.boundingGeoRectangle().topLeft()); + + bool invertedCircle = false; + if (crossEarthPole(circle_.center(), circle_.radius()) && circlePath_.size() == pathCount) { + geometry_.updateScreenPointsInvert(circlePath_, *map()); // invert fill area for really huge circles + invertedCircle = true; + } else { + geometry_.updateSourcePoints(*map(), circlePath_); + geometry_.updateScreenPoints(*map()); + } + + borderGeometry_.clear(); + QList<QGeoMapItemGeometry *> geoms; + geoms << &geometry_; + + if (border_.color() != Qt::transparent && border_.width() > 0) { + QList<QGeoCoordinate> closedPath = circlePath_; + closedPath << closedPath.first(); + + if (invertedCircle) { + closedPath = originalCirclePath; + closedPath << closedPath.first(); + std::reverse(closedPath.begin(), closedPath.end()); + } + + borderGeometry_.setPreserveGeometry(true, circle_.boundingGeoRectangle().topLeft()); // to set the geoLeftBound_ + borderGeometry_.setPreserveGeometry(preserve, circle_.boundingGeoRectangle().topLeft()); + + // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail. + const QGeoCoordinate &geometryOrigin = geometry_.origin(); + + borderGeometry_.srcPoints_.clear(); + borderGeometry_.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = map()->geoProjection().geoToWrappedMapProjection(geometryOrigin); + borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); + borderGeometry_.updateScreenPoints(*map(), border_.width()); + geoms << &borderGeometry_; + } else { + borderGeometry_.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + setWidth(combined.width()); + setHeight(combined.height()); + + setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset()); +} + +/*! + \internal +*/ +void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + return; + + markSourceDirtyAndUpdate(); +} + +/*! + \internal +*/ +bool QDeclarativeCircleMapItem::contains(const QPointF &point) const +{ + return (geometry_.contains(point) || borderGeometry_.contains(point)); +} + +const QGeoShape &QDeclarativeCircleMapItem::geoShape() const +{ + return circle_; +} + +/*! + \internal +*/ +void QDeclarativeCircleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (updatingGeometry_ || newGeometry == oldGeometry) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + + QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) / 2; + QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint, false); + if (newCoordinate.isValid()) + setCenter(newCoordinate); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QGeoCoordinate> &path, + const QGeoCoordinate ¢er, qreal distance) +{ + // if circle crosses north/south pole, then don't preserve circular shape, + if ( crossEarthPole(center, distance)) { + updateCirclePathForRendering(path, center, distance); + return false; + } + return true; + +} + +/* + * A workaround for circle path to be drawn correctly using a polygon geometry + * This method generates a polygon like + * _____________ + * | | + * \ / + * | | + * / \ + * | | + * ------------- + * + * or a polygon like + * + * ______________ + * | ____ | + * \__/ \__/ + */ +void QDeclarativeCircleMapItem::updateCirclePathForRendering(QList<QGeoCoordinate> &path, + const QGeoCoordinate ¢er, + qreal distance) +{ + qreal poleLat = 90; + qreal distanceToNorthPole = center.distanceTo(QGeoCoordinate(poleLat, 0)); + qreal distanceToSouthPole = center.distanceTo(QGeoCoordinate(-poleLat, 0)); + bool crossNorthPole = distanceToNorthPole < distance; + bool crossSouthPole = distanceToSouthPole < distance; + + QList<int> wrapPathIndex; + QDoubleVector2D prev = map()->geoProjection().wrapMapProjection(map()->geoProjection().geoToMapProjection(path.at(0))); + + for (int i = 1; i <= path.count(); ++i) { + int index = i % path.count(); + QDoubleVector2D point = map()->geoProjection().wrapMapProjection(map()->geoProjection().geoToMapProjection(path.at(index))); + double diff = qAbs(point.x() - prev.x()); + if (diff > 0.5) { + continue; + } + } + + // find the points in path where wrapping occurs + for (int i = 1; i <= path.count(); ++i) { + int index = i % path.count(); + QDoubleVector2D point = map()->geoProjection().wrapMapProjection(map()->geoProjection().geoToMapProjection(path.at(index))); + if ( (qAbs(point.x() - prev.x())) >= 0.5 ) { // TODO: Add a projectionWidth to GeoProjection, perhaps? + wrapPathIndex << index; + if (wrapPathIndex.size() == 2 || !(crossNorthPole && crossSouthPole)) + break; + } + prev = point; + } + // insert two additional coords at top/bottom map corners of the map for shape + // to be drawn correctly + if (wrapPathIndex.size() > 0) { + qreal newPoleLat = 90; + QGeoCoordinate wrapCoord = path.at(wrapPathIndex[0]); + if (wrapPathIndex.size() == 2) { + QGeoCoordinate wrapCoord2 = path.at(wrapPathIndex[1]); + if (wrapCoord2.latitude() > wrapCoord.latitude()) + newPoleLat = -90; + } else if (center.latitude() < 0) { + newPoleLat = -90; + } + for (int i = 0; i < wrapPathIndex.size(); ++i) { + int index = wrapPathIndex[i] == 0 ? 0 : wrapPathIndex[i] + i*2; + int prevIndex = (index - 1) < 0 ? (path.count() - 1): index - 1; + QGeoCoordinate coord0 = path.at(prevIndex); + QGeoCoordinate coord1 = path.at(index); + coord0.setLatitude(newPoleLat); + coord1.setLatitude(newPoleLat); + path.insert(index ,coord1); + path.insert(index, coord0); + newPoleLat = -newPoleLat; + } + } +} + +////////////////////////////////////////////////////////////////////// + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem_p.h b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h new file mode 100644 index 00000000..62bef6d3 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECIRCLEMAPITEM_H +#define QDECLARATIVECIRCLEMAPITEM_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeomapitembase_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p.h> +#include <QSGGeometryNode> +#include <QSGFlatColorMaterial> +#include <QtPositioning/QGeoCircle> + +QT_BEGIN_NAMESPACE + +class QGeoMapCircleGeometry : public QGeoMapPolygonGeometry +{ +public: + QGeoMapCircleGeometry(); + + void updateScreenPointsInvert(const QList<QGeoCoordinate> &circlePath, const QGeoMap &map); +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItem : public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged) + Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + +public: + explicit QDeclarativeCircleMapItem(QQuickItem *parent = 0); + ~QDeclarativeCircleMapItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) Q_DECL_OVERRIDE; + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; + + QGeoCoordinate center(); + void setCenter(const QGeoCoordinate ¢er); + + qreal radius() const; + void setRadius(qreal radius); + + QColor color() const; + void setColor(const QColor &color); + + QDeclarativeMapLineProperties *border(); + + bool contains(const QPointF &point) const Q_DECL_OVERRIDE; + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; + +Q_SIGNALS: + void centerChanged(const QGeoCoordinate ¢er); + void radiusChanged(qreal radius); + void colorChanged(const QColor &color); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void updatePolish() Q_DECL_OVERRIDE; + +protected Q_SLOTS: + void markSourceDirtyAndUpdate(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) Q_DECL_OVERRIDE; + +private: + bool preserveCircleGeometry(QList<QGeoCoordinate> &path, const QGeoCoordinate ¢er, + qreal distance); + void updateCirclePathForRendering(QList<QGeoCoordinate> &path, const QGeoCoordinate ¢er, + qreal distance); + +private: + QGeoCircle circle_; + QDeclarativeMapLineProperties border_; + QColor color_; + QList<QGeoCoordinate> circlePath_; + bool dirtyMaterial_; + QGeoMapCircleGeometry geometry_; + QGeoMapPolylineGeometry borderGeometry_; + bool updatingGeometry_; +}; + +////////////////////////////////////////////////////////////////////// + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeCircleMapItem) + +#endif /* QDECLARATIVECIRCLEMAPITEM_H */ diff --git a/src/location/declarativemaps/qdeclarativegeocodemodel.cpp b/src/location/declarativemaps/qdeclarativegeocodemodel.cpp new file mode 100644 index 00000000..e92949ee --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeocodemodel.cpp @@ -0,0 +1,727 @@ +/**************************************************************************** +** +** 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 "qdeclarativegeocodemodel_p.h" +#include "error_messages.h" + +#include <QtCore/QCoreApplication> +#include <QtQml/QQmlInfo> +#include <QtPositioning/QGeoCircle> +#include <QtLocation/QGeoServiceProvider> +#include <QtLocation/QGeoCodingManager> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype GeocodeModel + \instantiates QDeclarativeGeocodeModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-geocoding + \since Qt Location 5.5 + + \brief The GeocodeModel type provides support for searching operations + related to geographic information. + + The GeocodeModel type is used as part of a model/view grouping to + match addresses or search strings with geographic locations. How the + geographic locations generated are used or displayed is decided by any + Views attached to the GeocodeModel (for example a \l MapItemView or \l{ListView}). + + Like \l Map and \l RouteModel, all the data for a GeocodeModel to work + comes from a services plugin. This is contained in the \l{plugin} property, + and this must be set before the GeocodeModel can do any useful work. + + Once the plugin is set, the \l{query} property can be used to specify the + address or search string to match. If \l{autoUpdate} is enabled, the Model + will update its output automatically. Otherwise, the \l{update} method may + be used. By default, autoUpdate is disabled. + + The data stored and returned in the GeocodeModel consists of \l{Location} + objects, as a list with the role name "locationData". See the documentation + for \l{Location} for further details on its structure and contents. + + \section2 Example Usage + + The following snippet is two-part, showing firstly the declaration of + objects, and secondly a short piece of procedural code using it. We set + the geocodeModel's \l{autoUpdate} property to false, and call \l{update} once + the query is set up. In this case, as we use a string value in \l{query}, + only one update would occur, even with autoUpdate enabled. However, if we + provided an \l{Address} object we may inadvertently trigger multiple + requests whilst setting its properties. + + \code + Plugin { + id: aPlugin + } + + GeocodeModel { + id: geocodeModel + plugin: aPlugin + autoUpdate: false + } + \endcode + + \code + { + geocodeModel.query = "53 Brandl St, Eight Mile Plains, Australia" + geocodeModel.update() + } + \endcode +*/ + +/*! + \qmlsignal QtLocation::GeocodeModel::locationsChanged() + + This signal is emitted when locations in the model have changed. + + \sa count +*/ + + +QDeclarativeGeocodeModel::QDeclarativeGeocodeModel(QObject *parent) +: QAbstractListModel(parent), autoUpdate_(false), complete_(false), reply_(0), plugin_(0), + status_(QDeclarativeGeocodeModel::Null), error_(QDeclarativeGeocodeModel::NoError), + address_(0), limit_(-1), offset_(0) +{ +} + +QDeclarativeGeocodeModel::~QDeclarativeGeocodeModel() +{ + qDeleteAll(declarativeLocations_); + declarativeLocations_.clear(); + delete reply_; +} + +/*! + \internal + From QQmlParserStatus +*/ +void QDeclarativeGeocodeModel::componentComplete() +{ + complete_ = true; + if (autoUpdate_) + update(); +} + +/*! + \qmlmethod void QtLocation::GeocodeModel::update() + + Instructs the GeocodeModel to update its data. This is most useful + when \l autoUpdate is disabled, to force a refresh when the query + has been changed. +*/ +void QDeclarativeGeocodeModel::update() +{ + if (!complete_) + return; + + if (!plugin_) { + setError(EngineNotSetError, tr("Cannot geocode, plugin not set.")); + return; + } + + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + if (!serviceProvider) + return; + + QGeoCodingManager *geocodingManager = serviceProvider->geocodingManager(); + if (!geocodingManager) { + setError(EngineNotSetError, tr("Cannot geocode, geocode manager not set.")); + return; + } + if (!coordinate_.isValid() && (!address_ || address_->address().isEmpty()) && + (searchString_.isEmpty())) { + setError(ParseError, tr("Cannot geocode, valid query not set.")); + return; + } + abortRequest(); // abort possible previous requests + setError(NoError, QString()); + + if (coordinate_.isValid()) { + setStatus(QDeclarativeGeocodeModel::Loading); + reply_ = geocodingManager->reverseGeocode(coordinate_, boundingArea_); + if (reply_->isFinished()) { + if (reply_->error() == QGeoCodeReply::NoError) { + geocodeFinished(reply_); + } else { + geocodeError(reply_, reply_->error(), reply_->errorString()); + } + } + } else if (address_) { + setStatus(QDeclarativeGeocodeModel::Loading); + reply_ = geocodingManager->geocode(address_->address(), boundingArea_); + if (reply_->isFinished()) { + if (reply_->error() == QGeoCodeReply::NoError) { + geocodeFinished(reply_); + } else { + geocodeError(reply_, reply_->error(), reply_->errorString()); + } + } + } else if (!searchString_.isEmpty()) { + setStatus(QDeclarativeGeocodeModel::Loading); + reply_ = geocodingManager->geocode(searchString_, limit_, offset_, boundingArea_); + if (reply_->isFinished()) { + if (reply_->error() == QGeoCodeReply::NoError) { + geocodeFinished(reply_); + } else { + geocodeError(reply_, reply_->error(), reply_->errorString()); + } + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::abortRequest() +{ + if (reply_) { + reply_->abort(); + reply_->deleteLater(); + reply_ = 0; + } +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::queryContentChanged() +{ + if (autoUpdate_) + update(); +} + +/*! + \internal + From QAbstractListModel +*/ +int QDeclarativeGeocodeModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return declarativeLocations_.count(); +} + +/*! + \internal +*/ +QVariant QDeclarativeGeocodeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + if (index.row() >= declarativeLocations_.count()) + return QVariant(); + if (role == QDeclarativeGeocodeModel::LocationRole) { + QObject *locationObject = declarativeLocations_.at(index.row()); + Q_ASSERT(locationObject); + return QVariant::fromValue(locationObject); + } + return QVariant(); +} + +QHash<int, QByteArray> QDeclarativeGeocodeModel::roleNames() const +{ + QHash<int, QByteArray> roleNames = QAbstractItemModel::roleNames(); + roleNames.insert(LocationRole, "locationData"); + return roleNames; +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (plugin_ == plugin) + return; + + reset(); // reset the model + plugin_ = plugin; + if (complete_) + emit pluginChanged(); + + if (!plugin) + return; + + if (plugin_->isAttached()) { + pluginReady(); + } else { + connect(plugin_, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::pluginReady() +{ + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + QGeoCodingManager *geocodingManager = serviceProvider->geocodingManager(); + + if (serviceProvider->error() != QGeoServiceProvider::NoError) { + QDeclarativeGeocodeModel::GeocodeError newError = UnknownError; + switch (serviceProvider->error()) { + case QGeoServiceProvider::NotSupportedError: + newError = EngineNotSetError; break; + case QGeoServiceProvider::UnknownParameterError: + newError = UnknownParameterError; break; + case QGeoServiceProvider::MissingRequiredParameterError: + newError = MissingRequiredParameterError; break; + case QGeoServiceProvider::ConnectionError: + newError = CommunicationError; break; + default: + break; + } + + setError(newError, serviceProvider->errorString()); + return; + } + + if (!geocodingManager) { + setError(EngineNotSetError,tr("Plugin does not support (reverse) geocoding.")); + return; + } + + connect(geocodingManager, SIGNAL(finished(QGeoCodeReply*)), + this, SLOT(geocodeFinished(QGeoCodeReply*))); + connect(geocodingManager, SIGNAL(error(QGeoCodeReply*,QGeoCodeReply::Error,QString)), + this, SLOT(geocodeError(QGeoCodeReply*,QGeoCodeReply::Error,QString))); +} + +/*! + \qmlproperty Plugin QtLocation::GeocodeModel::plugin + + This property holds the plugin that provides the actual geocoding service. + Note that all plugins do not necessarily provide geocoding (could for example provide + only routing or maps). + + \sa Plugin +*/ + +QDeclarativeGeoServiceProvider *QDeclarativeGeocodeModel::plugin() const +{ + return plugin_; +} + +void QDeclarativeGeocodeModel::setBounds(const QVariant &boundingArea) +{ + QGeoShape s; + + if (boundingArea.userType() == qMetaTypeId<QGeoRectangle>()) + s = boundingArea.value<QGeoRectangle>(); + else if (boundingArea.userType() == qMetaTypeId<QGeoCircle>()) + s = boundingArea.value<QGeoCircle>(); + else if (boundingArea.userType() == qMetaTypeId<QGeoShape>()) + s = boundingArea.value<QGeoShape>(); + + + if (boundingArea_ == s) + return; + + boundingArea_ = s; + emit boundsChanged(); +} + +/*! + \qmlproperty geoshape QtLocation::GeocodeModel::bounds + + This property holds the bounding area used to limit the results to those + within the area. This is particularly useful if query is only partially filled out, + as the service will attempt to (reverse) geocode all matches for the specified data. + + Accepted types are \l {georectangle} and + \l {geocircle}. +*/ +QVariant QDeclarativeGeocodeModel::bounds() const +{ + if (boundingArea_.type() == QGeoShape::RectangleType) + return QVariant::fromValue(QGeoRectangle(boundingArea_)); + else if (boundingArea_.type() == QGeoShape::CircleType) + return QVariant::fromValue(QGeoCircle(boundingArea_)); + else + return QVariant::fromValue(boundingArea_); +} + +void QDeclarativeGeocodeModel::geocodeFinished(QGeoCodeReply *reply) +{ + if (reply != reply_ || reply->error() != QGeoCodeReply::NoError) + return; + int oldCount = declarativeLocations_.count(); + setLocations(reply->locations()); + setError(NoError, QString()); + setStatus(QDeclarativeGeocodeModel::Ready); + reply->deleteLater(); + reply_ = 0; + emit locationsChanged(); + if (oldCount != declarativeLocations_.count()) + emit countChanged(); +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::geocodeError(QGeoCodeReply *reply, + QGeoCodeReply::Error error, + const QString &errorString) +{ + if (reply != reply_) + return; + Q_UNUSED(error); + int oldCount = declarativeLocations_.count(); + if (oldCount > 0) { + // Reset the model + setLocations(reply->locations()); + emit locationsChanged(); + emit countChanged(); + } + setError(static_cast<QDeclarativeGeocodeModel::GeocodeError>(error), errorString); + setStatus(QDeclarativeGeocodeModel::Error); + reply->deleteLater(); + reply_ = 0; +} + +/*! + \qmlproperty enumeration QtLocation::GeocodeModel::status + + This read-only property holds the current status of the model. + + \list + \li GeocodeModel.Null - No geocode requests have been issued or \l reset has been called. + \li GeocodeModel.Ready - Geocode request(s) have finished successfully. + \li GeocodeModel.Loading - Geocode request has been issued but not yet finished + \li GeocodeModel.Error - Geocoding error has occurred, details are in \l error and \l errorString + \endlist +*/ + +QDeclarativeGeocodeModel::Status QDeclarativeGeocodeModel::status() const +{ + return status_; +} + +void QDeclarativeGeocodeModel::setStatus(QDeclarativeGeocodeModel::Status status) +{ + if (status_ == status) + return; + status_ = status; + emit statusChanged(); +} + +/*! + \qmlproperty enumeration QtLocation::GeocodeModel::error + + This read-only property holds the latest error value of the geocoding request. + + \list + \li GeocodeModel.NoError - No error has occurred. + \li GeocodeModel.CombinationError - An error occurred while results where being combined from multiple sources. + \li GeocodeModel.CommunicationError - An error occurred while communicating with the service provider. + \li GeocodeModel.EngineNotSetError - The model's plugin property was not set or there is no geocoding manager associated with the plugin. + \li GeocodeModel.MissingRequiredParameterError - A required parameter was not specified. + \li GeocodeModel.ParseError - The response from the service provider was in an unrecognizable format. + \li GeocodeModel.UnknownError - An error occurred which does not fit into any of the other categories. + \li GeocodeModel.UnknownParameterError - The plugin did not recognize one of the parameters it was given. + \li GeocodeModel.UnsupportedOptionError - The requested operation is not supported by the geocoding provider. + This may happen when the loaded engine does not support a particular geocoding request + such as reverse geocoding. + \endlist +*/ + +QDeclarativeGeocodeModel::GeocodeError QDeclarativeGeocodeModel::error() const +{ + return error_; +} + +void QDeclarativeGeocodeModel::setError(GeocodeError error, const QString &errorString) +{ + if (error_ == error && errorString_ == errorString) + return; + error_ = error; + errorString_ = errorString; + emit errorChanged(); +} + +/*! + \qmlproperty string QtLocation::GeocodeModel::errorString + + This read-only property holds the textual presentation of the latest geocoding error. + If no error has occurred or the model has been reset, an empty string is returned. + + An empty string may also be returned if an error occurred which has no associated + textual representation. +*/ + +QString QDeclarativeGeocodeModel::errorString() const +{ + return errorString_; +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::setLocations(const QList<QGeoLocation> &locations) +{ + beginResetModel(); + qDeleteAll(declarativeLocations_); + declarativeLocations_.clear(); + for (int i = 0; i < locations.count(); ++i) { + QDeclarativeGeoLocation *location = new QDeclarativeGeoLocation(locations.at(i), this); + declarativeLocations_.append(location); + } + endResetModel(); +} + +/*! + \qmlproperty int QtLocation::GeocodeModel::count + + This property holds how many locations the model currently has. + Amongst other uses, you can use this value when accessing locations + via the GeocodeModel::get -method. +*/ + +int QDeclarativeGeocodeModel::count() const +{ + return declarativeLocations_.count(); +} + +/*! + \qmlmethod Location QtLocation::GeocodeModel::get(int) + + Returns the Location at given index. Use \l count property to check the + amount of locations available. The locations are indexed from zero, so the accessible range + is 0...(count - 1). + + If you access out of bounds, a zero (null object) is returned and a warning is issued. +*/ + +QDeclarativeGeoLocation *QDeclarativeGeocodeModel::get(int index) +{ + if (index < 0 || index >= declarativeLocations_.count()) { + qmlWarning(this) << QStringLiteral("Index '%1' out of range").arg(index); + return 0; + } + return declarativeLocations_.at(index); +} + +/*! + \qmlproperty int QtLocation::GeocodeModel::limit + + This property holds the maximum number of results. The limit and \l offset values are only + applicable with free string geocoding (that is they are not considered when using addresses + or coordinates in the search query). + + If limit is -1 the entire result set will be returned, otherwise at most limit results will be + returned. The limit and \l offset results can be used together to implement paging. +*/ + +int QDeclarativeGeocodeModel::limit() const +{ + return limit_; +} + +void QDeclarativeGeocodeModel::setLimit(int limit) +{ + if (limit == limit_) + return; + limit_ = limit; + if (autoUpdate_) { + update(); + } + emit limitChanged(); +} + +/*! + \qmlproperty int QtLocation::GeocodeModel::offset + + This property tells not to return the first 'offset' number of the results. The \l limit and + offset values are only applicable with free string geocoding (that is they are not considered + when using addresses or coordinates in the search query). + + The \l limit and offset results can be used together to implement paging. +*/ + +int QDeclarativeGeocodeModel::offset() const +{ + return offset_; +} + +void QDeclarativeGeocodeModel::setOffset(int offset) +{ + if (offset == offset_) + return; + offset_ = offset; + if (autoUpdate_) { + update(); + } + emit offsetChanged(); +} + +/*! + \qmlmethod void QtLocation::GeocodeModel::reset() + + Resets the model. All location data is cleared, any outstanding requests + are aborted and possible errors are cleared. Model status will be set + to GeocodeModel.Null +*/ + +void QDeclarativeGeocodeModel::reset() +{ + beginResetModel(); + if (!declarativeLocations_.isEmpty()) { + setLocations(QList<QGeoLocation>()); + emit countChanged(); + } + endResetModel(); + + abortRequest(); + setError(NoError, QString()); + setStatus(QDeclarativeGeocodeModel::Null); +} + +/*! + \qmlmethod void QtLocation::GeocodeModel::cancel() + + Cancels any outstanding requests and clears errors. Model status will be set to either + GeocodeModel.Null or GeocodeModel.Ready. +*/ +void QDeclarativeGeocodeModel::cancel() +{ + abortRequest(); + setError(NoError, QString()); + setStatus(declarativeLocations_.isEmpty() ? Null : Ready); +} + +/*! + \qmlproperty QVariant QtLocation::GeocodeModel::query + + This property holds the data of the geocoding request. + The property accepts three types of queries which determine both the data and + the type of action to be performed: + + \list + \li Address - Geocoding (address to coordinate) + \li \l {coordinate} - Reverse geocoding (coordinate to address) + \li string - Geocoding (address to coordinate) + \endlist +*/ + +QVariant QDeclarativeGeocodeModel::query() const +{ + return queryVariant_; +} + +void QDeclarativeGeocodeModel::setQuery(const QVariant &query) +{ + if (query == queryVariant_) + return; + + if (query.userType() == qMetaTypeId<QGeoCoordinate>()) { + if (address_) { + address_->disconnect(this); + address_ = 0; + } + searchString_.clear(); + + coordinate_ = query.value<QGeoCoordinate>(); + } else if (query.type() == QVariant::String) { + searchString_ = query.toString(); + if (address_) { + address_->disconnect(this); + address_ = 0; + } + coordinate_ = QGeoCoordinate(); + } else if (QObject *object = query.value<QObject *>()) { + if (QDeclarativeGeoAddress *address = qobject_cast<QDeclarativeGeoAddress *>(object)) { + if (address_) + address_->disconnect(this); + coordinate_ = QGeoCoordinate(); + searchString_.clear(); + + address_ = address; + connect(address_, SIGNAL(countryChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(countryCodeChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(stateChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(countyChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(cityChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(districtChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(streetChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(postalCodeChanged()), this, SLOT(queryContentChanged())); + } else { + qmlWarning(this) << QStringLiteral("Unsupported query type for geocode model ") + << QStringLiteral("(coordinate, string and Address supported)."); + return; + } + } else { + qmlWarning(this) << QStringLiteral("Unsupported query type for geocode model ") + << QStringLiteral("(coordinate, string and Address supported)."); + return; + } + + queryVariant_ = query; + emit queryChanged(); + if (autoUpdate_) + update(); +} + +/*! + \qmlproperty bool QtLocation::GeocodeModel::autoUpdate + + This property controls whether the Model automatically updates in response + to changes in its attached query. The default value of this property + is false. + + If setting this value to 'true' and using an Address or + \l {coordinate} as the query, note that any change at all in the + object's properties will trigger a new request to be sent. If you are adjusting many + properties of the object whilst autoUpdate is enabled, this can generate large numbers of + useless (and later discarded) requests. +*/ + +bool QDeclarativeGeocodeModel::autoUpdate() const +{ + return autoUpdate_; +} + +void QDeclarativeGeocodeModel::setAutoUpdate(bool update) +{ + if (autoUpdate_ == update) + return; + autoUpdate_ = update; + emit autoUpdateChanged(); +} + +#include "moc_qdeclarativegeocodemodel_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeocodemodel_p.h b/src/location/declarativemaps/qdeclarativegeocodemodel_p.h new file mode 100644 index 00000000..6c8f533b --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeocodemodel_p.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOCODEMODEL_H +#define QDECLARATIVEGEOCODEMODEL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeoserviceprovider_p.h> + +#include <QtLocation/qgeocodereply.h> +#include <QtPositioning/private/qdeclarativegeoaddress_p.h> +#include <QtPositioning/private/qdeclarativegeolocation_p.h> + +#include <QtQml/qqml.h> +#include <QtQml/QQmlParserStatus> +#include <QAbstractListModel> +#include <QPointer> + + +QT_BEGIN_NAMESPACE + +class QGeoServiceProvider; +class QGeoCodingManager; +class QDeclarativeGeoLocation; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeocodeModel : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + Q_ENUMS(Status) + Q_ENUMS(GeocodeError) + + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(bool autoUpdate READ autoUpdate WRITE setAutoUpdate NOTIFY autoUpdateChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(int limit READ limit WRITE setLimit NOTIFY limitChanged) + Q_PROPERTY(int offset READ offset WRITE setOffset NOTIFY offsetChanged) + Q_PROPERTY(QVariant query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(QVariant bounds READ bounds WRITE setBounds NOTIFY boundsChanged) + Q_PROPERTY(GeocodeError error READ error NOTIFY errorChanged) + Q_INTERFACES(QQmlParserStatus) + +public: + enum Status { + Null, + Ready, + Loading, + Error + }; + + enum GeocodeError { + NoError = QGeoCodeReply::NoError, + EngineNotSetError = QGeoCodeReply::EngineNotSetError, //TODO Qt6 consider merge with NotSupportedError + CommunicationError = QGeoCodeReply::CommunicationError, //TODO Qt6 merge with Map's ConnectionError + ParseError = QGeoCodeReply::ParseError, + UnsupportedOptionError = QGeoCodeReply::UnsupportedOptionError, //TODO Qt6 consider rename UnsupportedOperationError + CombinationError = QGeoCodeReply::CombinationError, + UnknownError = QGeoCodeReply::UnknownError, + //we leave gap for future QGeoCodeReply errors + + //QGeoServiceProvider related errors start here + UnknownParameterError = 100, + MissingRequiredParameterError + }; + + enum Roles { + LocationRole = Qt::UserRole + 1 + }; + + explicit QDeclarativeGeocodeModel(QObject *parent = 0); + virtual ~QDeclarativeGeocodeModel(); + + // From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + // From QAbstractListModel + virtual int rowCount(const QModelIndex &parent) const; + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QHash<int,QByteArray> roleNames() const; + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + void setBounds(const QVariant &boundingArea); + QVariant bounds() const; + + Status status() const; + QString errorString() const; + GeocodeError error() const; + + bool autoUpdate() const; + void setAutoUpdate(bool update); + + int count() const; + Q_INVOKABLE QDeclarativeGeoLocation *get(int index); + + int limit() const; + void setLimit(int limit); + int offset() const; + void setOffset(int offset); + + QVariant query() const; + void setQuery(const QVariant &query); + Q_INVOKABLE void reset(); + Q_INVOKABLE void cancel(); + +Q_SIGNALS: + void countChanged(); + void pluginChanged(); + void statusChanged(); + void errorChanged(); //emitted also for errorString notification + void locationsChanged(); + void autoUpdateChanged(); + void boundsChanged(); + void queryChanged(); + void limitChanged(); + void offsetChanged(); + +public Q_SLOTS: + void update(); + +protected Q_SLOTS: + void queryContentChanged(); + void geocodeFinished(QGeoCodeReply *reply); + void geocodeError(QGeoCodeReply *reply, + QGeoCodeReply::Error error, + const QString &errorString); + void pluginReady(); + +protected: + QGeoCodingManager *searchManager(); + void setStatus(Status status); + void setError(GeocodeError error, const QString &errorString); + bool autoUpdate_; + bool complete_; + +private: + void setLocations(const QList<QGeoLocation> &locations); + void abortRequest(); + QGeoCodeReply *reply_; + + QDeclarativeGeoServiceProvider *plugin_; + QGeoShape boundingArea_; + + QList<QDeclarativeGeoLocation *> declarativeLocations_; + + Status status_; + QString errorString_; + GeocodeError error_; + QVariant queryVariant_; + QGeoCoordinate coordinate_; + QDeclarativeGeoAddress *address_; + QString searchString_; + + int limit_; + int offset_; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomaneuver.cpp b/src/location/declarativemaps/qdeclarativegeomaneuver.cpp new file mode 100644 index 00000000..72c38865 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomaneuver.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** 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 "qdeclarativegeomaneuver_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype RouteManeuver + \instantiates QDeclarativeGeoManeuver + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since Qt Location 5.5 + + \brief The RouteManeuver type represents the information relevant to the + point at which two RouteSegments meet. + + RouteSegment instances can be thought of as edges on a routing + graph, with RouteManeuver instances as optional labels attached to the + vertices of the graph. + + The most interesting information held in a RouteManeuver instance is + normally the textual navigation to provide and the position at which to + provide it, accessible by \l instructionText and \l position respectively. + + \section1 Example + + The following QML snippet demonstrates how to print information about a + route maneuver: + + \snippet declarative/routing.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/routing.qml RouteManeuver +*/ + +QDeclarativeGeoManeuver::QDeclarativeGeoManeuver(QObject *parent) + : QObject(parent) +{ +} + +QDeclarativeGeoManeuver::QDeclarativeGeoManeuver(const QGeoManeuver &maneuver, QObject *parent) + : QObject(parent), + maneuver_(maneuver) +{ +} + +QDeclarativeGeoManeuver::~QDeclarativeGeoManeuver() {} + +/*! + \qmlproperty bool RouteManeuver::valid + + This read-only property holds whether this maneuver is valid or not. + + Invalid maneuvers are used when there is no information + that needs to be attached to the endpoint of a QGeoRouteSegment instance. +*/ + +bool QDeclarativeGeoManeuver::valid() const +{ + return maneuver_.isValid(); +} + +/*! + \qmlproperty coordinate RouteManeuver::position + + This read-only property holds where the \l instructionText should be displayed. + +*/ + +QGeoCoordinate QDeclarativeGeoManeuver::position() const +{ + return maneuver_.position(); +} + +/*! + \qmlproperty string RouteManeuver::instructionText + + This read-only property holds textual navigation instruction. +*/ + +QString QDeclarativeGeoManeuver::instructionText() const +{ + return maneuver_.instructionText(); +} + +/*! + \qmlproperty enumeration RouteManeuver::direction + + Describes the change in direction associated with the instruction text + that is associated with a RouteManeuver. + + \list + \li RouteManeuver.NoDirection - There is no direction associated with the instruction text + \li RouteManeuver.DirectionForward - The instruction indicates that the direction of travel does not need to change + \li RouteManeuver.DirectionBearRight - The instruction indicates that the direction of travel should bear to the right + \li RouteManeuver.DirectionLightRight - The instruction indicates that a light turn to the right is required + \li RouteManeuver.DirectionRight - The instruction indicates that a turn to the right is required + \li RouteManeuver.DirectionHardRight - The instruction indicates that a hard turn to the right is required + \li RouteManeuver.DirectionUTurnRight - The instruction indicates that a u-turn to the right is required + \li RouteManeuver.DirectionUTurnLeft - The instruction indicates that a u-turn to the left is required + \li RouteManeuver.DirectionHardLeft - The instruction indicates that a hard turn to the left is required + \li RouteManeuver.DirectionLeft - The instruction indicates that a turn to the left is required + \li RouteManeuver.DirectionLightLeft - The instruction indicates that a light turn to the left is required + \li RouteManeuver.DirectionBearLeft - The instruction indicates that the direction of travel should bear to the left + \endlist +*/ + +QDeclarativeGeoManeuver::Direction QDeclarativeGeoManeuver::direction() const +{ + return QDeclarativeGeoManeuver::Direction(maneuver_.direction()); +} + +/*! + \qmlproperty int RouteManeuver::timeToNextInstruction + + This read-only property holds the estimated time it will take to travel + from the point at which the associated instruction was issued and the + point that the next instruction should be issued, in seconds. +*/ + +int QDeclarativeGeoManeuver::timeToNextInstruction() const +{ + return maneuver_.timeToNextInstruction(); +} + +/*! + \qmlproperty real RouteManeuver::distanceToNextInstruction + + This read-only property holds the distance, in meters, between the point at which + the associated instruction was issued and the point that the next instruction should + be issued. +*/ + +qreal QDeclarativeGeoManeuver::distanceToNextInstruction() const +{ + return maneuver_.distanceToNextInstruction(); +} + +/*! + \qmlproperty coordinate RouteManeuver::waypoint + + This property holds the waypoint associated with this maneuver. + All maneuvers do not have a waypoint associated with them, this + can be checked with \l waypointValid. + +*/ + +QGeoCoordinate QDeclarativeGeoManeuver::waypoint() const +{ + return maneuver_.waypoint(); +} + +/*! + \qmlproperty bool RouteManeuver::waypointValid + + This read-only property holds whether this \l waypoint, associated with this + maneuver, is valid or not. +*/ + +bool QDeclarativeGeoManeuver::waypointValid() const +{ + return maneuver_.waypoint().isValid(); +} + +#include "moc_qdeclarativegeomaneuver_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomaneuver_p.h b/src/location/declarativemaps/qdeclarativegeomaneuver_p.h new file mode 100644 index 00000000..0e957a1f --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomaneuver_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMANEUVER_H +#define QDECLARATIVEGEOMANEUVER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/qgeomaneuver.h> + +#include <QtPositioning/QGeoCoordinate> + +#include <QObject> + + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoManeuver : public QObject +{ + Q_OBJECT + Q_ENUMS(Direction) + + Q_PROPERTY(bool valid READ valid CONSTANT) + Q_PROPERTY(QGeoCoordinate position READ position CONSTANT) + Q_PROPERTY(QString instructionText READ instructionText CONSTANT) + Q_PROPERTY(Direction direction READ direction CONSTANT) + Q_PROPERTY(int timeToNextInstruction READ timeToNextInstruction CONSTANT) + Q_PROPERTY(qreal distanceToNextInstruction READ distanceToNextInstruction CONSTANT) + Q_PROPERTY(QGeoCoordinate waypoint READ waypoint CONSTANT) + Q_PROPERTY(bool waypointValid READ waypointValid CONSTANT) + +public: + enum Direction { + NoDirection = QGeoManeuver::NoDirection, + DirectionForward = QGeoManeuver::DirectionForward, + DirectionBearRight = QGeoManeuver::DirectionBearRight, + DirectionLightRight = QGeoManeuver::DirectionLightRight, + DirectionRight = QGeoManeuver::DirectionRight, + DirectionHardRight = QGeoManeuver::DirectionHardRight, + DirectionUTurnRight = QGeoManeuver::DirectionUTurnRight, + DirectionUTurnLeft = QGeoManeuver::DirectionUTurnLeft, + DirectionHardLeft = QGeoManeuver::DirectionHardLeft, + DirectionLeft = QGeoManeuver::DirectionLeft, + DirectionLightLeft = QGeoManeuver::DirectionLightLeft, + DirectionBearLeft = QGeoManeuver::DirectionBearLeft + }; + + explicit QDeclarativeGeoManeuver(QObject *parent = 0); + QDeclarativeGeoManeuver(const QGeoManeuver &maneuver, QObject *parent = 0); + ~QDeclarativeGeoManeuver(); + + bool valid() const; + bool waypointValid() const; + + QGeoCoordinate position() const; + QString instructionText() const; + Direction direction() const; + int timeToNextInstruction() const; + qreal distanceToNextInstruction() const; + QGeoCoordinate waypoint() const; + +private: + QGeoManeuver maneuver_; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomap.cpp b/src/location/declarativemaps/qdeclarativegeomap.cpp new file mode 100644 index 00000000..15d802e6 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomap.cpp @@ -0,0 +1,1822 @@ +/**************************************************************************** +** +** 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 "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 <QtPositioning/QGeoCircle> +#include <QtPositioning/QGeoRectangle> +#include <QtQuick/QQuickWindow> +#include <QtQuick/QSGRectangleNode> +#include <QtQuick/private/qquickwindow_p.h> +#include <QtQml/qqmlinfo.h> +#include <cmath> + +#ifndef M_PI +#define M_PI 3.141592653589793238463 +#endif + + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Map + \instantiates QDeclarativeGeoMap + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 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 objects programmatically, first be + sure they are created with the Map as their parent (for example in an argument to + Component::createObject), and then call the \l addMapItem method on the Map. + A corresponding \l removeMapItem method also exists to do the opposite and + remove an object 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 + details about 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 10. + + \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. +*/ + +static const qreal EARTH_MEAN_RADIUS = 6371007.2; + +QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) + : QQuickItem(parent), + m_plugin(0), + m_mappingManager(0), + m_activeMapType(0), + m_gestureArea(new QQuickGeoMapGestureArea(this)), + m_map(0), + m_error(QGeoServiceProvider::NoError), + m_color(QColor::fromRgbF(0.9, 0.9, 0.9)), + m_componentCompleted(false), + m_pendingFitViewport(false), + m_copyrightsVisible(true), + m_maximumViewportLatitude(0.0), + m_initialized(false) +{ + setAcceptHoverEvents(false); + setAcceptedMouseButtons(Qt::LeftButton); + setFlags(QQuickItem::ItemHasContents | QQuickItem::ItemClipsChildrenToShape); + setFiltersChildMouseEvents(true); + + connect(this, SIGNAL(childrenChanged()), this, SLOT(onMapChildrenChanged()), Qt::QueuedConnection); + + m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap, + tr("No Map"), + tr("No Map"), false, false, 0), this); + m_cameraData.setCenter(QGeoCoordinate(51.5073,-0.1277)); //London city center + m_cameraData.setZoomLevel(8.0); +} + +QDeclarativeGeoMap::~QDeclarativeGeoMap() +{ + if (!m_mapViews.isEmpty()) + qDeleteAll(m_mapViews); + // remove any map items associations + for (int i = 0; i < m_mapItems.count(); ++i) { + if (m_mapItems.at(i)) + m_mapItems.at(i).data()->setMap(0,0); + } + m_mapItems.clear(); + + delete m_copyrights.data(); + m_copyrights.clear(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::onMapChildrenChanged() +{ + if (!m_componentCompleted || !m_map) + return; + + int maxChildZ = 0; + QObjectList kids = children(); + bool foundCopyrights = false; + + for (int i = 0; i < kids.size(); ++i) { + QDeclarativeGeoMapCopyrightNotice *copyrights = qobject_cast<QDeclarativeGeoMapCopyrightNotice *>(kids.at(i)); + if (copyrights) { + foundCopyrights = true; + } else { + QDeclarativeGeoMapItemBase *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(kids.at(i)); + if (mapItem) { + if (mapItem->z() > maxChildZ) + maxChildZ = mapItem->z(); + } + } + } + + QDeclarativeGeoMapCopyrightNotice *copyrights = m_copyrights.data(); + // if copyrights object not found within the map's children + if (!foundCopyrights) { + // if copyrights object was deleted! + if (!copyrights) { + // create a new one and set its parent, re-assign it to the weak pointer, then connect the copyrights-change signal + m_copyrights = new QDeclarativeGeoMapCopyrightNotice(this); + copyrights = m_copyrights.data(); + connect(m_map, SIGNAL(copyrightsChanged(QImage)), + copyrights, SLOT(copyrightsChanged(QImage))); + connect(m_map, SIGNAL(copyrightsChanged(QString)), + copyrights, SLOT(copyrightsChanged(QString))); + connect(copyrights, SIGNAL(linkActivated(QString)), + this, SIGNAL(copyrightLinkActivated(QString))); + + // set visibility of copyright notice + copyrights->setCopyrightsVisible(m_copyrightsVisible); + + } else { + // just re-set its parent. + copyrights->setParent(this); + } + } + + // put the copyrights notice object at the highest z order + copyrights->setCopyrightsZ(maxChildZ + 1); +} + +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 Q_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; + foreach (QDeclarativeGeoMapType *declarativeType, 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(); +} + +void QDeclarativeGeoMap::initialize() +{ + // try to keep change signals in the end + bool centerHasChanged = false; + bool bearingHasChanged = false; + bool tiltHasChanged = false; + bool fovHasChanged = false; + bool minTiltHasChanged = false; + bool maxTiltHasChanged = false; + bool minFovHasChanged = false; + bool maxFovHasChanged = false; + + QGeoCoordinate center = m_cameraData.center(); + + setMinimumZoomLevel(m_map->minimumZoom()); + + double bearing = m_cameraData.bearing(); + double tilt = m_cameraData.tilt(); + double fov = m_cameraData.fieldOfView(); // Must be 45.0 + if (m_map->cameraCapabilities().minimumFieldOfView() != 1) + minFovHasChanged = true; + if (m_map->cameraCapabilities().maximumFieldOfView() != 179) + maxFovHasChanged = true; + if (m_map->cameraCapabilities().minimumTilt() != 0) + minTiltHasChanged = true; + if (m_map->cameraCapabilities().maximumTilt() != 89) + maxTiltHasChanged = true; + if (!m_map->cameraCapabilities().supportsBearing() && bearing != 0.0) { + m_cameraData.setBearing(0); + bearingHasChanged = true; + } + if (!m_map->cameraCapabilities().supportsTilting() && tilt != 0.0) { + m_cameraData.setTilt(0); + tiltHasChanged = true; + } + + m_cameraData.setFieldOfView(qBound(m_map->cameraCapabilities().minimumFieldOfView(), + fov, + m_map->cameraCapabilities().maximumFieldOfView())); + if (fov != m_cameraData.fieldOfView()) + fovHasChanged = true; + + // set latitude boundary check + m_maximumViewportLatitude = m_map->maximumCenterLatitudeAtZoom(m_cameraData); + + center.setLatitude(qBound(-m_maximumViewportLatitude, center.latitude(), m_maximumViewportLatitude)); + + if (center != m_cameraData.center()) { + centerHasChanged = true; + m_cameraData.setCenter(center); + } + + m_map->setCameraData(m_cameraData); + + m_initialized = true; + + if (centerHasChanged) + emit centerChanged(m_cameraData.center()); + + if (bearingHasChanged) + emit bearingChanged(m_cameraData.bearing()); + + if (tiltHasChanged) + emit tiltChanged(m_cameraData.tilt()); + + if (fovHasChanged) + emit fieldOfViewChanged(m_cameraData.fieldOfView()); + + if (minTiltHasChanged) + emit minimumTiltChanged(m_map->cameraCapabilities().minimumTilt()); + + if (maxTiltHasChanged) + emit maximumTiltChanged(m_map->cameraCapabilities().maximumTilt()); + + if (minFovHasChanged) + emit minimumFieldOfViewChanged(m_map->cameraCapabilities().minimumFieldOfView()); + + if (maxFovHasChanged) + emit maximumFieldOfViewChanged(m_map->cameraCapabilities().maximumFieldOfView()); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::pluginReady() +{ + QGeoServiceProvider *provider = m_plugin->sharedGeoServiceProvider(); + m_mappingManager = provider->mappingManager(); + + if (provider->error() != QGeoServiceProvider::NoError) { + setError(provider->error(), provider->errorString()); + 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, SIGNAL(initialized()), this, SLOT(mappingManagerInitialized())); + else + mappingManagerInitialized(); + + // make sure this is only called once + disconnect(this, SLOT(pluginReady())); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::componentComplete() +{ + m_componentCompleted = true; + populateParameters(); + populateMap(); + QQuickItem::componentComplete(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::mousePressEvent(QMouseEvent *event) +{ + if (isInteractive()) + m_gestureArea->handleMousePressEvent(event); + else + QQuickItem::mousePressEvent(event); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::mouseMoveEvent(QMouseEvent *event) +{ + if (isInteractive()) + m_gestureArea->handleMouseMoveEvent(event); + else + QQuickItem::mouseMoveEvent(event); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::mouseReleaseEvent(QMouseEvent *event) +{ + if (isInteractive()) { + m_gestureArea->handleMouseReleaseEvent(event); + } else { + QQuickItem::mouseReleaseEvent(event); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::mouseUngrabEvent() +{ + if (isInteractive()) + m_gestureArea->handleMouseUngrabEvent(); + else + QQuickItem::mouseUngrabEvent(); +} + +void QDeclarativeGeoMap::touchUngrabEvent() +{ + if (isInteractive()) + m_gestureArea->handleTouchUngrabEvent(); + else + QQuickItem::touchUngrabEvent(); +} + +/*! + \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 +*/ +void QDeclarativeGeoMap::populateMap() +{ + QObjectList kids = children(); + QList<QQuickItem *> quickKids = childItems(); + for (int i=0; i < quickKids.count(); ++i) + kids.append(quickKids.at(i)); + + for (int i = 0; i < kids.size(); ++i) { + // dispatch items appropriately + QDeclarativeGeoMapItemView *mapView = qobject_cast<QDeclarativeGeoMapItemView *>(kids.at(i)); + if (mapView) { + m_mapViews.append(mapView); + setupMapView(mapView); + continue; + } + QDeclarativeGeoMapItemBase *mapItem = qobject_cast<QDeclarativeGeoMapItemBase *>(kids.at(i)); + if (mapItem) { + addMapItem(mapItem); + } + } +} + +void QDeclarativeGeoMap::populateParameters() +{ + QObjectList kids = children(); + QList<QQuickItem *> quickKids = childItems(); + for (int i = 0; i < quickKids.count(); ++i) + kids.append(quickKids.at(i)); + for (int i = 0; i < kids.size(); ++i) { + QDeclarativeGeoMapParameter *mapParameter = qobject_cast<QDeclarativeGeoMapParameter *>(kids.at(i)); + if (mapParameter) + addMapParameter(mapParameter); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::setupMapView(QDeclarativeGeoMapItemView *view) +{ + Q_UNUSED(view) + view->setMap(this); + view->repopulate(); +} + +/*! + * \internal + */ +QSGNode *QDeclarativeGeoMap::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + if (!m_map) { + delete oldNode; + return 0; + } + + 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, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +/*! + \internal + this function will only be ever called once +*/ +void QDeclarativeGeoMap::mappingManagerInitialized() +{ + m_map = m_mappingManager->createMap(this); + + if (!m_map) + return; + + 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_supportedMapTypes.isEmpty()) { + QDeclarativeGeoMapType *type = m_supportedMapTypes.at(0); + m_activeMapType = type; + m_map->setActiveMapType(type->mapType()); + } else { + m_map->setActiveMapType(m_activeMapType->mapType()); + } + + //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_mappingManager->cameraCapabilities().maximumZoomLevelAt256() < m_gestureArea->maximumZoomLevel()) + setMaximumZoomLevel(m_mappingManager->cameraCapabilities().maximumZoomLevelAt256()); + + if (m_mappingManager->cameraCapabilities().minimumZoomLevelAt256() > m_gestureArea->minimumZoomLevel()) + setMinimumZoomLevel(m_mappingManager->cameraCapabilities().minimumZoomLevelAt256()); + + + // Map tiles are built in this call. m_map->minimumZoom() becomes operational + // after this has been called at least once, after creation. + + if (!m_initialized && width() > 0 && height() > 0) { + m_map->setViewportSize(QSize(width(), height())); + initialize(); + } + + m_copyrights = new QDeclarativeGeoMapCopyrightNotice(this); + connect(m_map, SIGNAL(copyrightsChanged(QImage)), + m_copyrights.data(), SLOT(copyrightsChanged(QImage))); + connect(m_map, SIGNAL(copyrightsChanged(QString)), + m_copyrights.data(), SLOT(copyrightsChanged(QString))); + connect(m_copyrights.data(), SIGNAL(linkActivated(QString)), + this, SIGNAL(copyrightLinkActivated(QString))); + connect(m_map, &QGeoMap::sgNodeChanged, this, &QQuickItem::update); + + // set visibility of copyright notice + m_copyrights->setCopyrightsVisible(m_copyrightsVisible); + + // This prefetches a buffer around the map + m_map->prefetchData(); + + connect(m_mappingManager, SIGNAL(supportedMapTypesChanged()), this, SLOT(onSupportedMapTypesChanged())); + emit minimumZoomLevelChanged(); + emit maximumZoomLevelChanged(); + emit supportedMapTypesChanged(); + emit activeMapTypeChanged(); + + // Any map items that were added before the plugin was ready + // need to have setMap called again + foreach (const QPointer<QDeclarativeGeoMapItemBase> &item, m_mapItems) { + if (item) + item.data()->setMap(this, m_map); + } + + // All map parameters that were added before the plugin was ready + // need to be added to m_map + for (QDeclarativeGeoMapParameter *p : m_mapParameters) + m_map->addParameter(p); +} + +/*! + \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) +{ + + if (minimumZoomLevel >= 0) { + qreal oldMinimumZoomLevel = this->minimumZoomLevel(); + + if (m_map) { + minimumZoomLevel = qBound(qreal(m_map->cameraCapabilities().minimumZoomLevelAt256()), minimumZoomLevel, maximumZoomLevel()); + double minimumViewportZoomLevel = m_map->minimumZoom(); + if (minimumZoomLevel < minimumViewportZoomLevel) + minimumZoomLevel = minimumViewportZoomLevel; + } + + m_gestureArea->setMinimumZoomLevel(minimumZoomLevel); + + if (zoomLevel() < minimumZoomLevel) + setZoomLevel(minimumZoomLevel); + + if (oldMinimumZoomLevel != minimumZoomLevel) + 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 a plugin supporting mapping is not set, 0.0 is returned. +*/ + +qreal QDeclarativeGeoMap::minimumZoomLevel() const +{ + return m_gestureArea->minimumZoomLevel(); +} + +/*! + \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) +{ + if (maximumZoomLevel >= 0) { + qreal oldMaximumZoomLevel = this->maximumZoomLevel(); + + if (m_map) + maximumZoomLevel = qBound(minimumZoomLevel(), maximumZoomLevel, qreal(m_map->cameraCapabilities().maximumZoomLevelAt256())); + + m_gestureArea->setMaximumZoomLevel(maximumZoomLevel); + + if (zoomLevel() > maximumZoomLevel) + 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 a plugin supporting mapping is not set, 30.0 is returned. +*/ + +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. +*/ +void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel) +{ + if (m_cameraData.zoomLevel() == zoomLevel || zoomLevel < 0) + return; + + //small optimization to avoid double setCameraData + bool centerHasChanged = false; + + if (m_initialized) { + m_cameraData.setZoomLevel(qBound(minimumZoomLevel(), zoomLevel, maximumZoomLevel())); + m_maximumViewportLatitude = m_map->maximumCenterLatitudeAtZoom(m_cameraData); + QGeoCoordinate coord = m_cameraData.center(); + coord.setLatitude(qBound(-m_maximumViewportLatitude, coord.latitude(), m_maximumViewportLatitude)); + if (coord != m_cameraData.center()) { + centerHasChanged = true; + m_cameraData.setCenter(coord); + } + m_map->setCameraData(m_cameraData); + } else { + m_cameraData.setZoomLevel(zoomLevel); + } + + if (centerHasChanged) + emit centerChanged(m_cameraData.center()); + emit zoomLevelChanged(m_cameraData.zoomLevel()); +} + +qreal QDeclarativeGeoMap::zoomLevel() const +{ + 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 Qt Location 5.9 +*/ +void QDeclarativeGeoMap::setBearing(qreal bearing) +{ + bearing = std::fmod(bearing, qreal(360.0)); + if (m_map && !m_map->cameraCapabilities().supportsBearing()) + bearing = 0.0; + if (m_cameraData.bearing() == bearing || bearing < 0.0) + return; + + m_cameraData.setBearing(bearing); + if (m_map) + m_map->setCameraData(m_cameraData); + emit bearingChanged(bearing); +} + +qreal QDeclarativeGeoMap::bearing() const +{ + return m_cameraData.bearing(); +} + +/*! + \qmlproperty real QtLocation::Map::tilt + + This property holds the tilt for the map. + The default value is 0. + If the Plugin used for the Map supports tilt, the valid range for this value is + [ plugin.minimumTilt, plugin.maximumTilt ]. + If the Plugin used for the Map does not support tilting, changing this property will have no effect. + + \since Qt Location 5.9 +*/ +void QDeclarativeGeoMap::setTilt(qreal tilt) +{ + tilt = qBound(minimumTilt(), tilt, maximumTilt()); + if (m_cameraData.tilt() == tilt) + return; + + m_cameraData.setTilt(tilt); + if (m_map) + m_map->setCameraData(m_cameraData); + emit tiltChanged(tilt); +} + +qreal QDeclarativeGeoMap::tilt() const +{ + return m_cameraData.tilt(); +} + +/*! + \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}. + + \since Qt Location 5.9 +*/ +void QDeclarativeGeoMap::setFieldOfView(qreal fieldOfView) +{ + fieldOfView = qBound(minimumFieldOfView(), fieldOfView, maximumFieldOfView()); + if (m_cameraData.fieldOfView() == fieldOfView) + return; + + m_cameraData.setFieldOfView(fieldOfView); + if (m_map) + m_map->setCameraData(m_cameraData); + emit fieldOfViewChanged(fieldOfView); +} + +qreal QDeclarativeGeoMap::fieldOfView() const +{ + return m_cameraData.fieldOfView(); +} + +/*! + \qmlproperty bool QtLocation::Map::minimumFieldOfView + This property holds the minimum field of view that the map supports. + If the plugin property of the map is not set, or the plugin does not support mapping, this property is 1. + + \since Qt Location 5.9 +*/ +qreal QDeclarativeGeoMap::minimumFieldOfView() const +{ + if (!m_map) + return 1; + return m_map->cameraCapabilities().minimumFieldOfView(); +} + +/*! + \qmlproperty bool QtLocation::Map::maximumFieldOfView + This property holds the maximum field of view that the map supports. + If the plugin property of the map is not set, or the plugin does not support mapping, this property is 179. + + \since Qt Location 5.9 +*/ +qreal QDeclarativeGeoMap::maximumFieldOfView() const +{ + if (!m_map) + return 179; + return m_map->cameraCapabilities().maximumFieldOfView(); +} + +/*! + \qmlproperty bool QtLocation::Map::bearingSupported + + This property indicates if the Map supports bearing. + If the plugin property of the map is not set, or the plugin does not support mapping, this property is false. + + \since Qt Location 5.9 +*/ +bool QDeclarativeGeoMap::isBearingSupported() const +{ + if (!m_map) + return false; + return m_map->cameraCapabilities().supportsBearing(); +} + +/*! + \qmlproperty bool QtLocation::Map::tiltingSupported + + This property indicates if the Map supports tilting. + If the plugin property of the map is not set, or the plugin does not support mapping, this property is false. + + \since Qt Location 5.9 +*/ +bool QDeclarativeGeoMap::isTiltingSupported() const +{ + if (!m_map) + return false; + return m_map->cameraCapabilities().supportsTilting(); +} + +/*! + \qmlproperty bool QtLocation::Map::minimumTilt + + This property holds the minimum tilt that the map supports. + If the plugin property of the map is not set, or the plugin does not support mapping, this property is 0. + + \since Qt Location 5.9 +*/ +qreal QDeclarativeGeoMap::minimumTilt() const +{ + if (!m_map || !m_map->cameraCapabilities().supportsTilting()) + return 0.0; + return m_map->cameraCapabilities().minimumTilt(); +} + +/*! + \qmlproperty bool QtLocation::Map::maximumTilt + + This property holds the maximum tilt that the map supports. + If the plugin property of the map is not set, this property is 89. + If the plugin is set and it does not support mapping, this property is 0. + + \since Qt Location 5.9 +*/ +qreal QDeclarativeGeoMap::maximumTilt() const +{ + if (!m_map) + return 89.0; + else if (!m_map->cameraCapabilities().supportsTilting()) + return 0.0; + return m_map->cameraCapabilities().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 ¢er) +{ + if (center == m_cameraData.center()) + return; + + if (!center.isValid()) + return; + + if (m_initialized) { + QGeoCoordinate coord(center); + coord.setLatitude(qBound(-m_maximumViewportLatitude, center.latitude(), m_maximumViewportLatitude)); + m_cameraData.setCenter(coord); + m_map->setCameraData(m_cameraData); + } else { + m_cameraData.setCenter(center); + } + + emit centerChanged(m_cameraData.center()); +} + +QGeoCoordinate QDeclarativeGeoMap::center() const +{ + 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. + + This property does not provide any 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; + return; + } + + if (!m_map || !width() || !height()) { + m_pendingFitViewport = true; + return; + } + + fitViewportToGeoShape(); +} + +QGeoShape QDeclarativeGeoMap::visibleRegion() const +{ + if (!m_map || !width() || !height()) + return m_visibleRegion; + + QGeoCoordinate tl = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(0, 0)); + QGeoCoordinate br = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(width(), height())); + + return QGeoRectangle(tl, br); +} + +/*! + \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; +} + +// TODO: offer the possibility to specify the margins. +void QDeclarativeGeoMap::fitViewportToGeoShape() +{ + const int margins = 10; + if (!m_map || !m_visibleRegion.isValid() || width() <= margins || height() <= margins) + return; + + QDoubleVector2D topLeftPoint = m_map->geoProjection().geoToMapProjection(m_visibleRegion.topLeft()); + QDoubleVector2D bottomRightPoint = m_map->geoProjection().geoToMapProjection(m_visibleRegion.bottomRight()); + if (bottomRightPoint.x() < topLeftPoint.x()) // crossing the dateline + bottomRightPoint.setX(bottomRightPoint.x() + 1.0); + + // find center of the bounding box + QDoubleVector2D center = (topLeftPoint + bottomRightPoint) * 0.5; + center.setX(center.x() > 1.0 ? center.x() - 1.0 : center.x()); + QGeoCoordinate centerCoordinate = m_map->geoProjection().mapProjectionToGeo(center); + + // position camera to the center of bounding box + setCenter(centerCoordinate); + + // if the shape is empty we just change center position, not zoom + double bboxWidth = (bottomRightPoint.x() - topLeftPoint.x()) * m_map->mapWidth(); + double bboxHeight = (bottomRightPoint.y() - topLeftPoint.y()) * m_map->mapHeight(); + + if (bboxHeight == 0.0 && bboxWidth == 0.0) + return; + + double zoomRatio = qMax(bboxWidth / (width() - margins), + bboxHeight / (height() - margins)); + zoomRatio = std::log(zoomRatio) / std::log(2.0); + double newZoom = qMax<double>(minimumZoomLevel(), zoomLevel() - zoomRatio); + setZoomLevel(newZoom); +} + + +/*! + \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 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() +{ + m_map->clearData(); +} + +/*! + \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; +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::touchEvent(QTouchEvent *event) +{ + if (isInteractive()) { + m_gestureArea->handleTouchEvent(event); + } else { + //ignore event so sythesized event is generated; + QQuickItem::touchEvent(event); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::wheelEvent(QWheelEvent *event) +{ + if (isInteractive()) + m_gestureArea->handleWheelEvent(event); + else + QQuickItem::wheelEvent(event); + +} + +bool QDeclarativeGeoMap::isInteractive() +{ + return (m_gestureArea->enabled() && m_gestureArea->acceptedGestures()) || m_gestureArea->isActive(); +} + +/*! + \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::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + return sendMouseEvent(static_cast<QMouseEvent *>(event)); + case QEvent::UngrabMouse: { + QQuickWindow *win = window(); + if (!win) break; + if (!win->mouseGrabberItem() || + (win->mouseGrabberItem() && + win->mouseGrabberItem() != this)) { + // child lost grab, we could even lost + // some events if grab already belongs for example + // in item in diffrent window , clear up states + mouseUngrabEvent(); + } + break; + } + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + case QEvent::TouchCancel: + if (static_cast<QTouchEvent *>(event)->touchPoints().count() >= 2) { + // 1 touch point = handle with MouseEvent (event is always synthesized) + // let the synthesized mouse event grab the mouse, + // note there is no mouse grabber at this point since + // touch event comes first (see Qt::AA_SynthesizeMouseForUnhandledTouchEvents) + return sendTouchEvent(static_cast<QTouchEvent *>(event)); + } + default: + break; + } + return QQuickItem::childMouseEventFilter(item, event); +} + +/*! + \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 (!item || item->quickMap()) + return; + item->setParentItem(this); + if (m_map) + item->setMap(this, m_map); + m_mapItems.append(item); + emit mapItemsChanged(); +} + +/*! + \qmlmethod void QtLocation::Map::addMapParameter(MapParameter parameter) + + Adds a MapParameter 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.insert(parameter); // parameter now owned by QDeclarativeGeoMap + if (m_map) + m_map->addParameter(parameter); +} + +/*! + \qmlmethod void QtLocation::Map::removeMapParameter(MapParameter parameter) + + Removes the given MapParameter 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.remove(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 : m_mapParameters) + ret << p; + return ret; +} + +/*! + \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; + foreach (const QPointer<QDeclarativeGeoMapItemBase> &ptr, m_mapItems) { + if (ptr) + ret << ptr.data(); + } + return ret; +} + +/*! + \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 (!ptr || !m_map) + return; + QPointer<QDeclarativeGeoMapItemBase> item(ptr); + if (!m_mapItems.contains(item)) + return; + item.data()->setParentItem(0); + item.data()->setMap(0, 0); + // these can be optimized for perf, as we already check the 'contains' above + m_mapItems.removeOne(item); + emit mapItemsChanged(); +} + +/*! + \qmlmethod void QtLocation::Map::clearMapItems() + + Removes all items from the map. + + \sa mapItems, addMapItem, removeMapItem +*/ +void QDeclarativeGeoMap::clearMapItems() +{ + if (m_mapItems.isEmpty()) + return; + for (int i = 0; i < m_mapItems.count(); ++i) { + if (m_mapItems.at(i)) { + m_mapItems.at(i).data()->setParentItem(0); + m_mapItems.at(i).data()->setMap(0, 0); + } + } + m_mapItems.clear(); + emit mapItemsChanged(); +} + +/*! + \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()) { + m_activeMapType = mapType; + if (m_map) + m_map->setActiveMapType(mapType->mapType()); + emit activeMapTypeChanged(); + } +} + +QDeclarativeGeoMapType * QDeclarativeGeoMap::activeMapType() const +{ + return m_activeMapType; +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + m_gestureArea->setSize(newGeometry.size()); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + if (!m_map || !newGeometry.size().isValid()) + return; + + m_map->setViewportSize(newGeometry.size().toSize()); + + if (!m_initialized) { + initialize(); + } else { + setMinimumZoomLevel(m_map->minimumZoom()); + + // Update the center latitudinal threshold + double maximumCenterLatitudeAtZoom = m_map->maximumCenterLatitudeAtZoom(m_cameraData); + if (maximumCenterLatitudeAtZoom != m_maximumViewportLatitude) { + m_maximumViewportLatitude = maximumCenterLatitudeAtZoom; + QGeoCoordinate coord = m_cameraData.center(); + coord.setLatitude(qBound(-m_maximumViewportLatitude, coord.latitude(), m_maximumViewportLatitude)); + + if (coord != m_cameraData.center()) { + m_cameraData.setCenter(coord); + m_map->setCameraData(m_cameraData); + emit centerChanged(m_cameraData.center()); + } + } + } + + /*! + 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_pendingFitViewport = false; + } + +} + +/*! + \qmlmethod void QtLocation::Map::fitViewportToMapItems() + + 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 + +*/ +void QDeclarativeGeoMap::fitViewportToMapItems() +{ + fitViewportToMapItemsRefine(true); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::fitViewportToMapItemsRefine(bool refine) +{ + if (!m_map) + return; + + if (m_mapItems.size() == 0) + return; + + double minX = 0; + double maxX = 0; + double minY = 0; + double maxY = 0; + double topLeftX = 0; + double topLeftY = 0; + double bottomRightX = 0; + double bottomRightY = 0; + bool haveQuickItem = false; + + // find bounds of all map items + int itemCount = 0; + for (int i = 0; i < m_mapItems.count(); ++i) { + if (!m_mapItems.at(i)) + continue; + QDeclarativeGeoMapItemBase *item = m_mapItems.at(i).data(); + if (!item) + continue; + + // skip quick items in the first pass and refine the fit later + if (refine) { + QDeclarativeGeoMapQuickItem *quickItem = + qobject_cast<QDeclarativeGeoMapQuickItem*>(item); + if (quickItem) { + haveQuickItem = true; + continue; + } + } + // Force map items to update immediately. Needed to ensure correct item size and positions + // when recursively calling this function. + if (item->isPolishScheduled()) + item->updatePolish(); + + topLeftX = item->position().x(); + topLeftY = item->position().y(); + bottomRightX = topLeftX + item->width(); + bottomRightY = topLeftY + item->height(); + + if (itemCount == 0) { + minX = topLeftX; + maxX = bottomRightX; + minY = topLeftY; + maxY = bottomRightY; + } else { + minX = qMin(minX, topLeftX); + maxX = qMax(maxX, bottomRightX); + minY = qMin(minY, topLeftY); + maxY = qMax(maxY, bottomRightY); + } + ++itemCount; + } + + if (itemCount == 0) { + if (haveQuickItem) + fitViewportToMapItemsRefine(false); + 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(false); +} + +bool QDeclarativeGeoMap::sendMouseEvent(QMouseEvent *event) +{ + QPointF localPos = mapFromScene(event->windowPos()); + QQuickWindow *win = window(); + QQuickItem *grabber = win ? win->mouseGrabberItem() : 0; + bool stealEvent = m_gestureArea->isActive(); + + if ((stealEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab())) { + QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos)); + mouseEvent->setAccepted(false); + + switch (mouseEvent->type()) { + case QEvent::MouseMove: + m_gestureArea->handleMouseMoveEvent(mouseEvent.data()); + break; + case QEvent::MouseButtonPress: + m_gestureArea->handleMousePressEvent(mouseEvent.data()); + break; + case QEvent::MouseButtonRelease: + m_gestureArea->handleMouseReleaseEvent(mouseEvent.data()); + break; + default: + break; + } + + stealEvent = m_gestureArea->isActive(); + grabber = win ? win->mouseGrabberItem() : 0; + + if (grabber && stealEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + if (stealEvent) { + //do not deliver + event->setAccepted(true); + return true; + } else { + return false; + } + } + + return false; +} + +bool QDeclarativeGeoMap::sendTouchEvent(QTouchEvent *event) +{ + const QQuickPointerDevice *touchDevice = QQuickPointerDevice::touchDevice(event->device()); + const QTouchEvent::TouchPoint &point = event->touchPoints().first(); + + auto touchPointGrabberItem = [touchDevice](const QTouchEvent::TouchPoint &point) -> QQuickItem* { + if (QQuickEventPoint *eventPointer = touchDevice->pointerEvent()->pointById(point.id())) + return eventPointer->grabber(); + return nullptr; + }; + + QQuickItem *grabber = touchPointGrabberItem(point); + + bool stealEvent = m_gestureArea->isActive(); + bool containsPoint = contains(mapFromScene(point.scenePos())); + + if ((stealEvent || containsPoint) && (!grabber || !grabber->keepTouchGrab())) { + QScopedPointer<QTouchEvent> touchEvent(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); + touchEvent->setTimestamp(event->timestamp()); + touchEvent->setAccepted(false); + + m_gestureArea->handleTouchEvent(touchEvent.data()); + stealEvent = m_gestureArea->isActive(); + grabber = touchPointGrabberItem(point); + + if (grabber && stealEvent && !grabber->keepTouchGrab() && grabber != this) { + QVector<int> ids; + foreach (const QTouchEvent::TouchPoint &tp, event->touchPoints()) { + if (!(tp.state() & Qt::TouchPointReleased)) { + ids.append(tp.id()); + } + } + grabTouchPoints(ids); + } + + if (stealEvent) { + //do not deliver + event->setAccepted(true); + return true; + } else { + return false; + } + } + + return false; +} + +#include "moc_qdeclarativegeomap_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomap_p.h b/src/location/declarativemaps/qdeclarativegeomap_p.h new file mode 100644 index 00000000..ca2ec0a9 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomap_p.h @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAP_H +#define QDECLARATIVEGEOMAP_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeomapitemview_p.h> +#include <QtLocation/private/qquickgeomapgesturearea_p.h> + +#include <QtLocation/qgeoserviceprovider.h> +#include <QtLocation/private/qgeocameradata_p.h> +#include <QtQuick/QQuickItem> +#include <QtCore/QPointer> +#include <QtCore/QSet> +#include <QtGui/QColor> +#include <QtPositioning/qgeoshape.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoServiceProvider; +class QDeclarativeGeoMapType; +class QDeclarativeGeoMapCopyrightNotice; +class QDeclarativeGeoMapParameter; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMap : public QQuickItem +{ + Q_OBJECT + Q_ENUMS(QGeoServiceProvider::Error) + Q_PROPERTY(QQuickGeoMapGestureArea *gesture READ gesture CONSTANT) + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(qreal minimumZoomLevel READ minimumZoomLevel WRITE setMinimumZoomLevel NOTIFY minimumZoomLevelChanged) + Q_PROPERTY(qreal maximumZoomLevel READ maximumZoomLevel WRITE setMaximumZoomLevel NOTIFY maximumZoomLevelChanged) + Q_PROPERTY(qreal zoomLevel READ zoomLevel WRITE setZoomLevel NOTIFY zoomLevelChanged) + + Q_PROPERTY(bool bearingSupported READ isBearingSupported NOTIFY bearingSupportChanged) + Q_PROPERTY(bool tiltingSupported READ isTiltingSupported NOTIFY tiltingSupportChanged) + Q_PROPERTY(qreal minimumTilt READ minimumTilt NOTIFY minimumTiltChanged) + Q_PROPERTY(qreal maximumTilt READ maximumTilt NOTIFY maximumTiltChanged) + Q_PROPERTY(qreal bearing READ bearing WRITE setBearing NOTIFY bearingChanged) + Q_PROPERTY(qreal tilt READ tilt WRITE setTilt NOTIFY tiltChanged) + Q_PROPERTY(qreal fieldOfView READ fieldOfView WRITE setFieldOfView NOTIFY fieldOfViewChanged) + Q_PROPERTY(qreal minimumFieldOfView READ minimumFieldOfView NOTIFY minimumFieldOfViewChanged) + Q_PROPERTY(qreal maximumFieldOfView READ maximumFieldOfView NOTIFY minimumFieldOfViewChanged) + + Q_PROPERTY(QDeclarativeGeoMapType *activeMapType READ activeMapType WRITE setActiveMapType NOTIFY activeMapTypeChanged) + Q_PROPERTY(QQmlListProperty<QDeclarativeGeoMapType> supportedMapTypes READ supportedMapTypes NOTIFY supportedMapTypesChanged) + Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged) + Q_PROPERTY(QList<QObject *> mapItems READ mapItems NOTIFY mapItemsChanged) + Q_PROPERTY(QList<QObject *> mapParameters READ mapParameters) + Q_PROPERTY(QGeoServiceProvider::Error error READ error NOTIFY errorChanged) + Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) + Q_PROPERTY(QGeoShape visibleRegion READ visibleRegion WRITE setVisibleRegion) + Q_PROPERTY(bool copyrightsVisible READ copyrightsVisible WRITE setCopyrightsVisible NOTIFY copyrightsVisibleChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_INTERFACES(QQmlParserStatus) + +public: + + explicit QDeclarativeGeoMap(QQuickItem *parent = 0); + ~QDeclarativeGeoMap(); + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + void setActiveMapType(QDeclarativeGeoMapType *mapType); + QDeclarativeGeoMapType *activeMapType() const; + + void setMinimumZoomLevel(qreal minimumZoomLevel); + qreal minimumZoomLevel() const; + + void setMaximumZoomLevel(qreal maximumZoomLevel); + qreal maximumZoomLevel() const; + + void setZoomLevel(qreal zoomLevel); + qreal zoomLevel() const; + + void setBearing(qreal bearing); + qreal bearing() const; + + void setTilt(qreal tilt); + qreal tilt() const; + + void setFieldOfView(qreal fieldOfView); + qreal fieldOfView() const; + qreal minimumFieldOfView() const; + qreal maximumFieldOfView() const; + + bool isBearingSupported() const; + bool isTiltingSupported() const; + qreal minimumTilt() const; + qreal maximumTilt() const; + + void setCenter(const QGeoCoordinate ¢er); + QGeoCoordinate center() const; + + void setVisibleRegion(const QGeoShape &shape); + QGeoShape visibleRegion() const; + + void setCopyrightsVisible(bool visible); + bool copyrightsVisible() const; + + void setColor(const QColor &color); + QColor color() const; + + QQmlListProperty<QDeclarativeGeoMapType> supportedMapTypes(); + + Q_INVOKABLE void removeMapItem(QDeclarativeGeoMapItemBase *item); + Q_INVOKABLE void addMapItem(QDeclarativeGeoMapItemBase *item); + + Q_INVOKABLE void clearMapItems(); + QList<QObject *> mapItems(); + + Q_INVOKABLE void addMapParameter(QDeclarativeGeoMapParameter *parameter); + Q_INVOKABLE void removeMapParameter(QDeclarativeGeoMapParameter *parameter); + Q_INVOKABLE void clearMapParameters(); + QList<QObject *> mapParameters(); + + Q_INVOKABLE QGeoCoordinate toCoordinate(const QPointF &position, bool clipToViewPort = true) const; + Q_INVOKABLE QPointF fromCoordinate(const QGeoCoordinate &coordinate, bool clipToViewPort = true) const; + + QQuickGeoMapGestureArea *gesture(); + + Q_INVOKABLE void fitViewportToMapItems(); + Q_INVOKABLE void pan(int dx, int dy); + Q_INVOKABLE void prefetchData(); // optional hint for prefetch + Q_INVOKABLE void clearData(); + + QString errorString() const; + QGeoServiceProvider::Error error() const; + +Q_SIGNALS: + void pluginChanged(QDeclarativeGeoServiceProvider *plugin); + void zoomLevelChanged(qreal zoomLevel); + void centerChanged(const QGeoCoordinate &coordinate); + void activeMapTypeChanged(); + void supportedMapTypesChanged(); + void minimumZoomLevelChanged(); + void maximumZoomLevelChanged(); + void mapItemsChanged(); + void errorChanged(); + void copyrightLinkActivated(const QString &link); + void copyrightsVisibleChanged(bool visible); + void colorChanged(const QColor &color); + void bearingChanged(qreal bearing); + void tiltChanged(qreal tilt); + void fieldOfViewChanged(qreal fieldOfView); + void bearingSupportChanged(bool bearingSupport); + void tiltingSupportChanged(bool tiltingSupport); + void minimumTiltChanged(qreal minimumTilt); + void maximumTiltChanged(qreal maximumTilt); + void minimumFieldOfViewChanged(qreal minimumFieldOfView); + void maximumFieldOfViewChanged(qreal maximumFieldOfView); + +protected: + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE ; + void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE ; + void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE ; + void mouseUngrabEvent() Q_DECL_OVERRIDE ; + void touchUngrabEvent() Q_DECL_OVERRIDE; + void touchEvent(QTouchEvent *event) Q_DECL_OVERRIDE ; + void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE ; + + bool childMouseEventFilter(QQuickItem *item, QEvent *event) Q_DECL_OVERRIDE; + bool sendMouseEvent(QMouseEvent *event); + bool sendTouchEvent(QTouchEvent *event); + + void componentComplete() Q_DECL_OVERRIDE; + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + + void setError(QGeoServiceProvider::Error error, const QString &errorString); + void initialize(); +private Q_SLOTS: + void mappingManagerInitialized(); + void pluginReady(); + void onMapChildrenChanged(); + void onSupportedMapTypesChanged(); + +private: + void setupMapView(QDeclarativeGeoMapItemView *view); + void populateMap(); + void populateParameters(); + void fitViewportToMapItemsRefine(bool refine); + void fitViewportToGeoShape(); + bool isInteractive(); + +private: + QDeclarativeGeoServiceProvider *m_plugin; + QGeoMappingManager *m_mappingManager; + QDeclarativeGeoMapType *m_activeMapType; + QList<QDeclarativeGeoMapType *> m_supportedMapTypes; + QList<QDeclarativeGeoMapItemView *> m_mapViews; + QQuickGeoMapGestureArea *m_gestureArea; + QGeoMap *m_map; + QPointer<QDeclarativeGeoMapCopyrightNotice> m_copyrights; + QList<QPointer<QDeclarativeGeoMapItemBase> > m_mapItems; + QString m_errorString; + QGeoServiceProvider::Error m_error; + QGeoRectangle m_visibleRegion; + QColor m_color; + QGeoCameraData m_cameraData; + bool m_componentCompleted; + bool m_pendingFitViewport; + bool m_copyrightsVisible; + double m_maximumViewportLatitude; + bool m_initialized; + QSet<QDeclarativeGeoMapParameter *> m_mapParameters; + + friend class QDeclarativeGeoMapItem; + friend class QDeclarativeGeoMapItemView; + friend class QQuickGeoMapGestureArea; + Q_DISABLE_COPY(QDeclarativeGeoMap) +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMap) + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp new file mode 100644 index 00000000..7b5a5765 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Aaron McCarthy <mccarthy.aaron@gmail.com> +** 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 "qdeclarativegeomapcopyrightsnotice_p.h" + +#include <QtGui/QTextDocument> +#include <QtGui/QAbstractTextDocumentLayout> +#include <QtGui/QPainter> +#include <QtQuick/private/qquickanchors_p.h> +#include <QtQuick/private/qquickanchors_p_p.h> + +QT_BEGIN_NAMESPACE + +QDeclarativeGeoMapCopyrightNotice::QDeclarativeGeoMapCopyrightNotice(QQuickItem *parent) +: QQuickPaintedItem(parent), m_copyrightsHtml(0), m_copyrightsVisible(true) +{ + QQuickAnchors *anchors = property("anchors").value<QQuickAnchors *>(); + if (anchors) { + anchors->setLeft(QQuickAnchorLine(parent, QQuickAnchors::LeftAnchor)); + anchors->setBottom(QQuickAnchorLine(parent, QQuickAnchors::BottomAnchor)); + } +} + +QDeclarativeGeoMapCopyrightNotice::~QDeclarativeGeoMapCopyrightNotice() +{ +} + +/*! + \internal +*/ +void QDeclarativeGeoMapCopyrightNotice::paint(QPainter *painter) +{ + painter->drawImage(0, 0, m_copyrightsImage); +} + +void QDeclarativeGeoMapCopyrightNotice::mousePressEvent(QMouseEvent *event) +{ + if (m_copyrightsHtml) { + m_activeAnchor = m_copyrightsHtml->documentLayout()->anchorAt(event->pos()); + if (!m_activeAnchor.isEmpty()) + return; + } + + QQuickPaintedItem::mousePressEvent(event); +} + +void QDeclarativeGeoMapCopyrightNotice::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_copyrightsHtml) { + QString anchor = m_copyrightsHtml->documentLayout()->anchorAt(event->pos()); + if (anchor == m_activeAnchor && !anchor.isEmpty()) { + emit linkActivated(anchor); + m_activeAnchor.clear(); + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapCopyrightNotice::setCopyrightsVisible(bool visible) +{ + m_copyrightsVisible = visible; + + setVisible(!m_copyrightsImage.isNull() && visible); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapCopyrightNotice::setCopyrightsZ(int copyrightsZ) +{ + setZ(copyrightsZ); + update(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QImage ©rightsImage) +{ + delete m_copyrightsHtml; + m_copyrightsHtml = 0; + + m_copyrightsImage = copyrightsImage; + + setWidth(m_copyrightsImage.width()); + setHeight(m_copyrightsImage.height()); + + setKeepMouseGrab(false); + setAcceptedMouseButtons(Qt::NoButton); + setVisible(m_copyrightsVisible); + + update(); +} + +void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QString ©rightsHtml) +{ + if (copyrightsHtml.isEmpty() || !m_copyrightsVisible) { + m_copyrightsImage = QImage(); + setVisible(false); + return; + } else { + setVisible(true); + } + + if (!m_copyrightsHtml) + m_copyrightsHtml = new QTextDocument(this); + + m_copyrightsHtml->setHtml(copyrightsHtml); + + m_copyrightsImage = QImage(m_copyrightsHtml->size().toSize(), + QImage::Format_ARGB32_Premultiplied); + m_copyrightsImage.fill(qPremultiply(qRgba(255, 255, 255, 128))); + + QPainter painter(&m_copyrightsImage); + //m_copyrightsHtml->drawContents(&painter); // <- this uses the default application palette, that might have, f.ex., white text + QAbstractTextDocumentLayout::PaintContext ctx; + ctx.palette.setColor(QPalette::Text, QColor(QStringLiteral("black"))); + ctx.palette.setColor(QPalette::Link, QColor(QStringLiteral("blue"))); + m_copyrightsHtml->documentLayout()->draw(&painter, ctx); + + setWidth(m_copyrightsImage.width()); + setHeight(m_copyrightsImage.height()); + + setContentsSize(m_copyrightsImage.size()); + + setKeepMouseGrab(true); + setAcceptedMouseButtons(Qt::LeftButton); + + update(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h new file mode 100644 index 00000000..1aeef7c1 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Aaron McCarthy <mccarthy.aaron@gmail.com> +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPCOPYRIGHTSNOTICE_H +#define QDECLARATIVEGEOMAPCOPYRIGHTSNOTICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> + +#include <QtGui/QImage> +#include <QtQuick/QQuickPaintedItem> + +QT_BEGIN_NAMESPACE + +class QTextDocument; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapCopyrightNotice : public QQuickPaintedItem +{ + Q_OBJECT + +public: + explicit QDeclarativeGeoMapCopyrightNotice(QQuickItem *parent); + ~QDeclarativeGeoMapCopyrightNotice(); + + void setCopyrightsZ(int copyrightsZ); + + void setCopyrightsVisible(bool visible); + +public Q_SLOTS: + void copyrightsChanged(const QImage ©rightsImage); + void copyrightsChanged(const QString ©rightsHtml); + +signals: + void linkActivated(const QString &link); + +protected: + void paint(QPainter *painter) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + +private: + QTextDocument *m_copyrightsHtml; + QImage m_copyrightsImage; + QString m_activeAnchor; + bool m_copyrightsVisible; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp new file mode 100644 index 00000000..93d07386 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** 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 "qdeclarativegeomapitembase_p.h" +#include "qgeocameradata_p.h" +#include <QtLocation/private/qgeomap_p.h> +#include <QtQml/QQmlInfo> +#include <QtQuick/QSGOpacityNode> +#include <QtQuick/private/qquickmousearea_p.h> +#include <QtQuick/private/qquickitem_p.h> + +QT_BEGIN_NAMESPACE + +QGeoMapViewportChangeEvent::QGeoMapViewportChangeEvent() + : zoomLevelChanged(false), + centerChanged(false), + mapSizeChanged(false), + tiltChanged(false), + bearingChanged(false), + rollChanged(false) +{ +} + +QGeoMapViewportChangeEvent::QGeoMapViewportChangeEvent(const QGeoMapViewportChangeEvent &other) +{ + this->operator=(other); +} + +QGeoMapViewportChangeEvent &QGeoMapViewportChangeEvent::operator=(const QGeoMapViewportChangeEvent &other) +{ + if (this == &other) + return (*this); + + cameraData = other.cameraData; + mapSize = other.mapSize; + zoomLevelChanged = other.zoomLevelChanged; + centerChanged = other.centerChanged; + mapSizeChanged = other.mapSizeChanged; + tiltChanged = other.tiltChanged; + bearingChanged = other.bearingChanged; + rollChanged = other.rollChanged; + + return (*this); +} + +QDeclarativeGeoMapItemBase::QDeclarativeGeoMapItemBase(QQuickItem *parent) +: QQuickItem(parent), map_(0), quickMap_(0) +{ + setFiltersChildMouseEvents(true); + connect(this, SIGNAL(childrenChanged()), + this, SLOT(afterChildrenChanged())); +} + +QDeclarativeGeoMapItemBase::~QDeclarativeGeoMapItemBase() +{ + disconnect(this, SLOT(afterChildrenChanged())); + if (quickMap_) + quickMap_->removeMapItem(this); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemBase::afterChildrenChanged() +{ + QList<QQuickItem *> kids = childItems(); + if (kids.size() > 0) { + bool printedWarning = false; + foreach (QQuickItem *i, kids) { + if (i->flags() & QQuickItem::ItemHasContents + && !qobject_cast<QQuickMouseArea *>(i)) { + if (!printedWarning) { + qmlWarning(this) << "Geographic map items do not support child items"; + printedWarning = true; + } + + qmlWarning(i) << "deleting this child"; + i->deleteLater(); + } + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemBase::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + if (quickMap == quickMap_) + return; + if (quickMap && quickMap_) + return; // don't allow association to more than one map + if (quickMap_) + quickMap_->disconnect(this); + if (map_) + map_->disconnect(this); + + quickMap_ = quickMap; + map_ = map; + + if (map_ && quickMap_) { + connect(map_, SIGNAL(cameraDataChanged(QGeoCameraData)), + this, SLOT(baseCameraDataChanged(QGeoCameraData))); + connect(quickMap, SIGNAL(heightChanged()), this, SLOT(polishAndUpdate())); + connect(quickMap, SIGNAL(widthChanged()), this, SLOT(polishAndUpdate())); + lastSize_ = QSizeF(quickMap_->width(), quickMap_->height()); + lastCameraData_ = map_->cameraData(); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemBase::baseCameraDataChanged(const QGeoCameraData &cameraData) +{ + QGeoMapViewportChangeEvent evt; + evt.cameraData = cameraData; + evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height()); + + if (evt.mapSize != lastSize_) + evt.mapSizeChanged = true; + + if (cameraData.bearing() != lastCameraData_.bearing()) + evt.bearingChanged = true; + if (cameraData.center() != lastCameraData_.center()) + evt.centerChanged = true; + if (cameraData.roll() != lastCameraData_.roll()) + evt.rollChanged = true; + if (cameraData.tilt() != lastCameraData_.tilt()) + evt.tiltChanged = true; + if (cameraData.zoomLevel() != lastCameraData_.zoomLevel()) + evt.zoomLevelChanged = true; + + lastSize_ = evt.mapSize; + lastCameraData_ = cameraData; + + afterViewportChanged(evt); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemBase::setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset) +{ + if (!map_ || !quickMap_) + return; + + QDoubleVector2D wrappedProjection = map_->geoProjection().geoToWrappedMapProjection(coordinate); + if (! map_->geoProjection().isProjectable(wrappedProjection)) + return; + + QDoubleVector2D pos = map_->geoProjection().wrappedMapProjectionToItemPosition(wrappedProjection); + QPointF topLeft = pos.toPointF() - offset; + + setPosition(topLeft); +} + +static const double opacityRampMin = 1.5; +static const double opacityRampMax = 2.5; +/*! + \internal +*/ +float QDeclarativeGeoMapItemBase::zoomLevelOpacity() const +{ + if (quickMap_->zoomLevel() > opacityRampMax) + return 1.0; + else if (quickMap_->zoomLevel() > opacityRampMin) + return quickMap_->zoomLevel() - opacityRampMin; + else + return 0.0; +} + +bool QDeclarativeGeoMapItemBase::childMouseEventFilter(QQuickItem *item, QEvent *event) +{ + Q_UNUSED(item) + if (event->type() == QEvent::MouseButtonPress && !contains(static_cast<QMouseEvent*>(event)->pos())) { + // This is an evil hack: in case of items that are not rectangles, we never accept the event. + // Instead the events are now delivered to QDeclarativeGeoMapItemBase which doesn't to anything with them. + // The map below it still works since it filters events and steals the events at some point. + event->setAccepted(false); + return true; + } + return false; +} + +/*! + \internal +*/ +QSGNode *QDeclarativeGeoMapItemBase::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *pd) +{ + if (!map_ || !quickMap_) { + delete oldNode; + return 0; + } + + QSGOpacityNode *opn = static_cast<QSGOpacityNode *>(oldNode); + if (!opn) + opn = new QSGOpacityNode(); + + opn->setOpacity(zoomLevelOpacity()); + + QSGNode *oldN = opn->childCount() ? opn->firstChild() : 0; + opn->removeAllChildNodes(); + if (opn->opacity() > 0.0) { + QSGNode *n = this->updateMapItemPaintNode(oldN, pd); + if (n) + opn->appendChildNode(n); + } else { + delete oldN; + } + + return opn; +} + +/*! + \internal +*/ +QSGNode *QDeclarativeGeoMapItemBase::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + delete oldNode; + return 0; +} + +bool QDeclarativeGeoMapItemBase::isPolishScheduled() const +{ + return QQuickItemPrivate::get(this)->polishScheduled; +} + +void QDeclarativeGeoMapItemBase::polishAndUpdate() +{ + polish(); + update(); +} + + +#include "moc_qdeclarativegeomapitembase_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase_p.h b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h new file mode 100644 index 00000000..82a24233 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPITEMBASE_H +#define QDECLARATIVEGEOMAPITEMBASE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> + +#include <QtQuick/QQuickItem> +#include <QtPositioning/QGeoShape> + +#include <QtLocation/private/qdeclarativegeomap_p.h> +#include <QtLocation/private/qlocationglobal_p.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapViewportChangeEvent +{ +public: + explicit QGeoMapViewportChangeEvent(); + QGeoMapViewportChangeEvent(const QGeoMapViewportChangeEvent &other); + QGeoMapViewportChangeEvent &operator=(const QGeoMapViewportChangeEvent &other); + + QGeoCameraData cameraData; + QSizeF mapSize; + + bool zoomLevelChanged; + bool centerChanged; + bool mapSizeChanged; + bool tiltChanged; + bool bearingChanged; + bool rollChanged; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemBase : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(QGeoShape geoShape READ geoShape STORED false ) +public: + explicit QDeclarativeGeoMapItemBase(QQuickItem *parent = 0); + virtual ~QDeclarativeGeoMapItemBase(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map); + virtual void setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset); + + QDeclarativeGeoMap *quickMap() { return quickMap_; } + QGeoMap *map() { return map_; } + virtual const QGeoShape &geoShape() const = 0; + + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *); + +protected Q_SLOTS: + virtual void afterChildrenChanged(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) = 0; + void polishAndUpdate(); + +protected: + float zoomLevelOpacity() const; + bool childMouseEventFilter(QQuickItem *item, QEvent *event); + bool isPolishScheduled() const; + +private Q_SLOTS: + void baseCameraDataChanged(const QGeoCameraData &camera); + +private: + QGeoMap *map_; + QDeclarativeGeoMap *quickMap_; + + QSizeF lastSize_; + QGeoCameraData lastCameraData_; + + friend class QDeclarativeGeoMap; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomapitemview.cpp b/src/location/declarativemaps/qdeclarativegeomapitemview.cpp new file mode 100644 index 00000000..cb1a4348 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitemview.cpp @@ -0,0 +1,541 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jolla Ltd. +** Contact: Aaron McCarthy <aaron.mccarthy@jollamobile.com> +** 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 "qdeclarativegeomapitemview_p.h" +#include "qdeclarativegeomapitemview_p_p.h" +#include "qdeclarativegeomap_p.h" +#include "qdeclarativegeomapitembase_p.h" +#include "mapitemviewdelegateincubator_p.h" + +#include <QtCore/QAbstractItemModel> +#include <QtQml/QQmlContext> +#include <QtQml/QQmlIncubator> +#include <QtQml/private/qqmlopenmetaobject_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapItemView + \instantiates QDeclarativeGeoMapItemView + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.5 + \inherits QObject + + \brief The MapItemView is used to populate Map from a model. + + The MapItemView is used to populate Map with MapItems from a model. + The MapItemView type only makes sense when contained in a Map, + meaning that it has no standalone presentation. + + \section2 Example Usage + + This example demonstrates how to use the MapViewItem object to display + a \l{Route}{route} on a \l{Map}{map}: + + \snippet declarative/maps.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/maps.qml MapRoute +*/ + +QDeclarativeGeoMapItemView::QDeclarativeGeoMapItemView(QQuickItem *parent) + : QObject(parent), componentCompleted_(false), delegate_(0), + itemModel_(0), map_(0), fitViewport_(false), m_metaObjectType(0), + m_readyIncubators(0), m_repopulating(false) +{ +} + +QDeclarativeGeoMapItemView::~QDeclarativeGeoMapItemView() +{ + removeInstantiatedItems(); + if (m_metaObjectType) + m_metaObjectType->release(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::componentComplete() +{ + componentCompleted_ = true; +} + +void QDeclarativeGeoMapItemView::incubatorStatusChanged(MapItemViewDelegateIncubator *incubator, + QQmlIncubator::Status status, + bool batched) +{ + if (status == QQmlIncubator::Loading) + return; + + QDeclarativeGeoMapItemViewItemData *itemData = incubator->m_itemData; + if (!itemData) { + // Should never get here + qWarning() << "MapItemViewDelegateIncubator incubating invalid itemData"; + return; + } + + switch (status) { + case QQmlIncubator::Ready: + { + QDeclarativeGeoMapItemBase *item = qobject_cast<QDeclarativeGeoMapItemBase *>(incubator->object()); + if (!item) + break; + itemData->item = item; + if (!itemData->item) { + qWarning() << "QDeclarativeGeoMapItemView map item delegate is of unsupported type."; + delete incubator->object(); + } else { + if (!batched) { + map_->addMapItem(itemData->item); + fitViewport(); + } else { + ++m_readyIncubators; // QSemaphore not needed as multiple threads not involved + + if (m_readyIncubators == m_itemDataBatched.size()) { + + // Clearing stuff older than the reset + foreach (QDeclarativeGeoMapItemViewItemData *i, m_itemData) + removeItemData(i); + m_itemData.clear(); + + // Adding everthing created after reset was issued + foreach (QDeclarativeGeoMapItemViewItemData *i, m_itemDataBatched) { + map_->addMapItem(i->item); + } + m_itemData = m_itemDataBatched; + m_itemDataBatched.clear(); + + m_readyIncubators = 0; + m_repopulating = false; + + fitViewport(); + } + } + } + delete itemData->incubator; + itemData->incubator = 0; + break; + } + case QQmlIncubator::Null: + // Should never get here + delete itemData->incubator; + itemData->incubator = 0; + break; + case QQmlIncubator::Error: + qWarning() << "QDeclarativeGeoMapItemView map item creation failed."; + delete itemData->incubator; + itemData->incubator = 0; + break; + default: + ; + } +} + +/*! + \qmlproperty model QtLocation::MapItemView::model + + This property holds the model that provides data used for creating the map items defined by the + delegate. Only QAbstractItemModel based models are supported. +*/ +QVariant QDeclarativeGeoMapItemView::model() const +{ + return QVariant::fromValue(itemModel_); +} + +void QDeclarativeGeoMapItemView::setModel(const QVariant &model) +{ + QAbstractItemModel *itemModel = model.value<QAbstractItemModel *>(); + if (itemModel == itemModel_) + return; + + if (itemModel_) { + disconnect(itemModel_, SIGNAL(modelReset()), this, SLOT(itemModelReset())); + disconnect(itemModel_, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(itemModelRowsRemoved(QModelIndex,int,int))); + disconnect(itemModel_, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(itemModelRowsInserted(QModelIndex,int,int))); + disconnect(itemModel_, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(itemModelRowsMoved(QModelIndex,int,int,QModelIndex,int))); + disconnect(itemModel_, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), + this, SLOT(itemModelDataChanged(QModelIndex,QModelIndex,QVector<int>))); + + removeInstantiatedItems(); // this also terminates ongong repopulations. + m_metaObjectType->release(); + m_metaObjectType = 0; + + itemModel_ = 0; + } + + if (itemModel) { + itemModel_ = itemModel; + connect(itemModel_, SIGNAL(modelReset()), this, SLOT(itemModelReset())); + connect(itemModel_, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(itemModelRowsRemoved(QModelIndex,int,int))); + connect(itemModel_, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(itemModelRowsInserted(QModelIndex,int,int))); + connect(itemModel_, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + this, SLOT(itemModelRowsMoved(QModelIndex,int,int,QModelIndex,int))); + connect(itemModel_, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), + this, SLOT(itemModelDataChanged(QModelIndex,QModelIndex,QVector<int>))); + + m_metaObjectType = new QQmlOpenMetaObjectType(&QObject::staticMetaObject, 0); + foreach (const QByteArray &name, itemModel_->roleNames()) + m_metaObjectType->createProperty(name); + + instantiateAllItems(); + } + + emit modelChanged(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::itemModelReset() +{ + repopulate(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::itemModelRowsInserted(const QModelIndex &index, int start, int end) +{ + Q_UNUSED(index) + + if (!componentCompleted_ || !map_ || !delegate_ || !itemModel_) + return; + + for (int i = start; i <= end; ++i) { + const QModelIndex insertedIndex = itemModel_->index(i, 0, index); + // If ran inside a qquickwidget which forces incubators to be synchronous, this call won't happen + // with m_repopulating == true while incubators from a model reset are still incubating. + // Note that having the model in a different thread is not supported in general. + createItemForIndex(insertedIndex, m_repopulating); + } + + fitViewport(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::itemModelRowsRemoved(const QModelIndex &index, int start, int end) +{ + Q_UNUSED(index) + + if (!componentCompleted_ || !map_ || !delegate_ || !itemModel_) + return; + + for (int i = end; i >= start; --i) { + if (m_repopulating) { + QDeclarativeGeoMapItemViewItemData *itemData = m_itemDataBatched.takeAt(i); + if (!itemData) + continue; + if (itemData->incubator) { + if (itemData->incubator->isReady()) { + --m_readyIncubators; + delete itemData->incubator->object(); + } + itemData->incubator->clear(); + } + delete itemData; + } else { + QDeclarativeGeoMapItemViewItemData *itemData = m_itemData.takeAt(i); + removeItemData(itemData); + } + } + + fitViewport(); +} + +void QDeclarativeGeoMapItemView::itemModelRowsMoved(const QModelIndex &parent, int start, int end, + const QModelIndex &destination, int row) +{ + Q_UNUSED(parent) + Q_UNUSED(start) + Q_UNUSED(end) + Q_UNUSED(destination) + Q_UNUSED(row) + + qWarning() << "QDeclarativeGeoMapItemView does not support models that move rows."; +} + +void QDeclarativeGeoMapItemView::itemModelDataChanged(const QModelIndex &topLeft, + const QModelIndex &bottomRight, + const QVector<int> &roles) +{ + Q_UNUSED(roles) + + if (!m_itemData.count() || (m_repopulating && !m_itemDataBatched.count()) ) + return; + + for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { + const QModelIndex index = itemModel_->index(i, 0); + QDeclarativeGeoMapItemViewItemData *itemData; + if (m_repopulating) + itemData= m_itemDataBatched.at(i); + else + itemData= m_itemData.at(i); + + QHashIterator<int, QByteArray> iterator(itemModel_->roleNames()); + while (iterator.hasNext()) { + iterator.next(); + + QVariant modelData = itemModel_->data(index, iterator.key()); + if (!modelData.isValid()) + continue; + + itemData->context->setContextProperty(QString::fromLatin1(iterator.value().constData()), + modelData); + itemData->modelDataMeta->setValue(iterator.value(), modelData); + } + } +} + +/*! + \qmlproperty Component QtLocation::MapItemView::delegate + + This property holds the delegate which defines how each item in the + model should be displayed. The Component must contain exactly one + MapItem -derived object as the root object. +*/ +QQmlComponent *QDeclarativeGeoMapItemView::delegate() const +{ + return delegate_; +} + +void QDeclarativeGeoMapItemView::setDelegate(QQmlComponent *delegate) +{ + if (delegate_ == delegate) + return; + + delegate_ = delegate; + + repopulate(); + emit delegateChanged(); +} + +/*! + \qmlproperty Component QtLocation::MapItemView::autoFitViewport + + This property controls whether to automatically pan and zoom the viewport + to display all map items when items are added or removed. + + Defaults to false. +*/ +bool QDeclarativeGeoMapItemView::autoFitViewport() const +{ + return fitViewport_; +} + +void QDeclarativeGeoMapItemView::setAutoFitViewport(const bool &fitViewport) +{ + if (fitViewport == fitViewport_) + return; + fitViewport_ = fitViewport; + emit autoFitViewportChanged(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::fitViewport() +{ + if (!map_ || !fitViewport_ || m_repopulating) + return; + + if (map_->mapItems().size() > 0) + map_->fitViewportToMapItems(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::setMap(QDeclarativeGeoMap *map) +{ + if (!map || map_) // changing map on the fly not supported + return; + map_ = map; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::removeInstantiatedItems() +{ + if (!map_) + return; + + terminateOngoingRepopulation(); + foreach (QDeclarativeGeoMapItemViewItemData *itemData, m_itemData) + removeItemData(itemData); + m_itemData.clear(); +} + +/*! + \internal + + Instantiates all items. +*/ +void QDeclarativeGeoMapItemView::instantiateAllItems() +{ + if (!componentCompleted_ || !map_ || !delegate_ || !itemModel_) + return; + Q_ASSERT(!m_itemDataBatched.size()); + m_repopulating = true; + + // QQuickWidget forces incubators to synchronous mode. Thus itemDataChanged gets called during the for loop below. + m_itemDataBatched.resize(itemModel_->rowCount()); + for (int i = 0; i < itemModel_->rowCount(); ++i) { + const QModelIndex index = itemModel_->index(i, 0); + createItemForIndex(index, true); + } + + fitViewport(); +} + +void QDeclarativeGeoMapItemView::removeItemData(QDeclarativeGeoMapItemViewItemData *itemData) +{ + if (!itemData) + return; + if (itemData->incubator) { + if (itemData->incubator->isReady()) { + if (itemData->incubator->object() == itemData->item) { + map_->removeMapItem(itemData->item); // removeMapItem checks whether the item is in the map, so it's safe to call. + itemData->item = 0; + } + delete itemData->incubator->object(); + } + itemData->incubator->clear(); // stops ongoing incubation + } + if (itemData->item) + map_->removeMapItem(itemData->item); + delete itemData; // destroys the ->item too. +} + +void QDeclarativeGeoMapItemView::terminateOngoingRepopulation() +{ + if (m_repopulating) { + // Terminate the previous resetting task. Not all incubators finished, but + // QQmlIncubatorController operates in the same thread, so it is safe + // to check, here, whether incubators are ready or not, without having + // to race with them. + + foreach (QDeclarativeGeoMapItemViewItemData *itemData, m_itemDataBatched) + removeItemData(itemData); + + m_itemDataBatched.clear(); + m_readyIncubators = 0; + m_repopulating = false; + } +} + +/*! + \internal + Removes and repopulates all items. +*/ +void QDeclarativeGeoMapItemView::repopulate() +{ + if (!itemModel_ || !itemModel_->rowCount()) { + removeInstantiatedItems(); + } else { + terminateOngoingRepopulation(); + instantiateAllItems(); // removal of instantiated item done at incubation completion + } +} + +/*! + \internal + + Note: this call is async. that is returns to the event loop before returning to the caller. + May also trigger incubatorStatusChanged() before returning to the caller if the incubator is fast enough. +*/ +void QDeclarativeGeoMapItemView::createItemForIndex(const QModelIndex &index, bool batched) +{ + // Expected to be already tested by caller. + Q_ASSERT(delegate_); + Q_ASSERT(itemModel_); + + QDeclarativeGeoMapItemViewItemData *itemData = new QDeclarativeGeoMapItemViewItemData; + + itemData->modelData = new QObject; + itemData->modelDataMeta = new QQmlOpenMetaObject(itemData->modelData, m_metaObjectType, false); + itemData->context = new QQmlContext(qmlContext(this)); + + QHashIterator<int, QByteArray> iterator(itemModel_->roleNames()); + while (iterator.hasNext()) { + iterator.next(); + + QVariant modelData = itemModel_->data(index, iterator.key()); + if (!modelData.isValid()) + continue; + + itemData->context->setContextProperty(QString::fromLatin1(iterator.value().constData()), + modelData); + + itemData->modelDataMeta->setValue(iterator.value(), modelData); + } + + itemData->context->setContextProperty(QLatin1String("model"), itemData->modelData); + itemData->context->setContextProperty(QLatin1String("index"), index.row()); + + if (batched || m_repopulating) { + if (index.row() < m_itemDataBatched.size()) + m_itemDataBatched.replace(index.row(), itemData); + else + m_itemDataBatched.insert(index.row(), itemData); + } else + m_itemData.insert(index.row(), itemData); + itemData->incubator = new MapItemViewDelegateIncubator(this, itemData, batched || m_repopulating); + + delegate_->create(*itemData->incubator, itemData->context); +} + +QDeclarativeGeoMapItemViewItemData::~QDeclarativeGeoMapItemViewItemData() +{ + delete incubator; + delete item; + delete context; + delete modelData; +} + +#include "moc_qdeclarativegeomapitemview_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapitemview_p.h b/src/location/declarativemaps/qdeclarativegeomapitemview_p.h new file mode 100644 index 00000000..bd696d6e --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitemview_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jolla Ltd. +** Contact: Aaron McCarthy <aaron.mccarthy@jollamobile.com> +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPITEMVIEW_H +#define QDECLARATIVEGEOMAPITEMVIEW_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> + +#include <QtCore/QModelIndex> +#include <QtQml/QQmlParserStatus> +#include <QtQml/QQmlIncubator> +#include <QtQml/qqml.h> + +QT_BEGIN_NAMESPACE + +class QAbstractItemModel; +class QQmlComponent; +class QQuickItem; +class QDeclarativeGeoMap; +class QDeclarativeGeoMapItemBase; +class QQmlOpenMetaObject; +class QQmlOpenMetaObjectType; +class MapItemViewDelegateIncubator; +class QDeclarativeGeoMapItemViewItemData; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemView : public QObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_INTERFACES(QQmlParserStatus) + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(bool autoFitViewport READ autoFitViewport WRITE setAutoFitViewport NOTIFY autoFitViewportChanged) + +public: + explicit QDeclarativeGeoMapItemView(QQuickItem *parent = 0); + ~QDeclarativeGeoMapItemView(); + + QVariant model() const; + void setModel(const QVariant &); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *); + + bool autoFitViewport() const; + void setAutoFitViewport(const bool &); + + void setMap(QDeclarativeGeoMap *); + void repopulate(); + void removeInstantiatedItems(); + void instantiateAllItems(); + + qreal zValue(); + void setZValue(qreal zValue); + + // From QQmlParserStatus + virtual void componentComplete(); + void classBegin() {} + +Q_SIGNALS: + void modelChanged(); + void delegateChanged(); + void autoFitViewportChanged(); + +protected: + void incubatorStatusChanged(MapItemViewDelegateIncubator *incubator, + QQmlIncubator::Status status, + bool batched); + +private Q_SLOTS: + void itemModelReset(); + void itemModelRowsInserted(const QModelIndex &index, int start, int end); + void itemModelRowsRemoved(const QModelIndex &index, int start, int end); + void itemModelRowsMoved(const QModelIndex &parent, int start, int end, + const QModelIndex &destination, int row); + void itemModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QVector<int> &roles); + +private: + void createItemForIndex(const QModelIndex &index, bool batched = false); + void fitViewport(); + void terminateOngoingRepopulation(); + void removeItemData(QDeclarativeGeoMapItemViewItemData *itemData); + + bool componentCompleted_; + QQmlComponent *delegate_; + QAbstractItemModel *itemModel_; + QDeclarativeGeoMap *map_; + QVector<QDeclarativeGeoMapItemViewItemData *> m_itemData; + QVector<QDeclarativeGeoMapItemViewItemData *> m_itemDataBatched; + bool fitViewport_; + + QQmlOpenMetaObjectType *m_metaObjectType; + int m_readyIncubators; + bool m_repopulating; + + friend struct QDeclarativeGeoMapItemViewItemData; + friend class MapItemViewDelegateIncubator; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMapItemView) + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomapitemview_p_p.h b/src/location/declarativemaps/qdeclarativegeomapitemview_p_p.h new file mode 100644 index 00000000..3ad3ceb4 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitemview_p_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPITEMVIEW_P_P_H +#define QDECLARATIVEGEOMAPITEMVIEW_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QModelIndex> +#include <QtQml/QQmlParserStatus> +#include <QtQml/QQmlIncubator> +#include <QtQml/qqml.h> +#include <QtQml/private/qqmlopenmetaobject_p.h> + +QT_BEGIN_NAMESPACE + +class MapItemViewDelegateIncubator; +class QDeclarativeGeoMapItemView; +class QDeclarativeGeoMapItemBase; + +class QDeclarativeGeoMapItemViewItemData +{ +public: + QDeclarativeGeoMapItemViewItemData() + : incubator(0), item(0), context(0), modelData(0), modelDataMeta(0) + { + } + + ~QDeclarativeGeoMapItemViewItemData(); + + MapItemViewDelegateIncubator *incubator; + QDeclarativeGeoMapItemBase *item; + QQmlContext *context; + QObject *modelData; + QQmlOpenMetaObject *modelDataMeta; + + friend class MapItemViewDelegateIncubator; + friend class QDeclarativeGeoMapItemView; +}; + +Q_DECLARE_TYPEINFO(QDeclarativeGeoMapItemViewItemData, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif // QDECLARATIVEGEOMAPITEMVIEW_P_P_H diff --git a/src/location/declarativemaps/qdeclarativegeomapparameter.cpp b/src/location/declarativemaps/qdeclarativegeomapparameter.cpp new file mode 100644 index 00000000..88d609f4 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapparameter.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "qdeclarativegeomapparameter_p.h" + +#include <QByteArray> +#include <QMetaObject> +#include <QMetaProperty> +#include <QSignalMapper> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapParameter + \instantiates QDeclarativeGeoMapParameter + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.9 + + \brief The MapParameter type represents a parameter for a Map element. + This type provides a mean to specify plugin-dependent optional parameters + for a map. + + MapParameters by default contain only the \l type property, and + are highly plugin-dependent. + For this reason, additional properties have to be defined inside a + MapParameter at declaration time, using the QML syntax "property var foo". + + What properties have to be put inside a particular MapParameter type for + a particular plugin can be found in the documentation of the plugin. + Note that MapProperties are \b optional. + By not specifying any of them, the Map will have the default behavior. + + The release of this API with Qt 5.9 is a Technology Preview. +*/ + +/*! + \qmlproperty georectangle QtLocation::MapParameter::type + + Set-once property which holds a string defining the type of the MapParameter +*/ + +QDeclarativeGeoMapParameter::QDeclarativeGeoMapParameter(QObject *parent) +: QGeoMapParameter(parent), m_initialPropertyCount(metaObject()->propertyCount()), m_complete(false) +{ + +} + +QDeclarativeGeoMapParameter::~QDeclarativeGeoMapParameter() +{ +} + +bool QDeclarativeGeoMapParameter::isComponentComplete() const +{ + return m_complete; +} + +int QDeclarativeGeoMapParameter::initialPropertyCount() const +{ + return m_initialPropertyCount; +} + +void QDeclarativeGeoMapParameter::classBegin() +{ +} + +void QDeclarativeGeoMapParameter::componentComplete() +{ + for (int i = m_initialPropertyCount; i < metaObject()->propertyCount(); ++i) { + QMetaProperty property = metaObject()->property(i); + + if (!property.hasNotifySignal()) { + return; + } + + QSignalMapper *mapper = new QSignalMapper(this); + mapper->setMapping(this, i); + + const QByteArray signalName = '2' + property.notifySignal().methodSignature(); // TODO: explain why '2' + QObject::connect(this, signalName, mapper, SLOT(map())); + QObject::connect(mapper, SIGNAL(mapped(int)), this, SLOT(onPropertyUpdated(int))); + } + m_complete = true; + emit completed(this); +} + +void QDeclarativeGeoMapParameter::onPropertyUpdated(int index) +{ + emit propertyUpdated(this, metaObject()->property(index).name()); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapparameter_p.h b/src/location/declarativemaps/qdeclarativegeomapparameter_p.h new file mode 100644 index 00000000..0f54e1b7 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapparameter_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPPARAMETER_P_H +#define QDECLARATIVEGEOMAPPARAMETER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qgeomapparameter_p.h> +#include <QQmlParserStatus> +#include <qqml.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapParameter : public QGeoMapParameter, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativeGeoMapParameter(QObject *parent = 0); + virtual ~QDeclarativeGeoMapParameter(); + + bool isComponentComplete() const; + +Q_SIGNALS: + void completed(QDeclarativeGeoMapParameter *); + +protected: + int initialPropertyCount() const; + // QQmlParserStatus implementation + void classBegin() override; + void componentComplete() override; + +private slots: + void onPropertyUpdated(int index); + +private: + int m_initialPropertyCount; + bool m_complete; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMapParameter) + +#endif // QDECLARATIVEGEOMAPPARAMETER_P_H diff --git a/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp b/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp new file mode 100644 index 00000000..b3b8aa8a --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** 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 "qdeclarativegeomapquickitem_p.h" + +#include <QtCore/QScopedValueRollback> +#include <QtQml/qqmlinfo.h> +#include <QtQuick/QSGOpacityNode> +#include <QtPositioning/private/qdoublevector2d_p.h> +#include <QtQuick/private/qquickmousearea_p.h> +#include <QtLocation/private/qgeomap_p.h> + +#include <QDebug> +#include <cmath> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapQuickItem + \instantiates QDeclarativeGeoMapQuickItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.5 + + \brief The MapQuickItem type displays an arbitrary Qt Quick object + on a Map. + + The MapQuickItem type is used to place an arbitrary Qt Quick object + on a Map at a specified location and size. Compared to floating an item + above the Map, a MapQuickItem will follow the panning (and optionally, the + zooming) of the Map as if it is on the Map surface. + + The \l{sourceItem} property contains the Qt Quick item to be drawn, which + can be any kind of visible type. + + \section2 Positioning and Sizing + + The positioning of the MapQuickItem on the Map is controlled by two + properties: \l coordinate and \l anchorPoint. If only \l coordinate is set, + it specifies a longitude/latitude coordinate for the item to be placed at. + The set coordinate will line up with the top-left corner of the contained + item when shown on the screen. + + The \l anchorPoint property provides a way to line up the coordinate with + other parts of the item than just the top-left corner, by setting a number + of pixels the item will be offset by. A simple way to think about it is + to note that the point given by \l anchorPoint on the item itself is the + point that will line up with the given \l coordinate when displayed. + + In addition to being anchored to the map, the MapQuickItem can optionally + follow the scale of the map, and change size when the Map is zoomed in or + zoomed out. This behaviour is controlled by the \l zoomLevel property. The + default behaviour if \l zoomLevel is not set is for the item to be drawn + "on the screen" rather than "on the map", so that its size remains the same + regardless of the zoom level of the Map. + + \section2 Performance + + Performance of a MapQuickItem is normally in the same ballpark as the + contained Qt Quick item alone. Overheads added amount to a translation + and (possibly) scaling of the original item, as well as a transformation + from longitude and latitude to screen position. + + \section2 Limitations + + \note Due to an implementation detail, items placed inside a + MapQuickItem will have a \c{parent} item which is not the MapQuickItem. + Refer to the MapQuickItem by its \c{id}, and avoid the use of \c{anchor} + in the \c{sourceItem}. + + \section2 Example Usage + + The following snippet shows a MapQuickItem containing an Image object, + to display a Marker on the Map. This strategy is used to show the map + markers in the MapViewer example. + + \snippet mapviewer/map/Marker.qml mqi-top + \snippet mapviewer/map/Marker.qml mqi-anchor + \snippet mapviewer/map/Marker.qml mqi-closeimage + \snippet mapviewer/map/Marker.qml mqi-close + + \image api-mapquickitem.png +*/ + +QDeclarativeGeoMapQuickItem::QDeclarativeGeoMapQuickItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), zoomLevel_(0.0), + mapAndSourceItemSet_(false), updatingGeometry_(false) +{ + setFlag(ItemHasContents, true); + opacityContainer_ = new QQuickItem(this); + opacityContainer_->setParentItem(this); + opacityContainer_->setFlag(ItemHasContents, true); +} + +QDeclarativeGeoMapQuickItem::~QDeclarativeGeoMapQuickItem() {} + +/*! + \qmlproperty coordinate MapQuickItem::coordinate + + This property holds the anchor coordinate of the MapQuickItem. The point + on the sourceItem that is specified by anchorPoint is kept aligned with + this coordinate when drawn on the map. + + In the image below, there are 3 MapQuickItems that are identical except + for the value of their anchorPoint properties. The values of anchorPoint + for each are written on top of the item. + + \image api-mapquickitem-anchor.png +*/ +void QDeclarativeGeoMapQuickItem::setCoordinate(const QGeoCoordinate &coordinate) +{ + if (coordinate_ == coordinate) + return; + + coordinate_ = coordinate; + geoshape_.setTopLeft(coordinate_); + geoshape_.setBottomRight(coordinate_); + // TODO: Handle zoomLevel != 0.0 + + polishAndUpdate(); + emit coordinateChanged(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (map && quickMap) { + connect(map, SIGNAL(cameraDataChanged(QGeoCameraData)), + this, SLOT(polishAndUpdate())); + polishAndUpdate(); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (!mapAndSourceItemSet_ || updatingGeometry_ || + newGeometry.topLeft() == oldGeometry.topLeft()) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + + QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(x(), y()) + (scaleFactor() * QDoubleVector2D(anchorPoint_)), false); + if (newCoordinate.isValid()) + setCoordinate(newCoordinate); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +/*! + \internal +*/ +QGeoCoordinate QDeclarativeGeoMapQuickItem::coordinate() +{ + return coordinate_; +} + +/*! + \qmlproperty object MapQuickItem::sourceItem + + This property holds the source item that will be drawn on the map. +*/ +void QDeclarativeGeoMapQuickItem::setSourceItem(QQuickItem *sourceItem) +{ + if (sourceItem_.data() == sourceItem) + return; + sourceItem_ = sourceItem; + + polishAndUpdate(); + emit sourceItemChanged(); +} + +QQuickItem *QDeclarativeGeoMapQuickItem::sourceItem() +{ + return sourceItem_.data(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::afterChildrenChanged() +{ + QList<QQuickItem *> kids = childItems(); + if (kids.size() > 0) { + bool printedWarning = false; + foreach (QQuickItem *i, kids) { + if (i->flags() & QQuickItem::ItemHasContents + && !qobject_cast<QQuickMouseArea *>(i) + && sourceItem_.data() != i + && opacityContainer_ != i) { + if (!printedWarning) { + qmlWarning(this) << "Use the sourceItem property for the contained item, direct children are not supported"; + printedWarning = true; + } + + qmlWarning(i) << "deleting this child"; + i->deleteLater(); + } + } + } +} + +/*! + \qmlproperty QPointF MapQuickItem::anchorPoint + + This property determines which point on the sourceItem that will be lined + up with the coordinate on the map. +*/ +void QDeclarativeGeoMapQuickItem::setAnchorPoint(const QPointF &anchorPoint) +{ + if (anchorPoint == anchorPoint_) + return; + anchorPoint_ = anchorPoint; + polishAndUpdate(); + emit anchorPointChanged(); +} + +QPointF QDeclarativeGeoMapQuickItem::anchorPoint() const +{ + return anchorPoint_; +} + +/*! + \qmlproperty real MapQuickItem::zoomLevel + + This property controls the scaling behaviour of the contents of the + MapQuickItem. In particular, by setting this property it is possible + to choose between objects that are drawn on the screen (and sized in + screen pixels), and those drawn on the map surface (which change size + with the zoom level of the map). + + The default value for this property is 0.0, which corresponds to drawing + the object on the screen surface. If set to another value, the object will + be drawn on the map surface instead. The value (if not zero) specifies the + zoomLevel at which the object will be visible at a scale of 1:1 (ie, where + object pixels and screen pixels are the same). At zoom levels lower than + this, the object will appear smaller, and at higher zoom levels, appear + larger. This is in contrast to when this property is set to zero, where + the object remains the same size on the screen at all zoom levels. +*/ +void QDeclarativeGeoMapQuickItem::setZoomLevel(qreal zoomLevel) +{ + if (zoomLevel == zoomLevel_) + return; + zoomLevel_ = zoomLevel; + // TODO: update geoshape_! + polishAndUpdate(); + emit zoomLevelChanged(); +} + +qreal QDeclarativeGeoMapQuickItem::zoomLevel() const +{ + return zoomLevel_; +} + +const QGeoShape &QDeclarativeGeoMapQuickItem::geoShape() const +{ + // TODO: return a QGeoRectangle representing the bounding geo rectangle of the quick item + // when zoomLevel_ is != 0.0 + return geoshape_; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::updatePolish() +{ + if (!quickMap() && sourceItem_) { + mapAndSourceItemSet_ = false; + sourceItem_.data()->setParentItem(0); + return; + } + + if (!quickMap() || !map() || !sourceItem_) { + mapAndSourceItemSet_ = false; + return; + } + + if (!mapAndSourceItemSet_ && quickMap() && map() && sourceItem_) { + mapAndSourceItemSet_ = true; + sourceItem_.data()->setParentItem(opacityContainer_); + sourceItem_.data()->setTransformOrigin(QQuickItem::TopLeft); + connect(sourceItem_.data(), SIGNAL(xChanged()), + this, SLOT(polishAndUpdate())); + connect(sourceItem_.data(), SIGNAL(yChanged()), + this, SLOT(polishAndUpdate())); + connect(sourceItem_.data(), SIGNAL(widthChanged()), + this, SLOT(polishAndUpdate())); + connect(sourceItem_.data(), SIGNAL(heightChanged()), + this, SLOT(polishAndUpdate())); + } + + QScopedValueRollback<bool> rollback(updatingGeometry_); + updatingGeometry_ = true; + + opacityContainer_->setOpacity(zoomLevelOpacity()); + + sourceItem_.data()->setScale(scaleFactor()); + sourceItem_.data()->setPosition(QPointF(0,0)); + setWidth(sourceItem_.data()->width()); + setHeight(sourceItem_.data()->height()); + setPositionOnMap(coordinate(), scaleFactor() * anchorPoint_); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + Q_UNUSED(event); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapQuickItem::scaleFactor() +{ + qreal scale = 1.0; + // use 1+x to avoid fuzzy compare against zero + if (!qFuzzyCompare(1.0 + zoomLevel_, 1.0)) + scale = std::pow(0.5, zoomLevel_ - map()->cameraData().zoomLevel()); + return scale; +} + +#include "moc_qdeclarativegeomapquickitem_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h b/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h new file mode 100644 index 00000000..2d910167 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPQUICKITEM_H +#define QDECLARATIVEGEOMAPQUICKITEM_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> + +#include <QtQuick/QQuickItem> +#include <QtQuick/QSGNode> + +#include <QtLocation/private/qdeclarativegeomap_p.h> +#include <QtLocation/private/qdeclarativegeomapitembase_p.h> +#include <QtPositioning/qgeoshape.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapQuickItem : public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) + Q_PROPERTY(QPointF anchorPoint READ anchorPoint WRITE setAnchorPoint NOTIFY anchorPointChanged) + Q_PROPERTY(qreal zoomLevel READ zoomLevel WRITE setZoomLevel NOTIFY zoomLevelChanged) + Q_PROPERTY(QQuickItem *sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged) + +public: + explicit QDeclarativeGeoMapQuickItem(QQuickItem *parent = 0); + ~QDeclarativeGeoMapQuickItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) Q_DECL_OVERRIDE; + + void setCoordinate(const QGeoCoordinate &coordinate); + QGeoCoordinate coordinate(); + + void setSourceItem(QQuickItem *sourceItem); + QQuickItem *sourceItem(); + + void setAnchorPoint(const QPointF &anchorPoint); + QPointF anchorPoint() const; + + void setZoomLevel(qreal zoomLevel); + qreal zoomLevel() const; + + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; + +Q_SIGNALS: + void coordinateChanged(); + void sourceItemChanged(); + void anchorPointChanged(); + void zoomLevelChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void updatePolish() Q_DECL_OVERRIDE; + +protected Q_SLOTS: + virtual void afterChildrenChanged() Q_DECL_OVERRIDE; + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) Q_DECL_OVERRIDE; + +private: + qreal scaleFactor(); + QGeoCoordinate coordinate_; + QGeoRectangle geoshape_; + QPointer<QQuickItem> sourceItem_; + QQuickItem *opacityContainer_; + QPointF anchorPoint_; + qreal zoomLevel_; + bool mapAndSourceItemSet_; + bool updatingGeometry_; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMapQuickItem) + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomaptype.cpp b/src/location/declarativemaps/qdeclarativegeomaptype.cpp new file mode 100644 index 00000000..0b90ec3a --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomaptype.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** 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 "qdeclarativegeomaptype_p.h" +#include <qnumeric.h> +#include <QtQml/qqml.h> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapType + \instantiates QDeclarativeGeoMapType + \inherits QObject + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.5 + + \brief The MapType type holds information about a map type. + + This includes the map type's \l name and \l description, the \l style and + a flag to indicate if the map type is optimized for mobile devices (\l mobile). +*/ + +QDeclarativeGeoMapType::QDeclarativeGeoMapType(const QGeoMapType mapType, QObject *parent) + : QObject(parent), + mapType_(mapType) {} + +QDeclarativeGeoMapType::~QDeclarativeGeoMapType() {} + +/*! + \qmlproperty enumeration MapType::style + + This read-only property gives access to the style of the map type. + + \list + \li MapType.NoMap - No map. + \li MapType.StreetMap - A street map. + \li MapType.SatelliteMapDay - A map with day-time satellite imagery. + \li MapType.SatelliteMapNight - A map with night-time satellite imagery. + \li MapType.TerrainMap - A terrain map. + \li MapType.HybridMap - A map with satellite imagery and street information. + \li MapType.GrayStreetMap - A gray-shaded street map. + \li MapType.PedestrianMap - A street map suitable for pedestriants. + \li MapType.CarNavigationMap - A street map suitable for car navigation. + \li MapType.CycleMap - A street map suitable for cyclists. + \li MapType.CustomMap - A custom map type. + \endlist +*/ +QDeclarativeGeoMapType::MapStyle QDeclarativeGeoMapType::style() const +{ + return QDeclarativeGeoMapType::MapStyle(mapType_.style()); +} + +/*! + \qmlproperty string MapType::name + + This read-only property holds the name of the map type as a single formatted string. +*/ +QString QDeclarativeGeoMapType::name() const +{ + return mapType_.name(); +} + +/*! + \qmlproperty string MapType::description + + This read-only property holds the description of the map type as a single formatted string. +*/ +QString QDeclarativeGeoMapType::description() const +{ + return mapType_.description(); +} + +/*! + \qmlproperty bool MapType::mobile + + \brief Whether the map type is optimized for the use on a mobile device. + + Map types for mobile devices usually have higher constrast to counteract the + effects of sunlight and a reduced color for improved readability. +*/ +bool QDeclarativeGeoMapType::mobile() const +{ + return mapType_.mobile(); +} + +/*! + \qmlproperty bool MapType::night + \since Qt Location 5.4 + + \brief Whether the map type is optimized for use at night. + + Map types suitable for use at night usually have a dark background. +*/ +bool QDeclarativeGeoMapType::night() const +{ + return mapType_.night(); +} + +#include "moc_qdeclarativegeomaptype_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomaptype_p.h b/src/location/declarativemaps/qdeclarativegeomaptype_p.h new file mode 100644 index 00000000..7b449aa0 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomaptype_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPTYPE_H +#define QDECLARATIVEGEOMAPTYPE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> + +#include <QtCore/QObject> +#include <QtQml/qqml.h> +#include <QtLocation/private/qgeomaptype_p.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapType : public QObject +{ + Q_OBJECT + Q_ENUMS(MapStyle) + + Q_PROPERTY(MapStyle style READ style CONSTANT) + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QString description READ description CONSTANT) + Q_PROPERTY(bool mobile READ mobile CONSTANT) + Q_PROPERTY(bool night READ night CONSTANT REVISION 1) + +public: + enum MapStyle { + NoMap = QGeoMapType::NoMap, + StreetMap = QGeoMapType::StreetMap, + SatelliteMapDay = QGeoMapType::SatelliteMapDay, + SatelliteMapNight = QGeoMapType::SatelliteMapNight, + TerrainMap = QGeoMapType::TerrainMap, + HybridMap = QGeoMapType::HybridMap, + TransitMap = QGeoMapType::TransitMap, + GrayStreetMap = QGeoMapType::GrayStreetMap, + PedestrianMap = QGeoMapType::PedestrianMap, + CarNavigationMap = QGeoMapType::CarNavigationMap, + CycleMap = QGeoMapType::CycleMap, + CustomMap = 100 + }; + + QDeclarativeGeoMapType(const QGeoMapType mapType, QObject *parent = 0); + ~QDeclarativeGeoMapType(); + + MapStyle style() const; + QString name() const; + QString description() const; + bool mobile() const; + bool night() const; + + const QGeoMapType mapType() { return mapType_; } + +private: + QGeoMapType mapType_; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMapType) + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeoroute.cpp b/src/location/declarativemaps/qdeclarativegeoroute.cpp new file mode 100644 index 00000000..fd6378da --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroute.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** 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 "qdeclarativegeoroute_p.h" +#include "locationvaluetypehelper_p.h" +#include <QtLocation/private/qgeomap_p.h> + +#include <QtQml/QQmlEngine> +#include <QtQml/qqmlinfo.h> +#include <QtQml/private/qqmlengine_p.h> +#include <QtPositioning/QGeoRectangle> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Route + \instantiates QDeclarativeGeoRoute + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since Qt Location 5.5 + + \brief The Route type represents one geographical route. + + A Route type contains high level information about a route, such + as the length the route, the estimated travel time for the route, + and enough information to render a basic image of the route on a map. + + The QGeoRoute object also contains a list of \l RouteSegment objects which + describe subsections of the route in greater detail. + + The primary means of acquiring Route objects is \l RouteModel. + + \section1 Example + + This example shows how to display a route's maneuvers in a ListView: + + \snippet declarative/routing.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/routing.qml Route Maneuver List1 + \snippet declarative/routing.qml Route Maneuver List2 + \snippet declarative/routing.qml Route Maneuver List3 + +*/ + +QDeclarativeGeoRoute::QDeclarativeGeoRoute(QObject *parent) + : QObject(parent) +{ + this->init(); +} + +QDeclarativeGeoRoute::QDeclarativeGeoRoute(const QGeoRoute &route, QObject *parent) + : QObject(parent), + route_(route) +{ + this->init(); +} + +QDeclarativeGeoRoute::~QDeclarativeGeoRoute() {} + +void QDeclarativeGeoRoute::init() +{ + QGeoRouteSegment segment = route_.firstRouteSegment(); + while (segment.isValid()) { + QDeclarativeGeoRouteSegment *routeSegment = new QDeclarativeGeoRouteSegment(segment, this); + QQmlEngine::setContextForObject(routeSegment, QQmlEngine::contextForObject(this)); + segments_.append(routeSegment); + segment = segment.nextRouteSegment(); + } +} + +/*! + \internal +*/ +QList<QGeoCoordinate> QDeclarativeGeoRoute::routePath() +{ + return route_.path(); +} + +/*! + \qmlproperty georectangle QtLocation::Route::bounds + + Read-only property which holds a bounding box which encompasses the entire route. + +*/ + +QGeoRectangle QDeclarativeGeoRoute::bounds() const +{ + return route_.bounds(); +} + +/*! + \qmlproperty int QtLocation::Route::travelTime + + Read-only property which holds the estimated amount of time it will take to + traverse this route, in seconds. + +*/ + +int QDeclarativeGeoRoute::travelTime() const +{ + return route_.travelTime(); +} + +/*! + \qmlproperty real QtLocation::Route::distance + + Read-only property which holds distance covered by this route, in meters. +*/ + +qreal QDeclarativeGeoRoute::distance() const +{ + return route_.distance(); +} + +/*! + \qmlproperty QJSValue QtLocation::Route::path + + Read-only property which holds the geographical coordinates of this route. + Coordinates are listed in the order in which they would be traversed by someone + traveling along this segment of the route. + + To access individual segments you can use standard list accessors: 'path.length' + indicates the number of objects and 'path[index starting from zero]' gives + the actual object. + + \sa QtPositioning::coordinate +*/ + +QJSValue QDeclarativeGeoRoute::path() const +{ + QQmlContext *context = QQmlEngine::contextForObject(parent()); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped<QV4::ArrayObject> pathArray(scope, v4->newArrayObject(route_.path().length())); + for (int i = 0; i < route_.path().length(); ++i) { + const QGeoCoordinate &c = route_.path().at(i); + + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c))); + pathArray->putIndexed(i, cv); + } + + return QJSValue(v4, pathArray.asReturnedValue()); +} + +void QDeclarativeGeoRoute::setPath(const QJSValue &value) +{ + if (!value.isArray()) + return; + + QList<QGeoCoordinate> pathList; + quint32 length = value.property(QStringLiteral("length")).toUInt(); + for (quint32 i = 0; i < length; ++i) { + bool ok; + QGeoCoordinate c = parseCoordinate(value.property(i), &ok); + + if (!ok || !c.isValid()) { + qmlWarning(this) << "Unsupported path type"; + return; + } + + pathList.append(c); + } + + if (route_.path() == pathList) + return; + + route_.setPath(pathList); + + emit pathChanged(); +} + +/*! + \qmlproperty list<RouteSegment> QtLocation::Route::segments + + Read-only property which holds the list of \l RouteSegment objects of this route. + + To access individual segments you can use standard list accessors: 'segments.length' + indicates the number of objects and 'segments[index starting from zero]' gives + the actual objects. + + \sa RouteSegment +*/ + +QQmlListProperty<QDeclarativeGeoRouteSegment> QDeclarativeGeoRoute::segments() +{ + return QQmlListProperty<QDeclarativeGeoRouteSegment>(this, 0, segments_append, segments_count, + segments_at, segments_clear); +} + +/*! + \internal +*/ +void QDeclarativeGeoRoute::segments_append(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop, + QDeclarativeGeoRouteSegment *segment) +{ + static_cast<QDeclarativeGeoRoute *>(prop->object)->appendSegment(segment); +} + +/*! + \internal +*/ +int QDeclarativeGeoRoute::segments_count(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop) +{ + return static_cast<QDeclarativeGeoRoute *>(prop->object)->segments_.count(); +} + +/*! + \internal +*/ +QDeclarativeGeoRouteSegment *QDeclarativeGeoRoute::segments_at(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop, int index) +{ + return static_cast<QDeclarativeGeoRoute *>(prop->object)->segments_.at(index); +} + +/*! + \internal +*/ +void QDeclarativeGeoRoute::segments_clear(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop) +{ + static_cast<QDeclarativeGeoRoute *>(prop->object)->clearSegments(); +} + +/*! + \internal +*/ +void QDeclarativeGeoRoute::appendSegment(QDeclarativeGeoRouteSegment *segment) +{ + segments_.append(segment); +} + +/*! + \internal +*/ +void QDeclarativeGeoRoute::clearSegments() +{ + segments_.clear(); +} + +#include "moc_qdeclarativegeoroute_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeoroute_p.h b/src/location/declarativemaps/qdeclarativegeoroute_p.h new file mode 100644 index 00000000..e4501770 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroute_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOROUTE_H +#define QDECLARATIVEGEOROUTE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeoroutesegment_p.h> + +#include <QtCore/QObject> +#include <QtQml/QQmlListProperty> +#include <QtLocation/QGeoRoute> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoRoute : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QGeoRectangle bounds READ bounds CONSTANT) + Q_PROPERTY(int travelTime READ travelTime CONSTANT) + Q_PROPERTY(qreal distance READ distance CONSTANT) + Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QQmlListProperty<QDeclarativeGeoRouteSegment> segments READ segments CONSTANT) + +public: + explicit QDeclarativeGeoRoute(QObject *parent = 0); + QDeclarativeGeoRoute(const QGeoRoute &route, QObject *parent = 0); + ~QDeclarativeGeoRoute(); + + QGeoRectangle bounds() const; + int travelTime() const; + qreal distance() const; + + QJSValue path() const; + void setPath(const QJSValue &value); + + QQmlListProperty<QDeclarativeGeoRouteSegment> segments(); + + void appendSegment(QDeclarativeGeoRouteSegment *segment); + void clearSegments(); + +Q_SIGNALS: + void pathChanged(); + +private: + static void segments_append(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop, QDeclarativeGeoRouteSegment *segment); + static int segments_count(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop); + static QDeclarativeGeoRouteSegment *segments_at(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop, int index); + static void segments_clear(QQmlListProperty<QDeclarativeGeoRouteSegment> *prop); + + void init(); + QList<QGeoCoordinate> routePath(); + + QGeoRoute route_; + QList<QDeclarativeGeoRouteSegment *> segments_; + friend class QDeclarativeRouteMapItem; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp b/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp new file mode 100644 index 00000000..90796412 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp @@ -0,0 +1,1308 @@ +/**************************************************************************** +** +** 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 "qdeclarativegeoroutemodel_p.h" +#include "qdeclarativegeoroute_p.h" +#include "error_messages.h" +#include "locationvaluetypehelper_p.h" + +#include <QtCore/QCoreApplication> +#include <QtQml/QQmlEngine> +#include <QtQml/qqmlinfo.h> +#include <QtQml/private/qqmlengine_p.h> +#include <QtLocation/QGeoRoutingManager> +#include <QtPositioning/QGeoRectangle> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype RouteModel + \instantiates QDeclarativeGeoRouteModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since Qt Location 5.5 + + \brief The RouteModel type provides access to routes. + + The RouteModel type is used as part of a model/view grouping to retrieve + geographic routes from a backend provider. Routes include data about driving + directions between two points, walking directions with multiple waypoints, + and various other similar concepts. It functions much like other Model + types in QML (see for example \l {Models and Views in Qt Quick#ListModel}{ListModel} and + \l XmlListModel), and interacts with views such as \l MapItemView, and \l{ListView}. + + Like \l Map and \l GeocodeModel, all the data for a RouteModel to work comes + from a services plugin. This is contained in the \l{plugin} property, and + this must be set before the RouteModel can do any useful work. + + Once the plugin is set, create a \l RouteQuery with the appropriate + waypoints and other settings, and set the RouteModel's \l{query} + property. If \l autoUpdate is enabled, the update will being automatically. + Otherwise, the \l{update} method may be used. By default, autoUpdate is + disabled. + + The data stored and returned in the RouteModel consists of \l Route objects, + as a list with the role name "routeData". See the documentation for \l Route + for further details on its structure and contents. + + \section2 Example Usage + + The following snippet is two-part, showing firstly the declaration of + objects, and secondly a short piece of procedural code using it. We set + the routeModel's \l{autoUpdate} property to false, and call \l{update} once + the query is set up, to avoid useless extra requests halfway through the + set up of the query. + + \code + Plugin { + id: aPlugin + name: "osm" + } + + RouteQuery { + id: aQuery + } + + RouteModel { + id: routeModel + plugin: aPlugin + query: aQuery + autoUpdate: false + } + \endcode + + \code + { + aQuery.addWaypoint(...) + aQuery.addWaypoint(...) + aQuery.travelModes = ... + routeModel.update() + } + \endcode + +*/ + +QDeclarativeGeoRouteModel::QDeclarativeGeoRouteModel(QObject *parent) + : QAbstractListModel(parent), + complete_(false), + plugin_(0), + routeQuery_(0), + autoUpdate_(false), + status_(QDeclarativeGeoRouteModel::Null), + error_(QDeclarativeGeoRouteModel::NoError) +{ +} + +QDeclarativeGeoRouteModel::~QDeclarativeGeoRouteModel() +{ + if (!routes_.empty()) { + qDeleteAll(routes_); + routes_.clear(); + } +} + +/*! + \qmlproperty int QtLocation::RouteModel::count + + This property holds how many routes the model currently has. + Amongst other uses, you can use this value when accessing routes + via the QtLocation::RouteModel::get -method. +*/ + +int QDeclarativeGeoRouteModel::count() const +{ + return routes_.count(); +} + +/*! + \qmlmethod void QtLocation::RouteModel::reset() + + Resets the model. All route data is cleared, any outstanding requests + are aborted and possible errors are cleared. Model status will be set + to RouteModel.Null +*/ + +void QDeclarativeGeoRouteModel::reset() +{ + if (!routes_.isEmpty()) { + beginResetModel(); + qDeleteAll(routes_); + routes_.clear(); + emit countChanged(); + emit routesChanged(); + endResetModel(); + } + + emit abortRequested(); + setError(NoError, QString()); + setStatus(QDeclarativeGeoRouteModel::Null); +} + +/*! + \qmlmethod void QtLocation::RouteModel::cancel() + + Cancels any outstanding requests and clears errors. Model status will be set to either + RouteModel.Null or RouteModel.Ready. +*/ +void QDeclarativeGeoRouteModel::cancel() +{ + emit abortRequested(); + setError(NoError, QString()); + setStatus(routes_.isEmpty() ? Null : Ready); +} + +/*! + \qmlmethod void QtLocation::RouteModel::get(int) + + Returns the Route at given index. Use \l count property to check the + amount of routes available. The routes are indexed from zero, so the accessible range + is 0...(count - 1). + + If you access out of bounds, a zero (null object) is returned and a warning is issued. +*/ + +QDeclarativeGeoRoute *QDeclarativeGeoRouteModel::get(int index) +{ + if (index < 0 || index >= routes_.count()) { + qmlWarning(this) << QStringLiteral("Index '%1' out of range").arg(index); + return 0; + } + return routes_.at(index); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::componentComplete() +{ + complete_ = true; + if (autoUpdate_) { + update(); + } +} + +/*! + \internal +*/ +int QDeclarativeGeoRouteModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return routes_.count(); +} + +/*! + \internal +*/ +QVariant QDeclarativeGeoRouteModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + qmlWarning(this) << QStringLiteral("Error in indexing route model's data (invalid index)."); + return QVariant(); + } + + if (index.row() >= routes_.count()) { + qmlWarning(this) << QStringLiteral("Fatal error in indexing route model's data (index overflow)."); + return QVariant(); + } + + if (role == RouteRole) { + QObject *route = routes_.at(index.row()); + return QVariant::fromValue(route); + } + return QVariant(); +} + +QHash<int, QByteArray> QDeclarativeGeoRouteModel::roleNames() const +{ + QHash<int, QByteArray> roleNames = QAbstractListModel::roleNames(); + roleNames.insert(RouteRole, "routeData"); + return roleNames; +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (plugin_ == plugin) + return; + + reset(); // reset the model + + if (plugin_) + disconnect(plugin_, SIGNAL(localesChanged()), this, SIGNAL(measurementSystemChanged())); + if (plugin) + connect(plugin, SIGNAL(localesChanged()), this, SIGNAL(measurementSystemChanged())); + + plugin_ = plugin; + + if (complete_) + emit pluginChanged(); + + if (!plugin) + return; + + if (plugin_->isAttached()) { + pluginReady(); + } else { + connect(plugin_, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::pluginReady() +{ + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + QGeoRoutingManager *routingManager = serviceProvider->routingManager(); + + if (serviceProvider->error() != QGeoServiceProvider::NoError) { + QDeclarativeGeoRouteModel::RouteError newError = UnknownError; + switch (serviceProvider->error()) { + case QGeoServiceProvider::NotSupportedError: + newError = EngineNotSetError; break; + case QGeoServiceProvider::UnknownParameterError: + newError = UnknownParameterError; break; + case QGeoServiceProvider::MissingRequiredParameterError: + newError = MissingRequiredParameterError; break; + case QGeoServiceProvider::ConnectionError: + newError = CommunicationError; break; + default: + break; + } + + setError(newError, serviceProvider->errorString()); + return; + } + + if (!routingManager) { + setError(EngineNotSetError, tr("Plugin does not support routing.")); + return; + } + + connect(routingManager, SIGNAL(finished(QGeoRouteReply*)), + this, SLOT(routingFinished(QGeoRouteReply*))); + connect(routingManager, SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString)), + this, SLOT(routingError(QGeoRouteReply*,QGeoRouteReply::Error,QString))); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::queryDetailsChanged() +{ + if (autoUpdate_ && complete_) + update(); +} + +/*! + \qmlproperty Plugin QtLocation::RouteModel::plugin + + This property holds the plugin that providers the actual + routing service. Note that all plugins do not necessarily + provide routing (could for example provide only geocoding or maps). + + A valid plugin must be set before the RouteModel can perform any useful + operations. + + \sa Plugin +*/ + +QDeclarativeGeoServiceProvider *QDeclarativeGeoRouteModel::plugin() const +{ + return plugin_; +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::setQuery(QDeclarativeGeoRouteQuery *query) +{ + if (!query || query == routeQuery_) + return; + if (routeQuery_) + routeQuery_->disconnect(this); + routeQuery_ = query; + connect(query, SIGNAL(queryDetailsChanged()), this, SLOT(queryDetailsChanged())); + if (complete_) { + emit queryChanged(); + if (autoUpdate_) + update(); + } +} + +/*! + \qmlproperty RouteQuery QtLocation::RouteModel::query + + This property holds the data of the route request. + The primary data are the waypoint coordinates and possible further + preferences (means of traveling, things to avoid on route etc). +*/ + +QDeclarativeGeoRouteQuery *QDeclarativeGeoRouteModel::query() const +{ + return routeQuery_; +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::setAutoUpdate(bool autoUpdate) +{ + if (autoUpdate_ == autoUpdate) + return; + autoUpdate_ = autoUpdate; + if (complete_) + emit autoUpdateChanged(); +} + +/*! + \qmlproperty bool QtLocation::RouteModel::autoUpdate + + This property controls whether the Model automatically updates in response + to changes in its attached RouteQuery. The default value of this property + is false. + + If setting this value to 'true', note that any change at all in + the RouteQuery object set in the \l{query} property will trigger a new + request to be sent. If you are adjusting many properties of the RouteQuery + with autoUpdate enabled, this can generate large numbers of useless (and + later discarded) requests. +*/ + +bool QDeclarativeGeoRouteModel::autoUpdate() const +{ + return autoUpdate_; +} + +/*! + \qmlproperty Locale::MeasurementSystem QtLocation::RouteModel::measurementSystem + + This property holds the measurement system which will be used when calculating the route. This + property is changed when the \l {QtLocation::Plugin::locales}{Plugin::locales} property of + \l {QtLocation::RouteModel::plugin}{plugin} changes. + + If setting this property it must be set after the \l {QtLocation::RouteModel::plugin}{plugin} + property is set. +*/ +void QDeclarativeGeoRouteModel::setMeasurementSystem(QLocale::MeasurementSystem ms) +{ + if (!plugin_) + return; + + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + if (!serviceProvider) + return; + + QGeoRoutingManager *routingManager = serviceProvider->routingManager(); + if (!routingManager) + return; + + if (routingManager->measurementSystem() == ms) + return; + + routingManager->setMeasurementSystem(ms); + emit measurementSystemChanged(); +} + +QLocale::MeasurementSystem QDeclarativeGeoRouteModel::measurementSystem() const +{ + if (!plugin_) + return QLocale().measurementSystem(); + + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + if (!serviceProvider) { + if (plugin_->locales().isEmpty()) + return QLocale().measurementSystem(); + + return QLocale(plugin_->locales().first()).measurementSystem(); + } + + QGeoRoutingManager *routingManager = serviceProvider->routingManager(); + if (!routingManager) { + if (plugin_->locales().isEmpty()) + return QLocale().measurementSystem(); + + return QLocale(plugin_->locales().first()).measurementSystem(); + } + + return routingManager->measurementSystem(); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::setStatus(QDeclarativeGeoRouteModel::Status status) +{ + if (status_ == status) + return; + + status_ = status; + + if (complete_) + emit statusChanged(); +} + +/*! + \qmlproperty enumeration QtLocation::RouteModel::status + + This read-only property holds the current status of the model. + + \list + \li RouteModel.Null - No route requests have been issued or \l reset has been called. + \li RouteModel.Ready - Route request(s) have finished successfully. + \li RouteModel.Loading - Route request has been issued but not yet finished + \li RouteModel.Error - Routing error has occurred, details are in \l error and \l errorString + \endlist +*/ + +QDeclarativeGeoRouteModel::Status QDeclarativeGeoRouteModel::status() const +{ + return status_; +} + +/*! + \qmlproperty string QtLocation::RouteModel::errorString + + This read-only property holds the textual presentation of the latest routing error. + If no error has occurred or the model has been reset, an empty string is returned. + + An empty string may also be returned if an error occurred which has no associated + textual representation. +*/ + +QString QDeclarativeGeoRouteModel::errorString() const +{ + return errorString_; +} + +/*! + \qmlproperty enumeration QtLocation::RouteModel::error + + This read-only property holds the latest error value of the routing request. + + \list + \li RouteModel.NoError - No error has occurred. + \li RouteModel.CommunicationError - An error occurred while communicating with the service provider. + \li RouteModel.EngineNotSetError - The model's plugin property was not set or there is no routing manager associated with the plugin. + \li RouteModel.MissingRequiredParameterError - A required parameter was not specified. + \li RouteModel.ParseError - The response from the service provider was in an unrecognizable format. + \li RouteModel.UnknownError - An error occurred which does not fit into any of the other categories. + \li RouteModel.UnknownParameterError - The plugin did not recognize one of the parameters it was given. + \li RouteModel.UnsupportedOptionError - The requested operation is not supported by the routing provider. + This may happen when the loaded engine does not support a particular + type of routing request. + \endlist +*/ + +QDeclarativeGeoRouteModel::RouteError QDeclarativeGeoRouteModel::error() const +{ + return error_; +} + +void QDeclarativeGeoRouteModel::setError(RouteError error, const QString& errorString) +{ + if (error_ == error && errorString_ == errorString) + return; + error_ = error; + errorString_ = errorString; + emit errorChanged(); +} + +/*! + \qmlmethod void QtLocation::RouteModel::update() + + Instructs the RouteModel to update its data. This is most useful + when \l autoUpdate is disabled, to force a refresh when the query + has been changed. +*/ +void QDeclarativeGeoRouteModel::update() +{ + if (!complete_) + return; + + if (!plugin_) { + setError(EngineNotSetError, tr("Cannot route, plugin not set.")); + return; + } + + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + if (!serviceProvider) + return; + + QGeoRoutingManager *routingManager = serviceProvider->routingManager(); + if (!routingManager) { + setError(EngineNotSetError, tr("Cannot route, route manager not set.")); + return; + } + if (!routeQuery_) { + setError(ParseError,"Cannot route, valid query not set."); + return; + } + emit abortRequested(); // Clear previous requests + QGeoRouteRequest request = routeQuery_->routeRequest(); + if (request.waypoints().count() < 2) { + setError(ParseError,tr("Not enough waypoints for routing.")); + return; + } + + setError(NoError, QString()); + + QGeoRouteReply *reply = routingManager->calculateRoute(request); + setStatus(QDeclarativeGeoRouteModel::Loading); + if (!reply->isFinished()) { + connect(this, &QDeclarativeGeoRouteModel::abortRequested, reply, &QGeoRouteReply::abort); + } else { + if (reply->error() == QGeoRouteReply::NoError) { + routingFinished(reply); + } else { + routingError(reply, reply->error(), reply->errorString()); + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::routingFinished(QGeoRouteReply *reply) +{ + if (!reply) + return; + reply->deleteLater(); + if (reply->error() != QGeoRouteReply::NoError) + return; + + beginResetModel(); + int oldCount = routes_.count(); + qDeleteAll(routes_); + // Convert routes to declarative + routes_.clear(); + for (int i = 0; i < reply->routes().size(); ++i) { + QDeclarativeGeoRoute *route = new QDeclarativeGeoRoute(reply->routes().at(i), this); + QQmlEngine::setContextForObject(route, QQmlEngine::contextForObject(this)); + routes_.append(route); + } + endResetModel(); + + setError(NoError, QString()); + setStatus(QDeclarativeGeoRouteModel::Ready); + + if (oldCount != 0 || routes_.count() != 0) + emit routesChanged(); + if (oldCount != routes_.count()) + emit countChanged(); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::routingError(QGeoRouteReply *reply, + QGeoRouteReply::Error error, + const QString &errorString) +{ + if (!reply) + return; + reply->deleteLater(); + setError(static_cast<QDeclarativeGeoRouteModel::RouteError>(error), errorString); + setStatus(QDeclarativeGeoRouteModel::Error); +} + + +/*! + \qmltype RouteQuery + \instantiates QDeclarativeGeoRouteQuery + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since Qt Location 5.5 + + \brief The RouteQuery type is used to provide query parameters to a + RouteModel. + + A RouteQuery contains all the parameters necessary to make a request + to a routing service, which can then populate the contents of a RouteModel. + + These parameters describe key details of the route, such as \l waypoints to + pass through, \l excludedAreas to avoid, the \l travelModes in use, as well + as detailed preferences on how to optimize the route and what features + to prefer or avoid along the path (such as toll roads, highways, etc). + + RouteQuery objects are used exclusively to fill out the value of a + RouteModel's \l{RouteModel::query}{query} property, which can then begin + the retrieval process to populate the model. + + \section2 Example Usage + + The following snipped shows an incomplete example of creating a RouteQuery + object and setting it as the value of a RouteModel's \l{RouteModel::query}{query} + property. + + \code + RouteQuery { + id: aQuery + } + + RouteModel { + query: aQuery + autoUpdate: false + } + \endcode + + For a more complete example, see the documentation for the \l{RouteModel} + type, and the Mapviewer example. + + \sa RouteModel + +*/ + +QDeclarativeGeoRouteQuery::QDeclarativeGeoRouteQuery(QObject *parent) +: QObject(parent), complete_(false), m_excludedAreaCoordinateChanged(false) +{ +} + +QDeclarativeGeoRouteQuery::~QDeclarativeGeoRouteQuery() +{ +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteQuery::componentComplete() +{ + complete_ = true; +} + +/*! + \qmlproperty QList<FeatureType> RouteQuery::featureTypes + + List of features that will be considered when planning the + route. Features with a weight of NeutralFeatureWeight will not be returned. + + \list + \li RouteQuery.NoFeature - No features will be taken into account when planning the route + \li RouteQuery.TollFeature - Consider tollways when planning the route + \li RouteQuery.HighwayFeature - Consider highways when planning the route + \li RouteQuery.PublicTransitFeature - Consider public transit when planning the route + \li RouteQuery.FerryFeature - Consider ferries when planning the route + \li RouteQuery.TunnelFeature - Consider tunnels when planning the route + \li RouteQuery.DirtRoadFeature - Consider dirt roads when planning the route + \li RouteQuery.ParksFeature - Consider parks when planning the route + \li RouteQuery.MotorPoolLaneFeature - Consider motor pool lanes when planning the route + \endlist + + \sa setFeatureWeight, featureWeight +*/ + +QList<int> QDeclarativeGeoRouteQuery::featureTypes() +{ + QList<int> list; + + for (int i = 0; i < request_.featureTypes().count(); ++i) { + list.append(static_cast<int>(request_.featureTypes().at(i))); + } + return list; +} + +/*! + \qmlproperty int RouteQuery::numberAlternativeRoutes + + The number of alternative routes requested when requesting routes. + The default value is 0. +*/ + + +int QDeclarativeGeoRouteQuery::numberAlternativeRoutes() const +{ + return request_.numberAlternativeRoutes(); +} + +void QDeclarativeGeoRouteQuery::setNumberAlternativeRoutes(int numberAlternativeRoutes) +{ + if (numberAlternativeRoutes == request_.numberAlternativeRoutes()) + return; + + request_.setNumberAlternativeRoutes(numberAlternativeRoutes); + + if (complete_) { + emit numberAlternativeRoutesChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlproperty QJSValue RouteQuery::waypoints + + + The waypoint coordinates of the desired route. + The waypoints should be given in order from origin to destination. + Two or more coordinates are needed. + + Waypoints can be set as part of the RouteQuery type declaration or + dynamically with the functions provided. + + \sa addWaypoint, removeWaypoint, clearWaypoints +*/ + +QJSValue QDeclarativeGeoRouteQuery::waypoints() +{ + QQmlContext *context = QQmlEngine::contextForObject(parent()); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped<QV4::ArrayObject> waypointArray(scope, v4->newArrayObject(request_.waypoints().length())); + for (int i = 0; i < request_.waypoints().length(); ++i) { + const QGeoCoordinate &c = request_.waypoints().at(i); + + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c))); + waypointArray->putIndexed(i, cv); + } + + return QJSValue(v4, waypointArray.asReturnedValue()); +} + +void QDeclarativeGeoRouteQuery::setWaypoints(const QJSValue &value) +{ + if (!value.isArray()) + return; + + QList<QGeoCoordinate> waypointList; + quint32 length = value.property(QStringLiteral("length")).toUInt(); + for (quint32 i = 0; i < length; ++i) { + bool ok; + QGeoCoordinate c = parseCoordinate(value.property(i), &ok); + + if (!ok || !c.isValid()) { + qmlWarning(this) << "Unsupported waypoint type"; + return; + } + + waypointList.append(c); + } + + if (request_.waypoints() == waypointList) + return; + + request_.setWaypoints(waypointList); + + emit waypointsChanged(); + emit queryDetailsChanged(); +} + +/*! + \qmlproperty list<georectangle> RouteQuery::excludedAreas + + Areas that the route must not cross. + + Excluded areas can be set as part of the \l RouteQuery type declaration or + dynamically with the functions provided. + + \sa addExcludedArea, removeExcludedArea, clearExcludedAreas +*/ +QJSValue QDeclarativeGeoRouteQuery::excludedAreas() const +{ + QQmlContext *context = QQmlEngine::contextForObject(parent()); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped<QV4::ArrayObject> excludedAreasArray(scope, v4->newArrayObject(request_.excludeAreas().length())); + for (int i = 0; i < request_.excludeAreas().length(); ++i) { + const QGeoRectangle &r = request_.excludeAreas().at(i); + + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(r))); + excludedAreasArray->putIndexed(i, cv); + } + + return QJSValue(v4, excludedAreasArray.asReturnedValue()); +} + +void QDeclarativeGeoRouteQuery::setExcludedAreas(const QJSValue &value) +{ + if (!value.isArray()) + return; + + QList<QGeoRectangle> excludedAreasList; + quint32 length = value.property(QStringLiteral("length")).toUInt(); + for (quint32 i = 0; i < length; ++i) { + bool ok; + QGeoRectangle r = parseRectangle(value.property(i), &ok); + + if (!ok || !r.isValid()) { + qmlWarning(this) << "Unsupported area type"; + return; + } + + excludedAreasList.append(r); + } + + if (request_.excludeAreas() == excludedAreasList) + return; + + request_.setExcludeAreas(excludedAreasList); + + emit excludedAreasChanged(); + emit queryDetailsChanged(); +} + +/*! + \qmlmethod void QtLocation::RouteQuery::addExcludedArea(georectangle) + + Adds the given area to excluded areas (areas that the route must not cross). + Same area can only be added once. + + \sa removeExcludedArea, clearExcludedAreas +*/ + + +void QDeclarativeGeoRouteQuery::addExcludedArea(const QGeoRectangle &area) +{ + if (!area.isValid()) + return; + + QList<QGeoRectangle> excludedAreas = request_.excludeAreas(); + + if (excludedAreas.contains(area)) + return; + + excludedAreas.append(area); + + request_.setExcludeAreas(excludedAreas); + + if (complete_) { + emit excludedAreasChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod void QtLocation::RouteQuery::removeExcludedArea(georectangle) + + Removes the given area to excluded areas (areas that the route must not cross). + + \sa addExcludedArea, clearExcludedAreas +*/ + +void QDeclarativeGeoRouteQuery::removeExcludedArea(const QGeoRectangle &area) +{ + if (!area.isValid()) + return; + + QList<QGeoRectangle> excludedAreas = request_.excludeAreas(); + + int index = excludedAreas.lastIndexOf(area); + if (index == -1) { + qmlWarning(this) << QStringLiteral("Cannot remove nonexistent area."); + return; + } + excludedAreas.removeAt(index); + request_.setExcludeAreas(excludedAreas); + + emit excludedAreasChanged(); + emit queryDetailsChanged(); +} + +/*! + \qmlmethod void QtLocation::RouteQuery::clearExcludedAreas() + + Clears all excluded areas (areas that the route must not cross). + + \sa addExcludedArea, removeExcludedArea +*/ + +void QDeclarativeGeoRouteQuery::clearExcludedAreas() +{ + if (request_.excludeAreas().isEmpty()) + return; + + request_.setExcludeAreas(QList<QGeoRectangle>()); + + emit excludedAreasChanged(); + emit queryDetailsChanged(); +} + +/*! + \qmlmethod void QtLocation::RouteQuery::addWaypoint(coordinate) + + Appends a coordinate to the list of waypoints. Same coordinate + can be set multiple times. + + \sa removeWaypoint, clearWaypoints +*/ +void QDeclarativeGeoRouteQuery::addWaypoint(const QGeoCoordinate &waypoint) +{ + if (!waypoint.isValid()) { + qmlWarning(this) << QStringLiteral("Not adding invalid waypoint."); + return; + } + + QList<QGeoCoordinate> waypoints = request_.waypoints(); + waypoints.append(waypoint); + request_.setWaypoints(waypoints); + + if (complete_) { + emit waypointsChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod void QtLocation::RouteQuery::removeWaypoint(coordinate) + + Removes the given from the list of waypoints. In case same coordinate + appears multiple times, the most recently added coordinate instance is + removed. + + \sa addWaypoint, clearWaypoints +*/ +void QDeclarativeGeoRouteQuery::removeWaypoint(const QGeoCoordinate &waypoint) +{ + QList<QGeoCoordinate> waypoints = request_.waypoints(); + + int index = waypoints.lastIndexOf(waypoint); + if (index == -1) { + qmlWarning(this) << QStringLiteral("Cannot remove nonexistent waypoint."); + return; + } + + waypoints.removeAt(index); + + request_.setWaypoints(waypoints); + + emit waypointsChanged(); + emit queryDetailsChanged(); +} + +/*! + \qmlmethod void QtLocation::RouteQuery::clearWaypoints() + + Clears all waypoints. + + \sa removeWaypoint, addWaypoint +*/ +void QDeclarativeGeoRouteQuery::clearWaypoints() +{ + if (request_.waypoints().isEmpty()) + return; + + request_.setWaypoints(QList<QGeoCoordinate>()); + + emit waypointsChanged(); + emit queryDetailsChanged(); +} + +/*! + \qmlmethod void QtLocation::RouteQuery::setFeatureWeight(FeatureType, FeatureWeight) + + Defines the weight to associate with a feature during the planning of a + route. + + Following lists the possible feature weights: + + \list + \li RouteQuery.NeutralFeatureWeight - The presence or absence of the feature will not affect the planning of the route + \li RouteQuery.PreferFeatureWeight - Routes which contain the feature will be preferred over those that do not + \li RouteQuery.RequireFeatureWeight - Only routes which contain the feature will be considered, otherwise no route will be returned + \li RouteQuery.AvoidFeatureWeight - Routes which do not contain the feature will be preferred over those that do + \li RouteQuery.DisallowFeatureWeight - Only routes which do not contain the feature will be considered, otherwise no route will be returned + \endlist + + \sa featureTypes, resetFeatureWeights, featureWeight + +*/ + +void QDeclarativeGeoRouteQuery::setFeatureWeight(FeatureType featureType, FeatureWeight featureWeight) +{ + if (featureType == NoFeature && !request_.featureTypes().isEmpty()) { + resetFeatureWeights(); + return; + } + + // Check if the weight changes, as we need to signal it + FeatureWeight originalWeight = static_cast<FeatureWeight>(request_.featureWeight(static_cast<QGeoRouteRequest::FeatureType>(featureType))); + if (featureWeight == originalWeight) + return; + + request_.setFeatureWeight(static_cast<QGeoRouteRequest::FeatureType>(featureType), + static_cast<QGeoRouteRequest::FeatureWeight>(featureWeight)); + if (complete_ && ((originalWeight == NeutralFeatureWeight) || (featureWeight == NeutralFeatureWeight))) { + // featureTypes should now give a different list, because the original and new weight + // were not same, and other one was neutral weight + emit featureTypesChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod void QtLocation::RouteQuery::resetFeatureWeights() + + Resets all feature weights to their default state (NeutralFeatureWeight). + + \sa featureTypes, setFeatureWeight, featureWeight +*/ +void QDeclarativeGeoRouteQuery::resetFeatureWeights() +{ + // reset all feature types. + QList<QGeoRouteRequest::FeatureType> featureTypes = request_.featureTypes(); + for (int i = 0; i < featureTypes.count(); ++i) { + request_.setFeatureWeight(featureTypes.at(i), QGeoRouteRequest::NeutralFeatureWeight); + } + if (complete_) { + emit featureTypesChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod FeatureWeight QtLocation::RouteQuery::featureWeight(FeatureType featureType) + + Gets the weight for the \a featureType. + + \sa featureTypes, setFeatureWeight, resetFeatureWeights +*/ + +int QDeclarativeGeoRouteQuery::featureWeight(FeatureType featureType) +{ + return request_.featureWeight(static_cast<QGeoRouteRequest::FeatureType>(featureType)); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteQuery::setTravelModes(QDeclarativeGeoRouteQuery::TravelModes travelModes) +{ + QGeoRouteRequest::TravelModes reqTravelModes; + + if (travelModes & QDeclarativeGeoRouteQuery::CarTravel) + reqTravelModes |= QGeoRouteRequest::CarTravel; + if (travelModes & QDeclarativeGeoRouteQuery::PedestrianTravel) + reqTravelModes |= QGeoRouteRequest::PedestrianTravel; + if (travelModes & QDeclarativeGeoRouteQuery::BicycleTravel) + reqTravelModes |= QGeoRouteRequest::BicycleTravel; + if (travelModes & QDeclarativeGeoRouteQuery::PublicTransitTravel) + reqTravelModes |= QGeoRouteRequest::PublicTransitTravel; + if (travelModes & QDeclarativeGeoRouteQuery::TruckTravel) + reqTravelModes |= QGeoRouteRequest::TruckTravel; + + if (reqTravelModes == request_.travelModes()) + return; + + request_.setTravelModes(reqTravelModes); + + if (complete_) { + emit travelModesChanged(); + emit queryDetailsChanged(); + } +} + + +/*! + \qmlproperty enumeration RouteQuery::segmentDetail + + The level of detail which will be used in the representation of routing segments. + + \list + \li RouteQuery.NoSegmentData - No segment data should be included with the route + \li RouteQuery.BasicSegmentData - Basic segment data will be included with the route + \endlist + + The default value is RouteQuery.BasicSegmentData +*/ + +void QDeclarativeGeoRouteQuery::setSegmentDetail(SegmentDetail segmentDetail) +{ + if (static_cast<QGeoRouteRequest::SegmentDetail>(segmentDetail) == request_.segmentDetail()) + return; + request_.setSegmentDetail(static_cast<QGeoRouteRequest::SegmentDetail>(segmentDetail)); + if (complete_) { + emit segmentDetailChanged(); + emit queryDetailsChanged(); + } +} + +QDeclarativeGeoRouteQuery::SegmentDetail QDeclarativeGeoRouteQuery::segmentDetail() const +{ + return static_cast<QDeclarativeGeoRouteQuery::SegmentDetail>(request_.segmentDetail()); +} + +/*! + \qmlproperty enumeration RouteQuery::maneuverDetail + + The level of detail which will be used in the representation of routing maneuvers. + + \list + \li RouteQuery.NoManeuvers - No maneuvers should be included with the route + \li RouteQuery.BasicManeuvers - Basic maneuvers will be included with the route + \endlist + + The default value is RouteQuery.BasicManeuvers +*/ + +void QDeclarativeGeoRouteQuery::setManeuverDetail(ManeuverDetail maneuverDetail) +{ + if (static_cast<QGeoRouteRequest::ManeuverDetail>(maneuverDetail) == request_.maneuverDetail()) + return; + request_.setManeuverDetail(static_cast<QGeoRouteRequest::ManeuverDetail>(maneuverDetail)); + if (complete_) { + emit maneuverDetailChanged(); + emit queryDetailsChanged(); + } +} + +QDeclarativeGeoRouteQuery::ManeuverDetail QDeclarativeGeoRouteQuery::maneuverDetail() const +{ + return static_cast<QDeclarativeGeoRouteQuery::ManeuverDetail>(request_.maneuverDetail()); +} + +/*! + \qmlproperty enumeration RouteQuery::travelModes + + The travel modes which should be considered during the planning of the route. + Values can be combined with OR ('|') -operator. + + \list + \li RouteQuery.CarTravel - The route will be optimized for someone who is driving a car + \li RouteQuery.PedestrianTravel - The route will be optimized for someone who is walking + \li RouteQuery.BicycleTravel - The route will be optimized for someone who is riding a bicycle + \li RouteQuery.PublicTransitTravel - The route will be optimized for someone who is making use of public transit + \li RouteQuery.TruckTravel - The route will be optimized for someone who is driving a truck + \endlist + + The default value is RouteQuery.CarTravel +*/ + +QDeclarativeGeoRouteQuery::TravelModes QDeclarativeGeoRouteQuery::travelModes() const +{ + QGeoRouteRequest::TravelModes reqTravelModes = request_.travelModes(); + QDeclarativeGeoRouteQuery::TravelModes travelModes; + + if (reqTravelModes & QGeoRouteRequest::CarTravel) + travelModes |= QDeclarativeGeoRouteQuery::CarTravel; + if (reqTravelModes & QGeoRouteRequest::PedestrianTravel) + travelModes |= QDeclarativeGeoRouteQuery::PedestrianTravel; + if (reqTravelModes & QGeoRouteRequest::BicycleTravel) + travelModes |= QDeclarativeGeoRouteQuery::BicycleTravel; + if (reqTravelModes & QGeoRouteRequest::PublicTransitTravel) + travelModes |= QDeclarativeGeoRouteQuery::PublicTransitTravel; + if (reqTravelModes & QGeoRouteRequest::TruckTravel) + travelModes |= QDeclarativeGeoRouteQuery::TruckTravel; + + return travelModes; +} + +/*! + \qmlproperty enumeration RouteQuery::routeOptimizations + + The route optimizations which should be considered during the planning of the route. + Values can be combined with OR ('|') -operator. + + \list + \li RouteQuery.ShortestRoute - Minimize the length of the journey + \li RouteQuery.FastestRoute - Minimize the traveling time for the journey + \li RouteQuery.MostEconomicRoute - Minimize the cost of the journey + \li RouteQuery.MostScenicRoute - Maximize the scenic potential of the journey + \endlist + + The default value is RouteQuery.FastestRoute +*/ + +QDeclarativeGeoRouteQuery::RouteOptimizations QDeclarativeGeoRouteQuery::routeOptimizations() const +{ + QGeoRouteRequest::RouteOptimizations reqOptimizations = request_.routeOptimization(); + QDeclarativeGeoRouteQuery::RouteOptimizations optimization; + + if (reqOptimizations & QGeoRouteRequest::ShortestRoute) + optimization |= QDeclarativeGeoRouteQuery::ShortestRoute; + if (reqOptimizations & QGeoRouteRequest::FastestRoute) + optimization |= QDeclarativeGeoRouteQuery::FastestRoute; + if (reqOptimizations & QGeoRouteRequest::MostEconomicRoute) + optimization |= QDeclarativeGeoRouteQuery::MostEconomicRoute; + if (reqOptimizations & QGeoRouteRequest::MostScenicRoute) + optimization |= QDeclarativeGeoRouteQuery::MostScenicRoute; + + return optimization; +} + +void QDeclarativeGeoRouteQuery::setRouteOptimizations(QDeclarativeGeoRouteQuery::RouteOptimizations optimization) +{ + QGeoRouteRequest::RouteOptimizations reqOptimizations; + + if (optimization & QDeclarativeGeoRouteQuery::ShortestRoute) + reqOptimizations |= QGeoRouteRequest::ShortestRoute; + if (optimization & QDeclarativeGeoRouteQuery::FastestRoute) + reqOptimizations |= QGeoRouteRequest::FastestRoute; + if (optimization & QDeclarativeGeoRouteQuery::MostEconomicRoute) + reqOptimizations |= QGeoRouteRequest::MostEconomicRoute; + if (optimization & QDeclarativeGeoRouteQuery::MostScenicRoute) + reqOptimizations |= QGeoRouteRequest::MostScenicRoute; + + if (reqOptimizations == request_.routeOptimization()) + return; + + request_.setRouteOptimization(reqOptimizations); + + if (complete_) { + emit routeOptimizationsChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \internal +*/ +QGeoRouteRequest QDeclarativeGeoRouteQuery::routeRequest() const +{ + return request_; +} + +void QDeclarativeGeoRouteQuery::excludedAreaCoordinateChanged() +{ + if (!m_excludedAreaCoordinateChanged) { + m_excludedAreaCoordinateChanged = true; + QMetaObject::invokeMethod(this, "doCoordinateChanged", Qt::QueuedConnection); + } +} + +void QDeclarativeGeoRouteQuery::doCoordinateChanged() +{ + m_excludedAreaCoordinateChanged = false; + emit queryDetailsChanged(); +} + +#include "moc_qdeclarativegeoroutemodel_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeoroutemodel_p.h b/src/location/declarativemaps/qdeclarativegeoroutemodel_p.h new file mode 100644 index 00000000..3dfd2ce6 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroutemodel_p.h @@ -0,0 +1,344 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOROUTEMODEL_H +#define QDECLARATIVEGEOROUTEMODEL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeoserviceprovider_p.h> + +#include <QtPositioning/QGeoCoordinate> +#include <QtPositioning/QGeoRectangle> + +#include <qgeorouterequest.h> +#include <qgeoroutereply.h> + +#include <QtQml/qqml.h> +#include <QtQml/QQmlParserStatus> +#include <QtQml/private/qv4engine_p.h> +#include <QAbstractListModel> + +#include <QObject> + +QT_BEGIN_NAMESPACE + +class QGeoServiceProvider; +class QGeoRoutingManager; +class QDeclarativeGeoRoute; +class QDeclarativeGeoRouteQuery; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoRouteModel : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + Q_ENUMS(Status) + Q_ENUMS(RouteError) + + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(QDeclarativeGeoRouteQuery *query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(bool autoUpdate READ autoUpdate WRITE setAutoUpdate NOTIFY autoUpdateChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) + Q_PROPERTY(RouteError error READ error NOTIFY errorChanged) + Q_PROPERTY(QLocale::MeasurementSystem measurementSystem READ measurementSystem WRITE setMeasurementSystem NOTIFY measurementSystemChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + enum Roles { + RouteRole = Qt::UserRole + 500 + }; + + enum Status { + Null, + Ready, + Loading, + Error + }; + + enum RouteError { + NoError = QGeoRouteReply::NoError, + EngineNotSetError = QGeoRouteReply::EngineNotSetError, + CommunicationError = QGeoRouteReply::CommunicationError, + ParseError = QGeoRouteReply::ParseError, + UnsupportedOptionError = QGeoRouteReply::UnsupportedOptionError, + UnknownError = QGeoRouteReply::UnknownError, + //we leave gap for future QGeoRouteReply errors + + //QGeoServiceProvider related errors start here + UnknownParameterError = 100, + MissingRequiredParameterError + }; + + explicit QDeclarativeGeoRouteModel(QObject *parent = 0); + ~QDeclarativeGeoRouteModel(); + + // From QQmlParserStatus + void classBegin() {} + void componentComplete(); + + // From QAbstractListModel + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + virtual QHash<int,QByteArray> roleNames() const; + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + void setQuery(QDeclarativeGeoRouteQuery *query); + QDeclarativeGeoRouteQuery *query() const; + + void setAutoUpdate(bool autoUpdate); + bool autoUpdate() const; + + void setMeasurementSystem(QLocale::MeasurementSystem ms); + QLocale::MeasurementSystem measurementSystem() const; + + Status status() const; + QString errorString() const; + RouteError error() const; + + int count() const; + Q_INVOKABLE QDeclarativeGeoRoute *get(int index); + Q_INVOKABLE void reset(); + Q_INVOKABLE void cancel(); + +Q_SIGNALS: + void countChanged(); + void pluginChanged(); + void queryChanged(); + void autoUpdateChanged(); + void statusChanged(); + void errorChanged(); //emitted also for errorString notification + void routesChanged(); + void measurementSystemChanged(); + void abortRequested(); + +public Q_SLOTS: + void update(); + +private Q_SLOTS: + void routingFinished(QGeoRouteReply *reply); + void routingError(QGeoRouteReply *reply, + QGeoRouteReply::Error error, + const QString &errorString); + void queryDetailsChanged(); + void pluginReady(); + +private: + void setStatus(Status status); + void setError(RouteError error, const QString &errorString); + + bool complete_; + + QDeclarativeGeoServiceProvider *plugin_; + QDeclarativeGeoRouteQuery *routeQuery_; + + QList<QDeclarativeGeoRoute *> routes_; + bool autoUpdate_; + Status status_; + QString errorString_; + RouteError error_; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoRouteQuery : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_ENUMS(TravelMode) + Q_ENUMS(FeatureType) + Q_ENUMS(FeatureWeight) + Q_ENUMS(SegmentDetail) + Q_ENUMS(ManeuverDetail) + Q_ENUMS(RouteOptimization) + Q_FLAGS(RouteOptimizations) + Q_FLAGS(ManeuverDetails) + Q_FLAGS(SegmentDetails) + Q_FLAGS(TravelModes) + + Q_PROPERTY(int numberAlternativeRoutes READ numberAlternativeRoutes WRITE setNumberAlternativeRoutes NOTIFY numberAlternativeRoutesChanged) + Q_PROPERTY(TravelModes travelModes READ travelModes WRITE setTravelModes NOTIFY travelModesChanged) + Q_PROPERTY(RouteOptimizations routeOptimizations READ routeOptimizations WRITE setRouteOptimizations NOTIFY routeOptimizationsChanged) + Q_PROPERTY(SegmentDetail segmentDetail READ segmentDetail WRITE setSegmentDetail NOTIFY segmentDetailChanged) + Q_PROPERTY(ManeuverDetail maneuverDetail READ maneuverDetail WRITE setManeuverDetail NOTIFY maneuverDetailChanged) + Q_PROPERTY(QJSValue waypoints READ waypoints WRITE setWaypoints NOTIFY waypointsChanged) + Q_PROPERTY(QJSValue excludedAreas READ excludedAreas WRITE setExcludedAreas NOTIFY excludedAreasChanged) + Q_PROPERTY(QList<int> featureTypes READ featureTypes NOTIFY featureTypesChanged) + Q_INTERFACES(QQmlParserStatus) + +public: + + explicit QDeclarativeGeoRouteQuery(QObject *parent = 0); + ~QDeclarativeGeoRouteQuery(); + + // From QQmlParserStatus + void classBegin() {} + void componentComplete(); + + QGeoRouteRequest routeRequest() const; + + enum TravelMode { + CarTravel = QGeoRouteRequest::CarTravel, + PedestrianTravel = QGeoRouteRequest::PedestrianTravel, + BicycleTravel = QGeoRouteRequest::BicycleTravel, + PublicTransitTravel = QGeoRouteRequest::PublicTransitTravel, + TruckTravel = QGeoRouteRequest::TruckTravel + }; + Q_DECLARE_FLAGS(TravelModes, TravelMode) + + enum FeatureType { + NoFeature = QGeoRouteRequest::NoFeature, + TollFeature = QGeoRouteRequest::TollFeature, + HighwayFeature = QGeoRouteRequest::HighwayFeature, + PublicTransitFeature = QGeoRouteRequest::PublicTransitFeature, + FerryFeature = QGeoRouteRequest::FerryFeature, + TunnelFeature = QGeoRouteRequest::TunnelFeature, + DirtRoadFeature = QGeoRouteRequest::DirtRoadFeature, + ParksFeature = QGeoRouteRequest::ParksFeature, + MotorPoolLaneFeature = QGeoRouteRequest::MotorPoolLaneFeature + }; + Q_DECLARE_FLAGS(FeatureTypes, FeatureType) + + enum FeatureWeight { + NeutralFeatureWeight = QGeoRouteRequest::NeutralFeatureWeight, + PreferFeatureWeight = QGeoRouteRequest::PreferFeatureWeight, + RequireFeatureWeight = QGeoRouteRequest::RequireFeatureWeight, + AvoidFeatureWeight = QGeoRouteRequest::AvoidFeatureWeight, + DisallowFeatureWeight = QGeoRouteRequest::DisallowFeatureWeight + }; + Q_DECLARE_FLAGS(FeatureWeights, FeatureWeight) + + enum RouteOptimization { + ShortestRoute = QGeoRouteRequest::ShortestRoute, + FastestRoute = QGeoRouteRequest::FastestRoute, + MostEconomicRoute = QGeoRouteRequest::MostEconomicRoute, + MostScenicRoute = QGeoRouteRequest::MostScenicRoute + }; + Q_DECLARE_FLAGS(RouteOptimizations, RouteOptimization) + + enum SegmentDetail { + NoSegmentData = 0x0000, + BasicSegmentData = 0x0001 + }; + Q_DECLARE_FLAGS(SegmentDetails, SegmentDetail) + + enum ManeuverDetail { + NoManeuvers = 0x0000, + BasicManeuvers = 0x0001 + }; + Q_DECLARE_FLAGS(ManeuverDetails, ManeuverDetail) + + void setNumberAlternativeRoutes(int numberAlternativeRoutes); + int numberAlternativeRoutes() const; + + //QList<FeatureType> featureTypes(); + QList<int> featureTypes(); + + + QJSValue waypoints(); + void setWaypoints(const QJSValue &value); + + // READ functions for list properties + QJSValue excludedAreas() const; + void setExcludedAreas(const QJSValue &value); + + Q_INVOKABLE void addWaypoint(const QGeoCoordinate &waypoint); + Q_INVOKABLE void removeWaypoint(const QGeoCoordinate &waypoint); + Q_INVOKABLE void clearWaypoints(); + + Q_INVOKABLE void addExcludedArea(const QGeoRectangle &area); + Q_INVOKABLE void removeExcludedArea(const QGeoRectangle &area); + Q_INVOKABLE void clearExcludedAreas(); + + Q_INVOKABLE void setFeatureWeight(FeatureType featureType, FeatureWeight featureWeight); + Q_INVOKABLE int featureWeight(FeatureType featureType); + Q_INVOKABLE void resetFeatureWeights(); + + /* + feature weights + */ + + void setTravelModes(TravelModes travelModes); + TravelModes travelModes() const; + + void setSegmentDetail(SegmentDetail segmentDetail); + SegmentDetail segmentDetail() const; + + void setManeuverDetail(ManeuverDetail maneuverDetail); + ManeuverDetail maneuverDetail() const; + + void setRouteOptimizations(RouteOptimizations optimization); + RouteOptimizations routeOptimizations() const; + +Q_SIGNALS: + void numberAlternativeRoutesChanged(); + void travelModesChanged(); + void routeOptimizationsChanged(); + + void waypointsChanged(); + void excludedAreasChanged(); + + void featureTypesChanged(); + void maneuverDetailChanged(); + void segmentDetailChanged(); + + void queryDetailsChanged(); + +private Q_SLOTS: + void excludedAreaCoordinateChanged(); + +private: + Q_INVOKABLE void doCoordinateChanged(); + + QGeoRouteRequest request_; + bool complete_; + bool m_excludedAreaCoordinateChanged; + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp b/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp new file mode 100644 index 00000000..acfe2441 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** 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 "qdeclarativegeoroutesegment_p.h" + +#include <QtQml/QQmlEngine> +#include <QtQml/private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype RouteSegment + \instantiates QDeclarativeGeoRouteSegment + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since Qt Location 5.5 + + \brief The RouteSegment type represents a segment of a Route. + + A RouteSegment instance has information about the physical layout + of the route segment, the length of the route and estimated time required + to traverse the route segment and optional RouteManeuvers associated with + the end of the route segment. + + RouteSegment instances can be thought of as edges on a routing + graph, with RouteManeuver instances as optional labels attached to the + vertices of the graph. + + The primary means of acquiring Route objects is via Routes via \l RouteModel. + + \section1 Example + + The following QML snippet demonstrates how to print information about a + route segment: + + \snippet declarative/routing.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/routing.qml RouteSegment +*/ + +QDeclarativeGeoRouteSegment::QDeclarativeGeoRouteSegment(QObject *parent) + : QObject(parent) +{ + maneuver_ = new QDeclarativeGeoManeuver(this); +} + +QDeclarativeGeoRouteSegment::QDeclarativeGeoRouteSegment(const QGeoRouteSegment &segment, + QObject *parent) + : QObject(parent), + segment_(segment) +{ + maneuver_ = new QDeclarativeGeoManeuver(segment_.maneuver(), this); +} + +QDeclarativeGeoRouteSegment::~QDeclarativeGeoRouteSegment() {} + +/*! + \qmlproperty int QtLocation::RouteSegment::travelTime + + Read-only property which holds the estimated amount of time it will take to + traverse this segment, in seconds. + +*/ + +int QDeclarativeGeoRouteSegment::travelTime() const +{ + return segment_.travelTime(); +} + +/*! + \qmlproperty real QtLocation::RouteSegment::distance + + Read-only property which holds the distance covered by this segment of the route, in meters. + +*/ + +qreal QDeclarativeGeoRouteSegment::distance() const +{ + return segment_.distance(); +} + +/*! + \qmlproperty RouteManeuver QtLocation::RouteSegment::maneuver + + Read-only property which holds the maneuver for this route segment. + + Will return invalid maneuver if no information has been attached to the endpoint + of this route segment. +*/ + +QDeclarativeGeoManeuver *QDeclarativeGeoRouteSegment::maneuver() const +{ + return maneuver_; +} + +/*! + \qmlproperty QJSValue QtLocation::RouteSegment::path + + Read-only property which holds the geographical coordinates of this segment. + Coordinates are listed in the order in which they would be traversed by someone + traveling along this segment of the route. + + To access individual segments you can use standard list accessors: 'path.length' + indicates the number of objects and 'path[index starting from zero]' gives + the actual object. + + \sa QtPositioning::coordinate +*/ + +QJSValue QDeclarativeGeoRouteSegment::path() const +{ + QQmlContext *context = QQmlEngine::contextForObject(parent()); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped<QV4::ArrayObject> pathArray(scope, v4->newArrayObject(segment_.path().length())); + for (int i = 0; i < segment_.path().length(); ++i) { + const QGeoCoordinate &c = segment_.path().at(i); + + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c))); + pathArray->putIndexed(i, cv); + } + + return QJSValue(v4, pathArray.asReturnedValue()); +} + +#include "moc_qdeclarativegeoroutesegment_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeoroutesegment_p.h b/src/location/declarativemaps/qdeclarativegeoroutesegment_p.h new file mode 100644 index 00000000..c3203ef0 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroutesegment_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOROUTESEGMENT_H +#define QDECLARATIVEGEOROUTESEGMENT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeomaneuver_p.h> + +#include <QtCore/QObject> +#include <QtQml/qjsvalue.h> +#include <QtLocation/QGeoRouteSegment> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoRouteSegment : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int travelTime READ travelTime CONSTANT) + Q_PROPERTY(qreal distance READ distance CONSTANT) + Q_PROPERTY(QJSValue path READ path CONSTANT) + Q_PROPERTY(QDeclarativeGeoManeuver *maneuver READ maneuver CONSTANT) + +public: + explicit QDeclarativeGeoRouteSegment(QObject *parent = 0); + QDeclarativeGeoRouteSegment(const QGeoRouteSegment &segment, QObject *parent = 0); + ~QDeclarativeGeoRouteSegment(); + + int travelTime() const; + qreal distance() const; + QJSValue path() const; + QDeclarativeGeoManeuver *maneuver() const; + +private: + QGeoRouteSegment segment_; + QDeclarativeGeoManeuver *maneuver_; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeoserviceprovider.cpp b/src/location/declarativemaps/qdeclarativegeoserviceprovider.cpp new file mode 100644 index 00000000..994b4913 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoserviceprovider.cpp @@ -0,0 +1,821 @@ +/**************************************************************************** +** +** 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 "qdeclarativegeoserviceprovider_p.h" +#include <QtQml/QQmlInfo> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Plugin + \instantiates QDeclarativeGeoServiceProvider + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-common + \since Qt Location 5.5 + + \brief The Plugin type describes a Location based services plugin. + + The Plugin type is used to declaratively specify which available + GeoServices plugin should be used for various tasks in the Location API. + Plugins are used by \l Map, \l RouteModel, and \l GeocodeModel + types, as well as a variety of others. + + Plugins recognized by the system have a \l name property, a simple string + normally indicating the name of the service that the Plugin retrieves + data from. They also have a variety of features, which can be test for using the + \l {supportsRouting()}, \l {supportsGeocoding()}, \l {supportsMapping()} and + \l {supportsPlaces()} methods. + + When a Plugin object is created, it is "detached" and not associated with + any actual service plugin. Once it has received information via setting + its \l name, \l preferred, or \l required properties, it will choose an + appropriate service plugin to attach to. Plugin objects can only be + attached once; to use multiple plugins, create multiple Plugin objects. + + \section2 Example Usage + + The following snippet shows a Plugin object being created with the + \l required and \l preferred properties set. This Plugin will attach to the + first found plugin that supports both mapping and geocoding, and will + prefer plugins named "here" or "osm" to any others. + + \code + Plugin { + id: plugin + preferred: ["here", "osm"] + required: Plugin.AnyMappingFeatures | Plugin.AnyGeocodingFeatures + } + \endcode +*/ + +QDeclarativeGeoServiceProvider::QDeclarativeGeoServiceProvider(QObject *parent) +: QObject(parent), + sharedProvider_(0), + required_(new QDeclarativeGeoServiceProviderRequirements), + complete_(false), + experimental_(false) +{ + locales_.append(QLocale().name()); +} + +QDeclarativeGeoServiceProvider::~QDeclarativeGeoServiceProvider() +{ + delete required_; + delete sharedProvider_; +} + + + +/*! + \qmlproperty string Plugin::name + + This property holds the name of the plugin. Setting this property + will cause the Plugin to only attach to a plugin with exactly this + name. The value of \l required will be ignored. +*/ +void QDeclarativeGeoServiceProvider::setName(const QString &name) +{ + if (name_ == name) + return; + + name_ = name; + delete sharedProvider_; + sharedProvider_ = new QGeoServiceProvider(name_, parameterMap()); + sharedProvider_->setLocale(locales_.at(0)); + sharedProvider_->setAllowExperimental(experimental_); + + emit nameChanged(name_); + emit attached(); +} + +QString QDeclarativeGeoServiceProvider::name() const +{ + return name_; +} + + +/*! + \qmlproperty stringlist Plugin::availableServiceProviders + + This property holds a list of all available service plugins' names. This + can be used to manually enumerate the available plugins if the + control provided by \l name and \l required is not sufficient for your + needs. +*/ +QStringList QDeclarativeGeoServiceProvider::availableServiceProviders() +{ + return QGeoServiceProvider::availableServiceProviders(); +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProvider::componentComplete() +{ + complete_ = true; + if (!name_.isEmpty()) { + return; + } + + if (!prefer_.isEmpty() + || required_->mappingRequirements() != NoMappingFeatures + || required_->routingRequirements() != NoRoutingFeatures + || required_->geocodingRequirements() != NoGeocodingFeatures + || required_->placesRequirements() != NoPlacesFeatures) { + + QStringList providers = QGeoServiceProvider::availableServiceProviders(); + + /* first check any preferred plugins */ + foreach (const QString &name, prefer_) { + if (providers.contains(name)) { + // so we don't try it again later + providers.removeAll(name); + + QGeoServiceProvider sp(name, parameterMap(), experimental_); + if (required_->matches(&sp)) { + setName(name); + return; + } + } + } + + /* then try the rest */ + foreach (const QString &name, providers) { + QGeoServiceProvider sp(name, parameterMap(), experimental_); + if (required_->matches(&sp)) { + setName(name); + return; + } + } + + qmlWarning(this) << "Could not find a plugin with the required features to attach to"; + } +} + +/*! + \qmlmethod bool Plugin::supportsGeocoding(GeocodingFeatures features) + + This method returns a boolean indicating whether the specified set of \a features are supported + by the geo service provider plugin. True is returned if all specified \a features are + supported; otherwise false is returned. + + The \a features parameter can be any flag combination of: + \table + \header + \li Feature + \li Description + \row + \li Plugin.NoGeocodingFeatures + \li No geocoding features are supported. + \row + \li Plugin.OnlineGeocodingFeature + \li Online geocoding is supported. + \row + \li Plugin.OfflineGeocodingFeature + \li Offline geocoding is supported. + \row + \li Plugin.ReverseGeocodingFeature + \li Reverse geocoding is supported. + \row + \li Plugin.LocalizedGeocodingFeature + \li Supports returning geocoding results with localized addresses. + \row + \li Plugin.AnyGeocodingFeatures + \li Matches a geo service provider that provides any geocoding features. + \endtable +*/ +bool QDeclarativeGeoServiceProvider::supportsGeocoding(const GeocodingFeatures &feature) const +{ + QGeoServiceProvider *sp = sharedGeoServiceProvider(); + QGeoServiceProvider::GeocodingFeatures f = + static_cast<QGeoServiceProvider::GeocodingFeature>(int(feature)); + if (f == QGeoServiceProvider::AnyGeocodingFeatures) + return (sp && (sp->geocodingFeatures() != QGeoServiceProvider::NoGeocodingFeatures)); + else + return (sp && (sp->geocodingFeatures() & f) == f); +} + +/*! + \qmlmethod bool Plugin::supportsMapping(MappingFeatures features) + + This method returns a boolean indicating whether the specified set of \a features are supported + by the geo service provider plugin. True is returned if all specified \a features are + supported; otherwise false is returned. + + The \a features parameter can be any flag combination of: + \table + \header + \li Feature + \li Description + \row + \li Plugin.NoMappingFeatures + \li No mapping features are supported. + \row + \li Plugin.OnlineMappingFeature + \li Online mapping is supported. + \row + \li Plugin.OfflineMappingFeature + \li Offline mapping is supported. + \row + \li Plugin.LocalizedMappingFeature + \li Supports returning localized map data. + \row + \li Plugin.AnyMappingFeatures + \li Matches a geo service provider that provides any mapping features. + \endtable +*/ +bool QDeclarativeGeoServiceProvider::supportsMapping(const MappingFeatures &feature) const +{ + QGeoServiceProvider *sp = sharedGeoServiceProvider(); + QGeoServiceProvider::MappingFeatures f = + static_cast<QGeoServiceProvider::MappingFeature>(int(feature)); + if (f == QGeoServiceProvider::AnyMappingFeatures) + return (sp && (sp->mappingFeatures() != QGeoServiceProvider::NoMappingFeatures)); + else + return (sp && (sp->mappingFeatures() & f) == f); +} + +/*! + \qmlmethod bool Plugin::supportsRouting(RoutingFeatures features) + + This method returns a boolean indicating whether the specified set of \a features are supported + by the geo service provider plugin. True is returned if all specified \a features are + supported; otherwise false is returned. + + The \a features parameter can be any flag combination of: + \table + \header + \li Feature + \li Description + \row + \li Plugin.NoRoutingFeatures + \li No routing features are supported. + \row + \li Plugin.OnlineRoutingFeature + \li Online routing is supported. + \row + \li Plugin.OfflineRoutingFeature + \li Offline routing is supported. + \row + \li Plugin.LocalizedRoutingFeature + \li Supports returning routes with localized addresses and instructions. + \row + \li Plugin.RouteUpdatesFeature + \li Updating an existing route based on the current position is supported. + \row + \li Plugin.AlternativeRoutesFeature + \li Supports returning alternative routes. + \row + \li Plugin.ExcludeAreasRoutingFeature + \li Supports specifying a areas which the returned route must not cross. + \row + \li Plugin.AnyRoutingFeatures + \li Matches a geo service provider that provides any routing features. + \endtable +*/ +bool QDeclarativeGeoServiceProvider::supportsRouting(const RoutingFeatures &feature) const +{ + QGeoServiceProvider *sp = sharedGeoServiceProvider(); + QGeoServiceProvider::RoutingFeatures f = + static_cast<QGeoServiceProvider::RoutingFeature>(int(feature)); + if (f == QGeoServiceProvider::AnyRoutingFeatures) + return (sp && (sp->routingFeatures() != QGeoServiceProvider::NoRoutingFeatures)); + else + return (sp && (sp->routingFeatures() & f) == f); +} + +/*! + \qmlmethod bool Plugin::supportsPlaces(PlacesFeatures features) + + This method returns a boolean indicating whether the specified set of \a features are supported + by the geo service provider plugin. True is returned if all specified \a features are + supported; otherwise false is returned. + + The \a features parameter can be any flag combination of: + \table + \header + \li Feature + \li Description + \row + \li Plugin.NoPlacesFeatures + \li No places features are supported. + \row + \li Plugin.OnlinePlacesFeature + \li Online places is supported. + \row + \li Plugin.OfflinePlacesFeature + \li Offline places is supported. + \row + \li Plugin.SavePlaceFeature + \li Saving categories is supported. + \row + \li Plugin.RemovePlaceFeature + \li Removing or deleting places is supported. + \row + \li Plugin.PlaceRecommendationsFeature + \li Searching for recommended places similar to another place is supported. + \row + \li Plugin.SearchSuggestionsFeature + \li Search suggestions is supported. + \row + \li Plugin.LocalizedPlacesFeature + \li Supports returning localized place data. + \row + \li Plugin.NotificationsFeature + \li Notifications of place and category changes is supported. + \row + \li Plugin.PlaceMatchingFeature + \li Supports matching places from two different geo service providers. + \row + \li Plugin.AnyPlacesFeatures + \li Matches a geo service provider that provides any places features. + \endtable +*/ +bool QDeclarativeGeoServiceProvider::supportsPlaces(const PlacesFeatures &features) const +{ + QGeoServiceProvider *sp = sharedGeoServiceProvider(); + QGeoServiceProvider::PlacesFeatures f = + static_cast<QGeoServiceProvider::PlacesFeature>(int(features)); + if (f == QGeoServiceProvider::AnyPlacesFeatures) + return (sp && (sp->placesFeatures() != QGeoServiceProvider::NoPlacesFeatures)); + else + return (sp && (sp->placesFeatures() & f) == f); +} + +/*! + \qmlproperty enumeration Plugin::required + + This property contains the set of features that will be required by the + Plugin object when choosing which service plugin to attach to. If the + \l name property is set, this has no effect. + + Any of the following values or a bitwise combination of multiple values + may be set: + + \list + \li Plugin.NoFeatures + \li Plugin.GeocodingFeature + \li Plugin.ReverseGeocodingFeature + \li Plugin.RoutingFeature + \li Plugin.MappingFeature + \li Plugin.AnyPlacesFeature + \endlist +*/ +QDeclarativeGeoServiceProviderRequirements *QDeclarativeGeoServiceProvider::requirements() const +{ + return required_; +} + +/*! + \qmlproperty stringlist Plugin::preferred + + This property contains an ordered list of preferred plugin names, which + will be checked for the required features set in \l{Plugin::required}{required} + before any other available plugins are checked. +*/ +QStringList QDeclarativeGeoServiceProvider::preferred() const +{ + return prefer_; +} + +void QDeclarativeGeoServiceProvider::setPreferred(const QStringList &val) +{ + prefer_ = val; + emit preferredChanged(prefer_); +} + +/*! + \qmlproperty bool Plugin::isAttached + + This property indicates if the Plugin item is attached to a geoservice provider plugin. +*/ +bool QDeclarativeGeoServiceProvider::isAttached() const +{ + return (sharedProvider_ != 0); +} + +/*! + \qmlproperty bool Plugin::allowExperimental + + This property indicates if experimental plugins can be used. +*/ +bool QDeclarativeGeoServiceProvider::allowExperimental() const +{ + return experimental_; +} + +void QDeclarativeGeoServiceProvider::setAllowExperimental(bool allow) +{ + if (experimental_ == allow) + return; + + experimental_ = allow; + if (sharedProvider_) + sharedProvider_->setAllowExperimental(allow); + + emit allowExperimentalChanged(allow); +} + +/*! + \internal +*/ +QGeoServiceProvider *QDeclarativeGeoServiceProvider::sharedGeoServiceProvider() const +{ + return sharedProvider_; +} + +/*! + \qmlproperty stringlist Plugin::locales + + This property contains an ordered list of preferred plugin locales. If the first locale cannot be accommodated, then + the backend falls back to using the second, and so on. By default the locales property contains the system locale. + + The locales are specified as strings which have the format + "language[_script][_country]" or "C", where: + + \list + \li language is a lowercase, two-letter, ISO 639 language code, + \li script is a titlecase, four-letter, ISO 15924 script code, + \li country is an uppercase, two- or three-letter, ISO 3166 country code (also "419" as defined by United Nations), + \li the "C" locale is identical in behavior to English/UnitedStates as per QLocale + \endlist + + If the first specified locale cannot be accommodated, the \l {Plugin} falls back to the next and so forth. + Some \l {Plugin} backends may not support a set of locales which are rigidly defined. An arbitrary + example is that some \l {Place}'s in France could have French and English localizations, while + certain areas in America may only have the English localization available. In the above scenario, + the set of supported locales is context dependent on the search location. + + If the \l {Plugin} cannot accommodate any of the preferred locales, the manager falls + back to using a supported language that is backend specific. + + For \l {Plugin}'s that do not support locales, the locales list is always empty. + + The following code demonstrates how to set a single and multiple locales: + \snippet declarative/plugin.qml Plugin locale +*/ +QStringList QDeclarativeGeoServiceProvider::locales() const +{ + return locales_; +} + +void QDeclarativeGeoServiceProvider::setLocales(const QStringList &locales) +{ + if (locales_ == locales) + return; + + locales_ = locales; + + if (locales_.isEmpty()) + locales_.append(QLocale().name()); + + if (sharedProvider_) + sharedProvider_->setLocale(locales_.at(0)); + + emit localesChanged(); +} + +/*! + \qmlproperty list<PluginParameter> Plugin::parameters + \default + + This property holds the list of plugin parameters. +*/ +QQmlListProperty<QDeclarativeGeoServiceProviderParameter> QDeclarativeGeoServiceProvider::parameters() +{ + return QQmlListProperty<QDeclarativeGeoServiceProviderParameter>(this, + 0, + parameter_append, + parameter_count, + parameter_at, + parameter_clear); +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProvider::parameter_append(QQmlListProperty<QDeclarativeGeoServiceProviderParameter> *prop, QDeclarativeGeoServiceProviderParameter *parameter) +{ + QDeclarativeGeoServiceProvider *p = static_cast<QDeclarativeGeoServiceProvider *>(prop->object); + p->parameters_.append(parameter); + if (p->sharedProvider_) + p->sharedProvider_->setParameters(p->parameterMap()); +} + +/*! + \internal +*/ +int QDeclarativeGeoServiceProvider::parameter_count(QQmlListProperty<QDeclarativeGeoServiceProviderParameter> *prop) +{ + return static_cast<QDeclarativeGeoServiceProvider *>(prop->object)->parameters_.count(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProviderParameter *QDeclarativeGeoServiceProvider::parameter_at(QQmlListProperty<QDeclarativeGeoServiceProviderParameter> *prop, int index) +{ + return static_cast<QDeclarativeGeoServiceProvider *>(prop->object)->parameters_[index]; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProvider::parameter_clear(QQmlListProperty<QDeclarativeGeoServiceProviderParameter> *prop) +{ + QDeclarativeGeoServiceProvider *p = static_cast<QDeclarativeGeoServiceProvider *>(prop->object); + p->parameters_.clear(); + if (p->sharedProvider_) + p->sharedProvider_->setParameters(p->parameterMap()); +} + +/*! + \internal +*/ +QVariantMap QDeclarativeGeoServiceProvider::parameterMap() const +{ + QVariantMap map; + + for (int i = 0; i < parameters_.size(); ++i) { + QDeclarativeGeoServiceProviderParameter *parameter = parameters_.at(i); + map.insert(parameter->name(), parameter->value()); + } + + return map; +} + +/******************************************************************************* +*******************************************************************************/ + +QDeclarativeGeoServiceProviderRequirements::QDeclarativeGeoServiceProviderRequirements(QObject *parent) + : QObject(parent), + mapping_(QDeclarativeGeoServiceProvider::NoMappingFeatures), + routing_(QDeclarativeGeoServiceProvider::NoRoutingFeatures), + geocoding_(QDeclarativeGeoServiceProvider::NoGeocodingFeatures), + places_(QDeclarativeGeoServiceProvider::NoPlacesFeatures) +{ +} + +QDeclarativeGeoServiceProviderRequirements::~QDeclarativeGeoServiceProviderRequirements() +{ +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider::MappingFeatures QDeclarativeGeoServiceProviderRequirements::mappingRequirements() const +{ + return mapping_; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProviderRequirements::setMappingRequirements(const QDeclarativeGeoServiceProvider::MappingFeatures &features) +{ + if (mapping_ == features) + return; + + mapping_ = features; + emit mappingRequirementsChanged(mapping_); + emit requirementsChanged(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider::RoutingFeatures QDeclarativeGeoServiceProviderRequirements::routingRequirements() const +{ + return routing_; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProviderRequirements::setRoutingRequirements(const QDeclarativeGeoServiceProvider::RoutingFeatures &features) +{ + if (routing_ == features) + return; + + routing_ = features; + emit routingRequirementsChanged(routing_); + emit requirementsChanged(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider::GeocodingFeatures QDeclarativeGeoServiceProviderRequirements::geocodingRequirements() const +{ + return geocoding_; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProviderRequirements::setGeocodingRequirements(const QDeclarativeGeoServiceProvider::GeocodingFeatures &features) +{ + if (geocoding_ == features) + return; + + geocoding_ = features; + emit geocodingRequirementsChanged(geocoding_); + emit requirementsChanged(); +} + +/*! + \internal + + */ +QDeclarativeGeoServiceProvider::PlacesFeatures QDeclarativeGeoServiceProviderRequirements::placesRequirements() const +{ + return places_; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProviderRequirements::setPlacesRequirements(const QDeclarativeGeoServiceProvider::PlacesFeatures &features) +{ + if (places_ == features) + return; + + places_ = features; + emit placesRequirementsChanged(places_); + emit requirementsChanged(); +} + +/*! + \internal +*/ +bool QDeclarativeGeoServiceProviderRequirements::matches(const QGeoServiceProvider *provider) const +{ + QGeoServiceProvider::MappingFeatures mapping = + static_cast<QGeoServiceProvider::MappingFeatures>(int(mapping_)); + + // extra curlies here to avoid "dangling" else, which could belong to either if + // same goes for all the rest of these blocks + if (mapping == QGeoServiceProvider::AnyMappingFeatures) { + if (provider->mappingFeatures() == QGeoServiceProvider::NoMappingFeatures) + return false; + } else { + if ((provider->mappingFeatures() & mapping) != mapping) + return false; + } + + QGeoServiceProvider::RoutingFeatures routing = + static_cast<QGeoServiceProvider::RoutingFeatures>(int(routing_)); + + if (routing == QGeoServiceProvider::AnyRoutingFeatures) { + if (provider->routingFeatures() == QGeoServiceProvider::NoRoutingFeatures) + return false; + } else { + if ((provider->routingFeatures() & routing) != routing) + return false; + } + + QGeoServiceProvider::GeocodingFeatures geocoding = + static_cast<QGeoServiceProvider::GeocodingFeatures>(int(geocoding_)); + + if (geocoding == QGeoServiceProvider::AnyGeocodingFeatures) { + if (provider->geocodingFeatures() == QGeoServiceProvider::NoGeocodingFeatures) + return false; + } else { + if ((provider->geocodingFeatures() & geocoding) != geocoding) + return false; + } + + QGeoServiceProvider::PlacesFeatures places = + static_cast<QGeoServiceProvider::PlacesFeatures>(int(places_)); + + if (places == QGeoServiceProvider::AnyPlacesFeatures) { + if (provider->placesFeatures() == QGeoServiceProvider::NoPlacesFeatures) + return false; + } else { + if ((provider->placesFeatures() & places) != places) + return false; + } + + return true; +} + +/******************************************************************************* +*******************************************************************************/ + +/*! + \qmltype PluginParameter + \instantiates QDeclarativeGeoServiceProviderParameter + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-common + \since Qt Location 5.5 + + \brief The PluginParameter type describes a parameter to a \l Plugin. + + The PluginParameter object is used to provide a parameter of some kind + to a Plugin. Typically these parameters contain details like an application + token for access to a service, or a proxy server to use for network access. + + To set such a parameter, declare a PluginParameter inside a \l Plugin + object, and give it \l{name} and \l{value} properties. A list of valid + parameter names for each plugin is available from the + \l {Qt Location#Plugin References and Parameters}{plugin reference pages}. + + \section2 Example Usage + + The following example shows an instantiation of the \l {Qt Location HERE Plugin}{HERE} plugin + with a mapping API \e app_id and \e token pair specific to the application. + + \code + Plugin { + name: "here" + PluginParameter { name: "here.app_id"; value: "EXAMPLE_API_ID" } + PluginParameter { name: "here.token"; value: "EXAMPLE_TOKEN_123" } + } + \endcode +*/ + +QDeclarativeGeoServiceProviderParameter::QDeclarativeGeoServiceProviderParameter(QObject *parent) + : QObject(parent) {} + +QDeclarativeGeoServiceProviderParameter::~QDeclarativeGeoServiceProviderParameter() {} + +/*! + \qmlproperty string PluginParameter::name + + This property holds the name of the plugin parameter as a single formatted string. +*/ +void QDeclarativeGeoServiceProviderParameter::setName(const QString &name) +{ + if (name_ == name) + return; + + name_ = name; + + emit nameChanged(name_); +} + +QString QDeclarativeGeoServiceProviderParameter::name() const +{ + return name_; +} + +/*! + \qmlproperty QVariant PluginParameter::value + + This property holds the value of the plugin parameter which support different types of values (variant). +*/ +void QDeclarativeGeoServiceProviderParameter::setValue(const QVariant &value) +{ + if (value_ == value) + return; + + value_ = value; + + emit valueChanged(value_); +} + +QVariant QDeclarativeGeoServiceProviderParameter::value() const +{ + return value_; +} + +/******************************************************************************* +*******************************************************************************/ + +#include "moc_qdeclarativegeoserviceprovider_p.cpp" + +QT_END_NAMESPACE + diff --git a/src/location/declarativemaps/qdeclarativegeoserviceprovider_p.h b/src/location/declarativemaps/qdeclarativegeoserviceprovider_p.h new file mode 100644 index 00000000..bcf67124 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoserviceprovider_p.h @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEQGEOSERVICEPROVIDER_H +#define QDECLARATIVEQGEOSERVICEPROVIDER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> + +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <QtCore/QVariant> +#include <QtQml/qqml.h> +#include <QtQml/QQmlParserStatus> +#include <QtQml/QQmlListProperty> +#include <QtLocation/QGeoServiceProvider> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoServiceProviderParameter : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + explicit QDeclarativeGeoServiceProviderParameter(QObject *parent = 0); + ~QDeclarativeGeoServiceProviderParameter(); + + void setName(const QString &name); + QString name() const; + + void setValue(const QVariant &value); + QVariant value() const; + +Q_SIGNALS: + void nameChanged(const QString &name); + void valueChanged(const QVariant &value); + +private: + QString name_; + QVariant value_; +}; + +class QDeclarativeGeoServiceProviderRequirements; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoServiceProvider : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_ENUMS(RoutingFeature) + Q_ENUMS(GeocodingFeature) + Q_ENUMS(MappingFeature) + Q_ENUMS(PlacesFeature) + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QStringList availableServiceProviders READ availableServiceProviders CONSTANT) + Q_PROPERTY(QQmlListProperty<QDeclarativeGeoServiceProviderParameter> parameters READ parameters) + Q_PROPERTY(QDeclarativeGeoServiceProviderRequirements *required READ requirements) + Q_PROPERTY(QStringList locales READ locales WRITE setLocales NOTIFY localesChanged) + Q_PROPERTY(QStringList preferred READ preferred WRITE setPreferred NOTIFY preferredChanged) + Q_PROPERTY(bool allowExperimental READ allowExperimental WRITE setAllowExperimental NOTIFY allowExperimentalChanged) + Q_PROPERTY(bool isAttached READ isAttached NOTIFY attached) + + Q_CLASSINFO("DefaultProperty", "parameters") + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativeGeoServiceProvider(QObject *parent = Q_NULLPTR); + ~QDeclarativeGeoServiceProvider(); + + enum RoutingFeature { + NoRoutingFeatures = QGeoServiceProvider::NoRoutingFeatures, + OnlineRoutingFeature = QGeoServiceProvider::OnlineRoutingFeature, + OfflineRoutingFeature = QGeoServiceProvider::OfflineRoutingFeature, + LocalizedRoutingFeature = QGeoServiceProvider::LocalizedRoutingFeature, + RouteUpdatesFeature = QGeoServiceProvider::RouteUpdatesFeature, + AlternativeRoutesFeature = QGeoServiceProvider::AlternativeRoutesFeature, + ExcludeAreasRoutingFeature = QGeoServiceProvider::ExcludeAreasRoutingFeature, + AnyRoutingFeatures = QGeoServiceProvider::AnyRoutingFeatures + }; + + enum GeocodingFeature { + NoGeocodingFeatures = QGeoServiceProvider::NoGeocodingFeatures, + OnlineGeocodingFeature = QGeoServiceProvider::OnlineGeocodingFeature, + OfflineGeocodingFeature = QGeoServiceProvider::OfflineGeocodingFeature, + ReverseGeocodingFeature = QGeoServiceProvider::ReverseGeocodingFeature, + LocalizedGeocodingFeature = QGeoServiceProvider::LocalizedGeocodingFeature, + AnyGeocodingFeatures = QGeoServiceProvider::AnyGeocodingFeatures + }; + + enum MappingFeature { + NoMappingFeatures = QGeoServiceProvider::NoMappingFeatures, + OnlineMappingFeature = QGeoServiceProvider::OnlineMappingFeature, + OfflineMappingFeature = QGeoServiceProvider::OfflineMappingFeature, + LocalizedMappingFeature = QGeoServiceProvider::LocalizedMappingFeature, + AnyMappingFeatures = QGeoServiceProvider::AnyMappingFeatures + }; + + enum PlacesFeature { + NoPlacesFeatures = QGeoServiceProvider::NoPlacesFeatures, + OnlinePlacesFeature = QGeoServiceProvider::OnlinePlacesFeature, + OfflinePlacesFeature = QGeoServiceProvider::OfflinePlacesFeature, + SavePlaceFeature = QGeoServiceProvider::SavePlaceFeature, + RemovePlaceFeature = QGeoServiceProvider::RemovePlaceFeature, + SaveCategoryFeature = QGeoServiceProvider::SaveCategoryFeature, + RemoveCategoryFeature = QGeoServiceProvider::RemoveCategoryFeature, + PlaceRecommendationsFeature = QGeoServiceProvider::PlaceRecommendationsFeature, + SearchSuggestionsFeature = QGeoServiceProvider::SearchSuggestionsFeature, + LocalizedPlacesFeature = QGeoServiceProvider::LocalizedPlacesFeature, + NotificationsFeature = QGeoServiceProvider::NotificationsFeature, + PlaceMatchingFeature = QGeoServiceProvider::PlaceMatchingFeature, + AnyPlacesFeatures = QGeoServiceProvider::AnyPlacesFeatures + }; + + Q_DECLARE_FLAGS(RoutingFeatures, RoutingFeature) + Q_FLAGS(RoutingFeatures) + + Q_DECLARE_FLAGS(GeocodingFeatures, GeocodingFeature) + Q_FLAGS(GeocodingFeatures) + + Q_DECLARE_FLAGS(MappingFeatures, MappingFeature) + Q_FLAGS(MappingFeatures) + + Q_DECLARE_FLAGS(PlacesFeatures, PlacesFeature) + Q_FLAGS(PlacesFeatures) + + // From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + void setName(const QString &name); + QString name() const; + + QQmlListProperty<QDeclarativeGeoServiceProviderParameter> parameters(); + QVariantMap parameterMap() const; + + QStringList availableServiceProviders(); + + QDeclarativeGeoServiceProviderRequirements *requirements() const; + + QStringList preferred() const; + void setPreferred(const QStringList &val); + + QGeoServiceProvider *sharedGeoServiceProvider() const; + + Q_INVOKABLE bool supportsRouting(const RoutingFeatures &feature = AnyRoutingFeatures) const; + Q_INVOKABLE bool supportsGeocoding(const GeocodingFeatures &feature = AnyGeocodingFeatures) const; + Q_INVOKABLE bool supportsMapping(const MappingFeatures &feature = AnyMappingFeatures) const; + Q_INVOKABLE bool supportsPlaces(const PlacesFeatures &feature = AnyPlacesFeatures) const; + + QStringList locales() const; + void setLocales(const QStringList &locales); + + bool isAttached() const; + + void setAllowExperimental(bool allow); + bool allowExperimental() const; + +Q_SIGNALS: + void nameChanged(const QString &name); + void localesChanged(); + void attached(); + void preferredChanged(const QStringList &preferences); + void allowExperimentalChanged(bool allow); + +private: + static void parameter_append(QQmlListProperty<QDeclarativeGeoServiceProviderParameter> *prop, QDeclarativeGeoServiceProviderParameter *mapObject); + static int parameter_count(QQmlListProperty<QDeclarativeGeoServiceProviderParameter> *prop); + static QDeclarativeGeoServiceProviderParameter *parameter_at(QQmlListProperty<QDeclarativeGeoServiceProviderParameter> *prop, int index); + static void parameter_clear(QQmlListProperty<QDeclarativeGeoServiceProviderParameter> *prop); + + QGeoServiceProvider *sharedProvider_; + QString name_; + QList<QDeclarativeGeoServiceProviderParameter *> parameters_; + QDeclarativeGeoServiceProviderRequirements *required_; + bool complete_; + bool experimental_; + QStringList locales_; + QStringList prefer_; + Q_DISABLE_COPY(QDeclarativeGeoServiceProvider) +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoServiceProviderRequirements : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeGeoServiceProvider::MappingFeatures mapping + READ mappingRequirements WRITE setMappingRequirements + NOTIFY mappingRequirementsChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider::RoutingFeatures routing + READ routingRequirements WRITE setRoutingRequirements + NOTIFY routingRequirementsChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider::GeocodingFeatures geocoding + READ geocodingRequirements WRITE setGeocodingRequirements + NOTIFY geocodingRequirementsChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider::PlacesFeatures places + READ placesRequirements WRITE setPlacesRequirements + NOTIFY placesRequirementsChanged) + +public: + explicit QDeclarativeGeoServiceProviderRequirements(QObject *parent = 0); + ~QDeclarativeGeoServiceProviderRequirements(); + + QDeclarativeGeoServiceProvider::MappingFeatures mappingRequirements() const; + void setMappingRequirements(const QDeclarativeGeoServiceProvider::MappingFeatures &features); + + QDeclarativeGeoServiceProvider::RoutingFeatures routingRequirements() const; + void setRoutingRequirements(const QDeclarativeGeoServiceProvider::RoutingFeatures &features); + + QDeclarativeGeoServiceProvider::GeocodingFeatures geocodingRequirements() const; + void setGeocodingRequirements(const QDeclarativeGeoServiceProvider::GeocodingFeatures &features); + + QDeclarativeGeoServiceProvider::PlacesFeatures placesRequirements() const; + void setPlacesRequirements(const QDeclarativeGeoServiceProvider::PlacesFeatures &features); + + Q_INVOKABLE bool matches(const QGeoServiceProvider *provider) const; + +Q_SIGNALS: + void mappingRequirementsChanged(const QDeclarativeGeoServiceProvider::MappingFeatures &features); + void routingRequirementsChanged(const QDeclarativeGeoServiceProvider::RoutingFeatures &features); + void geocodingRequirementsChanged(const QDeclarativeGeoServiceProvider::GeocodingFeatures &features); + void placesRequirementsChanged(const QDeclarativeGeoServiceProvider::PlacesFeatures &features); + + void requirementsChanged(); + +private: + QDeclarativeGeoServiceProvider::MappingFeatures mapping_; + QDeclarativeGeoServiceProvider::RoutingFeatures routing_; + QDeclarativeGeoServiceProvider::GeocodingFeatures geocoding_; + QDeclarativeGeoServiceProvider::PlacesFeatures places_; + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoServiceProviderParameter) +QML_DECLARE_TYPE(QDeclarativeGeoServiceProviderRequirements) +QML_DECLARE_TYPE(QDeclarativeGeoServiceProvider) + +#endif diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp new file mode 100644 index 00000000..3f05957a --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp @@ -0,0 +1,743 @@ +/**************************************************************************** + ** + ** 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 "qdeclarativepolygonmapitem_p.h" +#include "qgeocameracapabilities_p.h" +#include "qlocationutils_p.h" +#include "error_messages.h" +#include "locationvaluetypehelper_p.h" +#include <QtLocation/private/qgeomap_p.h> + +#include <QtCore/QScopedValueRollback> +#include <QtGui/private/qtriangulator_p.h> +#include <QtQml/QQmlInfo> +#include <QtQml/private/qqmlengine_p.h> +#include <QPainter> +#include <QPainterPath> +#include <qnumeric.h> + +#include <QtPositioning/private/qdoublevector2d_p.h> +#include <QtPositioning/private/qclipperutils_p.h> + +/* poly2tri triangulator includes */ +#include <clip2tri.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapPolygon + \instantiates QDeclarativePolygonMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.5 + + \brief The MapPolygon type displays a polygon on a Map + + The MapPolygon type displays a polygon on a Map, specified in terms of an ordered list of + \l {QtPositioning::coordinate}{coordinates}. For best appearance and results, polygons should be + simple (not self-intersecting). + + The \l {QtPositioning::coordinate}{coordinates} on the path cannot be directly changed after + being added to the Polygon. Instead, copy the \l path into a var, modify the copy and reassign + the copy back to the \l path. + + \code + var path = mapPolygon.path; + path[0].latitude = 5; + mapPolygon.path = path; + \endcode + + Coordinates can also be added and removed at any time using the \l addCoordinate and + \l removeCoordinate methods. + + For drawing rectangles with "straight" edges (same latitude across one + edge, same latitude across the other), the \l MapRectangle type provides + a simpler, two-point API. + + By default, the polygon is displayed as a 1 pixel black border with no + fill. To change its appearance, use the \l color, \l border.color and + \l border.width properties. + + \note Since MapPolygons are geographic items, dragging a MapPolygon + (through the use of \l MouseArea) causes its vertices to be + recalculated in the geographic coordinate space. The edges retain the + same geographic lengths (latitude and longitude differences between the + vertices), but they remain straight. Apparent stretching of the item occurs + when dragged to a different latitude. + + \section2 Performance + + MapPolygons have a rendering cost that is O(n) with respect to the number + of vertices. This means that the per frame cost of having a Polygon on the + Map grows in direct proportion to the number of points on the Polygon. There + is an additional triangulation cost (approximately O(n log n)) which is + currently paid with each frame, but in future may be paid only upon adding + or removing points. + + Like the other map objects, MapPolygon is normally drawn without a smooth + appearance. Setting the \l {Item::opacity}{opacity} property will force the object to + be blended, which decreases performance considerably depending on the hardware in use. + + \section2 Example Usage + + The following snippet shows a MapPolygon being used to display a triangle, + with three vertices near Brisbane, Australia. The triangle is filled in + green, with a 1 pixel black border. + + \code + Map { + MapPolygon { + color: 'green' + path: [ + { latitude: -27, longitude: 153.0 }, + { latitude: -27, longitude: 154.1 }, + { latitude: -28, longitude: 153.5 } + ] + } + } + \endcode + + \image api-mappolygon.png +*/ + +QGeoMapPolygonGeometry::QGeoMapPolygonGeometry() +: assumeSimple_(false) +{ +} + +/*! + \internal +*/ +void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, + const QList<QGeoCoordinate> &path) +{ + if (!sourceDirty_) + return; + + srcPath_ = QPainterPath(); + + // build the actual path + // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints + srcOrigin_ = geoLeftBound_; + double unwrapBelowX = 0; + QDoubleVector2D leftBoundWrapped = map.geoProjection().wrapMapProjection(map.geoProjection().geoToMapProjection(geoLeftBound_)); + if (preserveGeometry_) + unwrapBelowX = leftBoundWrapped.x(); + + QList<QDoubleVector2D> wrappedPath; + wrappedPath.reserve(path.size()); + QDoubleVector2D wrappedLeftBound(qInf(), qInf()); + // 1) + for (int i = 0; i < path.size(); ++i) { + const QGeoCoordinate &coord = path.at(i); + if (!coord.isValid()) + continue; + + QDoubleVector2D wrappedProjection = map.geoProjection().wrapMapProjection(map.geoProjection().geoToMapProjection(coord)); + + // We can get NaN if the map isn't set up correctly, or the projection + // is faulty -- probably best thing to do is abort + if (!qIsFinite(wrappedProjection.x()) || !qIsFinite(wrappedProjection.y())) + return; + + const bool isPointLessThanUnwrapBelowX = (wrappedProjection.x() < leftBoundWrapped.x()); + // unwrap x to preserve geometry if moved to border of map + if (preserveGeometry_ && isPointLessThanUnwrapBelowX) { + double distance = wrappedProjection.x() - unwrapBelowX; + if (distance < 0.0) + distance += 1.0; + wrappedProjection.setX(unwrapBelowX + distance); + } + if (wrappedProjection.x() < wrappedLeftBound.x() || (wrappedProjection.x() == wrappedLeftBound.x() && wrappedProjection.y() < wrappedLeftBound.y())) { + wrappedLeftBound = wrappedProjection; + } + wrappedPath.append(wrappedProjection); + } + + // 2) + QList<QList<QDoubleVector2D> > clippedPaths; + const QList<QDoubleVector2D> &visibleRegion = map.geoProjection().visibleRegion(); + if (visibleRegion.size()) { + c2t::clip2tri clipper; + clipper.addSubjectPath(QClipperUtils::qListToPath(wrappedPath), true); + clipper.addClipPolygon(QClipperUtils::qListToPath(visibleRegion)); + Paths res = clipper.execute(c2t::clip2tri::Intersection, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); + clippedPaths = QClipperUtils::pathsToQList(res); + + // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X + QDoubleVector2D lb(qInf(), qInf()); + for (const QList<QDoubleVector2D> &path: clippedPaths) + for (const QDoubleVector2D &p: path) + if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) + // y-minimization needed to find the same point on polygon and border + lb = p; + + if (qIsInf(lb.x())) // e.g., when the polygon is clipped entirely + return; + + // 2.2) Prevent the conversion to and from clipper from introducing negative offsets which + // in turn will make the geometry wrap around. + lb.setX(qMax(wrappedLeftBound.x(), lb.x())); + leftBoundWrapped = lb; + srcOrigin_ = map.geoProjection().mapProjectionToGeo(map.geoProjection().unwrapMapProjection(lb)); + } else { + clippedPaths.append(wrappedPath); + } + + // 3) + QDoubleVector2D origin = map.geoProjection().wrappedMapProjectionToItemPosition(leftBoundWrapped); + for (const QList<QDoubleVector2D> &path: clippedPaths) { + QDoubleVector2D lastAddedPoint; + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D point = map.geoProjection().wrappedMapProjectionToItemPosition(path.at(i)); + point = point - origin; // (0,0) if point == geoLeftBound_ + + if (i == 0) { + srcPath_.moveTo(point.toPointF()); + lastAddedPoint = point; + } else { + if ((point - lastAddedPoint).manhattanLength() > 3 || + i == path.size() - 1) { + srcPath_.lineTo(point.toPointF()); + lastAddedPoint = point; + } + } + } + srcPath_.closeSubpath(); + } + + if (!assumeSimple_) + srcPath_ = srcPath_.simplified(); + + sourceBounds_ = srcPath_.boundingRect(); +} + +/*! + \internal +*/ +void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map) +{ + if (!screenDirty_) + return; + + if (map.viewportWidth() == 0 || map.viewportHeight() == 0) { + clear(); + return; + } + + QDoubleVector2D origin = map.geoProjection().coordinateToItemPosition(srcOrigin_, false); + + // Create the viewport rect in the same coordinate system + // as the actual points + QRectF viewport(0, 0, map.viewportWidth(), map.viewportHeight()); + viewport.translate(-1 * origin.toPointF()); + + QPainterPath vpPath; + vpPath.addRect(viewport); + + // The geometry has already been clipped against the visible region projection in wrapped mercator space. + QPainterPath ppi = srcPath_; + clear(); + + // a polygon requires at least 3 points; + if (ppi.elementCount() < 3) + return; + + // TODO: move this to clip2tri, and remove the code below. + // For clip2tri use the intersection between the the viewport AND the map as clipping region. + + // Intersection between the viewport and a concave polygon can create multiple polygons + // joined by a line at the viewport border, and poly2tri does not triangulate this very well + // so use the full src path if the resulting polygon is concave. + if (clipToViewport_) { + int changeInX = 0; + int changeInY = 0; + QPainterPath::Element e1 = ppi.elementAt(1); + QPainterPath::Element e = ppi.elementAt(0); + QVector2D edgeA(e1.x - e.x ,e1.y - e.y); + for (int i = 2; i <= ppi.elementCount(); ++i) { + e = ppi.elementAt(i % ppi.elementCount()); + if (e.x == e1.x && e.y == e1.y) + continue; + QVector2D edgeB(e.x - e1.x, e.y - e1.y); + if ((edgeA.x() < 0) == (edgeB.x() >= 0)) + changeInX++; + if ((edgeA.y() < 0) == (edgeB.y() >= 0)) + changeInY++; + edgeA = edgeB; + e1 = e; + } + if (changeInX > 2 || changeInY > 2) // polygon is concave + ppi = srcPath_; + } + + // translate the path into top-left-centric coordinates + QRectF bb = ppi.boundingRect(); + ppi.translate(-bb.left(), -bb.top()); + firstPointOffset_ = -1 * bb.topLeft(); + + ppi.closeSubpath(); + + screenOutline_ = ppi; + +#if 0 // TODO: This code appears to crash seldomly in presence of tilt. Requires further investigation + std::vector<std::vector<c2t::Point>> clipperPoints; + clipperPoints.push_back(std::vector<c2t::Point>()); + std::vector<c2t::Point> &curPts = clipperPoints.front(); + curPts.reserve(ppi.elementCount()); + for (int i = 0; i < ppi.elementCount(); ++i) { + const QPainterPath::Element e = ppi.elementAt(i); + if (e.isMoveTo() || i == ppi.elementCount() - 1 + || (qAbs(e.x - curPts.front().x) < 0.1 + && qAbs(e.y - curPts.front().y) < 0.1)) { + if (curPts.size() > 2) { + c2t::clip2tri *cdt = new c2t::clip2tri(); + std::vector<c2t::Point> outputTriangles; + cdt->triangulate(clipperPoints, outputTriangles, std::vector<c2t::Point>()); + for (size_t i = 0; i < outputTriangles.size(); ++i) { + screenVertices_ << QPointF(outputTriangles[i].x, outputTriangles[i].y); + } + delete cdt; + } + curPts.clear(); + curPts.reserve(ppi.elementCount() - i); + curPts.push_back( c2t::Point(e.x, e.y)); + } else if (e.isLineTo()) { + curPts.push_back( c2t::Point(e.x, e.y)); + } else { + qWarning("Unhandled element type in polygon painterpath"); + } + } +#else // Old qTriangulate()-based code. + QTriangleSet ts = qTriangulate(ppi); + qreal *vx = ts.vertices.data(); + + screenIndices_.reserve(ts.indices.size()); + screenVertices_.reserve(ts.vertices.size()); + + if (ts.indices.type() == QVertexIndexVector::UnsignedInt) { + const quint32 *ix = reinterpret_cast<const quint32 *>(ts.indices.data()); + for (int i = 0; i < (ts.indices.size()/3*3); ++i) + screenIndices_ << ix[i]; + } else { + const quint16 *ix = reinterpret_cast<const quint16 *>(ts.indices.data()); + for (int i = 0; i < (ts.indices.size()/3*3); ++i) + screenIndices_ << ix[i]; + } + for (int i = 0; i < (ts.vertices.size()/2*2); i += 2) + screenVertices_ << QPointF(vx[i], vx[i + 1]); +#endif + + screenBounds_ = ppi.boundingRect(); +} + +QDeclarativePolygonMapItem::QDeclarativePolygonMapItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), color_(Qt::transparent), dirtyMaterial_(true), + updatingGeometry_(false) +{ + setFlag(ItemHasContents, true); + QObject::connect(&border_, SIGNAL(colorChanged(QColor)), + this, SLOT(handleBorderUpdated())); + QObject::connect(&border_, SIGNAL(widthChanged(qreal)), + this, SLOT(handleBorderUpdated())); +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::handleBorderUpdated() +{ + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +QDeclarativePolygonMapItem::~QDeclarativePolygonMapItem() +{ +} + +/*! + \qmlpropertygroup Location::MapPolygon::border + \qmlproperty int MapPolygon::border.width + \qmlproperty color MapPolygon::border.color + + This property is part of the border property group. The border property + group holds the width and color used to draw the border of the polygon. + + The width is in pixels and is independent of the zoom level of the map. + + The default values correspond to a black border with a width of 1 pixel. + For no line, use a width of 0 or a transparent color. +*/ + +QDeclarativeMapLineProperties *QDeclarativePolygonMapItem::border() +{ + return &border_; +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (map) { + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); + } +} + +/*! + \qmlproperty list<coordinate> MapPolygon::path + + This property holds the ordered list of coordinates which + define the polygon. + + \sa addCoordinate, removeCoordinate +*/ +QJSValue QDeclarativePolygonMapItem::path() const +{ + QQmlContext *context = QQmlEngine::contextForObject(this); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped<QV4::ArrayObject> pathArray(scope, v4->newArrayObject(geopath_.path().length())); + for (int i = 0; i < geopath_.path().length(); ++i) { + const QGeoCoordinate &c = geopath_.coordinateAt(i); + + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c))); + pathArray->putIndexed(i, cv); + } + + return QJSValue(v4, pathArray.asReturnedValue()); +} + +void QDeclarativePolygonMapItem::setPath(const QJSValue &value) +{ + if (!value.isArray()) + return; + + QList<QGeoCoordinate> pathList; + quint32 length = value.property(QStringLiteral("length")).toUInt(); + for (quint32 i = 0; i < length; ++i) { + bool ok; + QGeoCoordinate c = parseCoordinate(value.property(i), &ok); + + if (!ok || !c.isValid()) { + qmlWarning(this) << "Unsupported path type"; + return; + } + + pathList.append(c); + } + + // Equivalent to QDeclarativePolylineMapItem::setPathFromGeoList + if (geopath_.path() == pathList) + return; + + geopath_.setPath(pathList); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolygon::addCoordinate(coordinate) + + Adds a coordinate to the path. + + \sa removeCoordinate, path +*/ + +void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate) +{ + geopath_.addCoordinate(coordinate); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolygon::removeCoordinate(coordinate) + + Removes \a coordinate from the path. If there are multiple instances of the + same coordinate, the one added last is removed. + + If \a coordinate is not in the path this method does nothing. + + \sa addCoordinate, path +*/ +void QDeclarativePolygonMapItem::removeCoordinate(const QGeoCoordinate &coordinate) +{ + int length = geopath_.path().length(); + geopath_.removeCoordinate(coordinate); + if (geopath_.path().length() == length) + return; + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlproperty color MapPolygon::color + + This property holds the color used to fill the polygon. + + The default value is transparent. +*/ + +QColor QDeclarativePolygonMapItem::color() const +{ + return color_; +} + +void QDeclarativePolygonMapItem::setColor(const QColor &color) +{ + if (color_ == color) + return; + + color_ = color; + dirtyMaterial_ = true; + update(); + emit colorChanged(color_); +} + +/*! + \internal +*/ +QSGNode *QDeclarativePolygonMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode); + + if (!node) + node = new MapPolygonNode(); + + //TODO: update only material + if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { + node->update(color_, border_.color(), &geometry_, &borderGeometry_); + geometry_.setPreserveGeometry(false); + borderGeometry_.setPreserveGeometry(false); + geometry_.markClean(); + borderGeometry_.markClean(); + dirtyMaterial_ = false; + } + return node; +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::updatePolish() +{ + if (!map() || geopath_.path().length() == 0) + return; + + QScopedValueRollback<bool> rollback(updatingGeometry_); + updatingGeometry_ = true; + + geometry_.updateSourcePoints(*map(), geopath_.path()); + geometry_.updateScreenPoints(*map()); + + QList<QGeoMapItemGeometry *> geoms; + geoms << &geometry_; + borderGeometry_.clear(); + + if (border_.color() != Qt::transparent && border_.width() > 0) { + QList<QGeoCoordinate> closedPath = geopath_.path(); + closedPath << closedPath.first(); + + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + + const QGeoCoordinate &geometryOrigin = geometry_.origin(); + + borderGeometry_.srcPoints_.clear(); + borderGeometry_.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = map()->geoProjection().geoToWrappedMapProjection(geometryOrigin); + borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); + borderGeometry_.updateScreenPoints(*map(), border_.width()); + + geoms << &borderGeometry_; + } else { + borderGeometry_.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + setWidth(combined.width()); + setHeight(combined.height()); + + setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft()); +} + +void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + return; + + geometry_.setPreserveGeometry(true, geometry_.geoLeftBound()); + borderGeometry_.setPreserveGeometry(true, borderGeometry_.geoLeftBound()); + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \internal +*/ +bool QDeclarativePolygonMapItem::contains(const QPointF &point) const +{ + return (geometry_.contains(point) || borderGeometry_.contains(point)); +} + +const QGeoShape &QDeclarativePolygonMapItem::geoShape() const +{ + return geopath_; +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + // TODO: change the algorithm to preserve the distances and size! + QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false); + QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false); + if (!newCenter.isValid() || !oldCenter.isValid()) + return; + double offsetLongi = newCenter.longitude() - oldCenter.longitude(); + double offsetLati = newCenter.latitude() - oldCenter.latitude(); + if (offsetLati == 0.0 && offsetLongi == 0.0) + return; + + geopath_.translate(offsetLati, offsetLongi); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +////////////////////////////////////////////////////////////////////// + +MapPolygonNode::MapPolygonNode() : + border_(new MapPolylineNode()), + geometry_(QSGGeometry::defaultAttributes_Point2D(), 0), + blocked_(true) +{ + geometry_.setDrawingMode(QSGGeometry::DrawTriangles); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&geometry_); + + appendChildNode(border_); +} + +MapPolygonNode::~MapPolygonNode() +{ +} + +/*! + \internal +*/ +bool MapPolygonNode::isSubtreeBlocked() const +{ + return blocked_; +} + +/*! + \internal +*/ +void MapPolygonNode::update(const QColor &fillColor, const QColor &borderColor, + const QGeoMapItemGeometry *fillShape, + const QGeoMapItemGeometry *borderShape) +{ + /* Do the border update first */ + border_->update(borderColor, borderShape); + + /* If we have neither fill nor border with valid points, block the whole + * tree. We can't just block the fill without blocking the border too, so + * we're a little conservative here (maybe at the expense of rendering + * accuracy) */ + if (fillShape->size() == 0) { + if (borderShape->size() == 0) { + blocked_ = true; + return; + } else { + blocked_ = false; + } + } else { + blocked_ = false; + } + + QSGGeometry *fill = QSGGeometryNode::geometry(); + fillShape->allocateAndFill(fill); + markDirty(DirtyGeometry); + + if (fillColor != fill_material_.color()) { + fill_material_.setColor(fillColor); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h b/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h new file mode 100644 index 00000000..cb4de6b9 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPOLYGONMAPITEM +#define QDECLARATIVEPOLYGONMAPITEM + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeomapitembase_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qgeomapitemgeometry_p.h> + +#include <QSGGeometryNode> +#include <QSGFlatColorMaterial> + +QT_BEGIN_NAMESPACE + +class MapPolygonNode; + +class QGeoMapPolygonGeometry : public QGeoMapItemGeometry +{ +public: + QGeoMapPolygonGeometry(); + + inline void setAssumeSimple(bool value) { assumeSimple_ = value; } + + void updateSourcePoints(const QGeoMap &map, + const QList<QGeoCoordinate> &path); + + void updateScreenPoints(const QGeoMap &map); + +protected: + QPainterPath srcPath_; + bool assumeSimple_; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItem : public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + + Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + +public: + explicit QDeclarativePolygonMapItem(QQuickItem *parent = 0); + ~QDeclarativePolygonMapItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) Q_DECL_OVERRIDE; + //from QuickItem + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; + + Q_INVOKABLE void addCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void removeCoordinate(const QGeoCoordinate &coordinate); + + QJSValue path() const; + void setPath(const QJSValue &value); + + QColor color() const; + void setColor(const QColor &color); + + QDeclarativeMapLineProperties *border(); + + bool contains(const QPointF &point) const Q_DECL_OVERRIDE; + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; + +Q_SIGNALS: + void pathChanged(); + void colorChanged(const QColor &color); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void updatePolish() Q_DECL_OVERRIDE; + +protected Q_SLOTS: + void markSourceDirtyAndUpdate(); + void handleBorderUpdated(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) Q_DECL_OVERRIDE; + +private: + void pathPropertyChanged(); + + QGeoPath geopath_; + QDeclarativeMapLineProperties border_; + QColor color_; + bool dirtyMaterial_; + QGeoMapPolygonGeometry geometry_; + QGeoMapPolylineGeometry borderGeometry_; + bool updatingGeometry_; +}; + +////////////////////////////////////////////////////////////////////// + +class MapPolygonNode : public QSGGeometryNode +{ + +public: + MapPolygonNode(); + ~MapPolygonNode(); + + void update(const QColor &fillColor, const QColor &borderColor, + const QGeoMapItemGeometry *fillShape, + const QGeoMapItemGeometry *borderShape); + + bool isSubtreeBlocked() const; + +private: + QSGFlatColorMaterial fill_material_; + MapPolylineNode *border_; + QSGGeometry geometry_; + bool blocked_; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePolygonMapItem) + +#endif /* QDECLARATIVEPOLYGONMAPITEM_H_ */ diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp new file mode 100644 index 00000000..601af6ee --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp @@ -0,0 +1,956 @@ +/**************************************************************************** + ** + ** 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 "qdeclarativepolylinemapitem_p.h" +#include "qgeocameracapabilities_p.h" +#include "qlocationutils_p.h" +#include "error_messages.h" +#include "locationvaluetypehelper_p.h" +#include "qdoublevector2d_p.h" +#include <QtLocation/private/qgeomap_p.h> + +#include <QtCore/QScopedValueRollback> +#include <QtQml/QQmlInfo> +#include <QtQml/private/qqmlengine_p.h> +#include <QPainter> +#include <QPainterPath> +#include <QPainterPathStroker> +#include <qnumeric.h> + +#include <QtGui/private/qvectorpath_p.h> +#include <QtGui/private/qtriangulatingstroker_p.h> +#include <QtGui/private/qtriangulator_p.h> + +#include <QtPositioning/private/qclipperutils_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapPolyline + \instantiates QDeclarativePolylineMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.0 + + \brief The MapPolyline type displays a polyline on a map. + + The MapPolyline type displays a polyline on a map, specified in terms of an ordered list of + \l {coordinate}{coordinates}. The \l {coordinate}{coordinates} on + the path cannot be directly changed after being added to the Polyline. Instead, copy the + \l path into a var, modify the copy and reassign the copy back to the \l path. + + \code + var path = mapPolyline.path; + path[0].latitude = 5; + mapPolyline.path = path; + \endcode + + Coordinates can also be added and removed at any time using the \l addCoordinate and + \l removeCoordinate methods. + + By default, the polyline is displayed as a 1-pixel thick black line. This + can be changed using the \l line.width and \l line.color properties. + + \section2 Performance + + MapPolylines have a rendering cost that is O(n) with respect to the number + of vertices. This means that the per frame cost of having a polyline on + the Map grows in direct proportion to the number of points in the polyline. + + Like the other map objects, MapPolyline is normally drawn without a smooth + appearance. Setting the \l {Item::opacity}{opacity} property will force the object to + be blended, which decreases performance considerably depending on the hardware in use. + + \note MapPolylines are implemented using the OpenGL GL_LINES + primitive. There have been occasional reports of issues and rendering + inconsistencies on some (particularly quite old) platforms. No workaround + is yet available for these issues. + + \section2 Example Usage + + The following snippet shows a MapPolyline with 4 points, making a shape + like the top part of a "question mark" (?), near Brisbane, Australia. + The line drawn is 3 pixels in width and green in color. + + \code + Map { + MapPolyline { + line.width: 3 + line.color: 'green' + path: [ + { latitude: -27, longitude: 153.0 }, + { latitude: -27, longitude: 154.1 }, + { latitude: -28, longitude: 153.5 }, + { latitude: -29, longitude: 153.5 } + ] + } + } + \endcode + + \image api-mappolyline.png +*/ + +QDeclarativeMapLineProperties::QDeclarativeMapLineProperties(QObject *parent) : + QObject(parent), + width_(1.0), + color_(Qt::black) +{ +} + +/*! + \internal +*/ +QColor QDeclarativeMapLineProperties::color() const +{ + return color_; +} + +/*! + \internal +*/ +void QDeclarativeMapLineProperties::setColor(const QColor &color) +{ + if (color_ == color) + return; + + color_ = color; + emit colorChanged(color_); +} + +/*! + \internal +*/ +qreal QDeclarativeMapLineProperties::width() const +{ + return width_; +} + +/*! + \internal +*/ +void QDeclarativeMapLineProperties::setWidth(qreal width) +{ + if (width_ == width) + return; + + width_ = width; + emit widthChanged(width_); +} + +struct Vertex +{ + QVector2D position; +}; + +QGeoMapPolylineGeometry::QGeoMapPolylineGeometry() +{ +} + +QList<QList<QDoubleVector2D> > QGeoMapPolylineGeometry::clipPath(const QGeoMap &map, + const QList<QGeoCoordinate> &path, + QDoubleVector2D &leftBoundWrapped) +{ + /* + * Approach: + * 1) project coordinates to wrapped web mercator, and do unwrapBelowX + * 2) if the scene is tilted, clip the geometry against the visible region (this may generate multiple polygons) + * 2.1) recalculate the origin and geoLeftBound to prevent these parameters from ending in unprojectable areas + * 2.2) ensure the left bound does not wrap around due to QGeoCoordinate <-> clipper conversions + */ + + srcOrigin_ = geoLeftBound_; + + double unwrapBelowX = 0; + leftBoundWrapped = map.geoProjection().wrapMapProjection(map.geoProjection().geoToMapProjection(geoLeftBound_)); + if (preserveGeometry_) + unwrapBelowX = leftBoundWrapped.x(); + + QList<QDoubleVector2D> wrappedPath; + wrappedPath.reserve(path.size()); + QDoubleVector2D wrappedLeftBound(qInf(), qInf()); + // 1) + for (int i = 0; i < path.size(); ++i) { + const QGeoCoordinate &coord = path.at(i); + if (!coord.isValid()) + continue; + + QDoubleVector2D wrappedProjection = map.geoProjection().wrapMapProjection(map.geoProjection().geoToMapProjection(coord)); + + // We can get NaN if the map isn't set up correctly, or the projection + // is faulty -- probably best thing to do is abort + if (!qIsFinite(wrappedProjection.x()) || !qIsFinite(wrappedProjection.y())) + return QList<QList<QDoubleVector2D> >(); + + const bool isPointLessThanUnwrapBelowX = (wrappedProjection.x() < leftBoundWrapped.x()); + // unwrap x to preserve geometry if moved to border of map + if (preserveGeometry_ && isPointLessThanUnwrapBelowX) { + double distance = wrappedProjection.x() - unwrapBelowX; + if (distance < 0.0) + distance += 1.0; + wrappedProjection.setX(unwrapBelowX + distance); + } + if (wrappedProjection.x() < wrappedLeftBound.x() || (wrappedProjection.x() == wrappedLeftBound.x() && wrappedProjection.y() < wrappedLeftBound.y())) { + wrappedLeftBound = wrappedProjection; + } + wrappedPath.append(wrappedProjection); + } + + // 2) + QList<QList<QDoubleVector2D> > clippedPaths; + const QList<QDoubleVector2D> &visibleRegion = map.geoProjection().visibleRegion(); + if (visibleRegion.size()) { + c2t::clip2tri clipper; + clipper.addSubjectPath(QClipperUtils::qListToPath(wrappedPath), false); + clipper.addClipPolygon(QClipperUtils::qListToPath(visibleRegion)); + Paths res = clipper.execute(c2t::clip2tri::Intersection); + clippedPaths = QClipperUtils::pathsToQList(res); + + // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X + QDoubleVector2D lb(qInf(), qInf()); + for (const QList<QDoubleVector2D> &path: clippedPaths) { + for (const QDoubleVector2D &p: path) { + if (p == leftBoundWrapped) { + lb = p; + break; + } else if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) { + // y-minimization needed to find the same point on polygon and border + lb = p; + } + } + } + if (qIsInf(lb.x())) + return QList<QList<QDoubleVector2D> >(); + + // 2.2) Prevent the conversion to and from clipper from introducing negative offsets which + // in turn will make the geometry wrap around. + lb.setX(qMax(wrappedLeftBound.x(), lb.x())); + leftBoundWrapped = lb; + } else { + clippedPaths.append(wrappedPath); + } + + return clippedPaths; +} + +void QGeoMapPolylineGeometry::pathToScreen(const QGeoMap &map, + const QList<QList<QDoubleVector2D> > &clippedPaths, + const QDoubleVector2D &leftBoundWrapped) +{ + // 3) project the resulting geometry to screen position and calculate screen bounds + double minX = qInf(); + double minY = qInf(); + double maxX = -qInf(); + double maxY = -qInf(); + + srcOrigin_ = map.geoProjection().mapProjectionToGeo(map.geoProjection().unwrapMapProjection(leftBoundWrapped)); + QDoubleVector2D origin = map.geoProjection().wrappedMapProjectionToItemPosition(leftBoundWrapped); + for (const QList<QDoubleVector2D> &path: clippedPaths) { + QDoubleVector2D lastAddedPoint; + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D point = map.geoProjection().wrappedMapProjectionToItemPosition(path.at(i)); + + point = point - origin; // (0,0) if point == geoLeftBound_ + + minX = qMin(point.x(), minX); + minY = qMin(point.y(), minY); + maxX = qMax(point.x(), maxX); + maxY = qMax(point.y(), maxY); + + if (i == 0) { + srcPoints_ << point.x() << point.y(); + srcPointTypes_ << QPainterPath::MoveToElement; + lastAddedPoint = point; + } else { + if ((point - lastAddedPoint).manhattanLength() > 3 || + i == path.size() - 1) { + srcPoints_ << point.x() << point.y(); + srcPointTypes_ << QPainterPath::LineToElement; + lastAddedPoint = point; + } + } + } + } + + sourceBounds_ = QRectF(QPointF(minX, minY), QPointF(maxX, maxY)); +} + +/*! + \internal +*/ +void QGeoMapPolylineGeometry::updateSourcePoints(const QGeoMap &map, + const QList<QGeoCoordinate> &path, + const QGeoCoordinate geoLeftBound) +{ + if (!sourceDirty_) + return; + + geoLeftBound_ = geoLeftBound; + + // clear the old data and reserve enough memory + srcPoints_.clear(); + srcPoints_.reserve(path.size() * 2); + srcPointTypes_.clear(); + srcPointTypes_.reserve(path.size()); + + /* + * Approach: + * 1) project coordinates to wrapped web mercator, and do unwrapBelowX + * 2) if the scene is tilted, clip the geometry against the visible region (this may generate multiple polygons) + * 3) project the resulting geometry to screen position and calculate screen bounds + */ + + QDoubleVector2D leftBoundWrapped; + // 1, 2) + const QList<QList<QDoubleVector2D> > &clippedPaths = clipPath(map, path, leftBoundWrapped); + + // 3) + pathToScreen(map, clippedPaths, leftBoundWrapped); +} + +//////////////////////////////////////////////////////////////////////////// +#if 0 // Old polyline to viewport clipping code. Retaining it for now. +/* Polyline clip */ + +enum ClipPointType { + InsidePoint = 0x00, + LeftPoint = 0x01, + RightPoint = 0x02, + BottomPoint = 0x04, + TopPoint = 0x08 +}; + +static inline int clipPointType(qreal x, qreal y, const QRectF &rect) +{ + int type = InsidePoint; + if (x < rect.left()) + type |= LeftPoint; + else if (x > rect.right()) + type |= RightPoint; + if (y < rect.top()) + type |= TopPoint; + else if (y > rect.bottom()) + type |= BottomPoint; + return type; +} + +static void clipSegmentToRect(qreal x0, qreal y0, qreal x1, qreal y1, + const QRectF &clipRect, + QVector<qreal> &outPoints, + QVector<QPainterPath::ElementType> &outTypes) +{ + int type0 = clipPointType(x0, y0, clipRect); + int type1 = clipPointType(x1, y1, clipRect); + bool accept = false; + + while (true) { + if (!(type0 | type1)) { + accept = true; + break; + } else if (type0 & type1) { + break; + } else { + qreal x = 0.0; + qreal y = 0.0; + int outsideType = type0 ? type0 : type1; + + if (outsideType & BottomPoint) { + x = x0 + (x1 - x0) * (clipRect.bottom() - y0) / (y1 - y0); + y = clipRect.bottom() - 0.1; + } else if (outsideType & TopPoint) { + x = x0 + (x1 - x0) * (clipRect.top() - y0) / (y1 - y0); + y = clipRect.top() + 0.1; + } else if (outsideType & RightPoint) { + y = y0 + (y1 - y0) * (clipRect.right() - x0) / (x1 - x0); + x = clipRect.right() - 0.1; + } else if (outsideType & LeftPoint) { + y = y0 + (y1 - y0) * (clipRect.left() - x0) / (x1 - x0); + x = clipRect.left() + 0.1; + } + + if (outsideType == type0) { + x0 = x; + y0 = y; + type0 = clipPointType(x0, y0, clipRect); + } else { + x1 = x; + y1 = y; + type1 = clipPointType(x1, y1, clipRect); + } + } + } + + if (accept) { + if (outPoints.size() >= 2) { + qreal lastX, lastY; + lastY = outPoints.at(outPoints.size() - 1); + lastX = outPoints.at(outPoints.size() - 2); + + if (!qFuzzyCompare(lastY, y0) || !qFuzzyCompare(lastX, x0)) { + outTypes << QPainterPath::MoveToElement; + outPoints << x0 << y0; + } + } else { + outTypes << QPainterPath::MoveToElement; + outPoints << x0 << y0; + } + + outTypes << QPainterPath::LineToElement; + outPoints << x1 << y1; + } +} + +static void clipPathToRect(const QVector<qreal> &points, + const QVector<QPainterPath::ElementType> &types, + const QRectF &clipRect, + QVector<qreal> &outPoints, + QVector<QPainterPath::ElementType> &outTypes) +{ + outPoints.clear(); + outPoints.reserve(points.size()); + outTypes.clear(); + outTypes.reserve(types.size()); + + qreal lastX, lastY; + for (int i = 0; i < types.size(); ++i) { + if (i > 0 && types[i] != QPainterPath::MoveToElement) { + qreal x = points[i * 2], y = points[i * 2 + 1]; + clipSegmentToRect(lastX, lastY, x, y, clipRect, outPoints, outTypes); + } + + lastX = points[i * 2]; + lastY = points[i * 2 + 1]; + } +} +#endif +/*! + \internal +*/ +void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map, + qreal strokeWidth) +{ + if (!screenDirty_) + return; + + QPointF origin = map.geoProjection().coordinateToItemPosition(srcOrigin_, false).toPointF(); + + if (!qIsFinite(origin.x()) || !qIsFinite(origin.y()) || srcPointTypes_.size() < 2) { // the line might have been clipped away. + clear(); + return; + } + + // Create the viewport rect in the same coordinate system + // as the actual points + QRectF viewport(0, 0, map.viewportWidth(), map.viewportHeight()); + viewport.adjust(-strokeWidth, -strokeWidth, strokeWidth, strokeWidth); + viewport.translate(-1 * origin); + + // The geometry has already been clipped against the visible region projection in wrapped mercator space. + QVector<qreal> points = srcPoints_; + QVector<QPainterPath::ElementType> types = srcPointTypes_; + + QVectorPath vp(points.data(), types.size(), types.data()); + QTriangulatingStroker ts; + // viewport is not used in the call below. + ts.process(vp, QPen(QBrush(Qt::black), strokeWidth), viewport, QPainter::Qt4CompatiblePainting); + + clear(); + + // Nothing is on the screen + if (ts.vertexCount() == 0) + return; + + // QTriangulatingStroker#vertexCount is actually the length of the array, + // not the number of vertices + screenVertices_.reserve(ts.vertexCount()); + + QRectF bb; + + QPointF pt; + const float *vs = ts.vertices(); + for (int i = 0; i < (ts.vertexCount()/2*2); i += 2) { + pt = QPointF(vs[i], vs[i + 1]); + screenVertices_ << pt; + + if (!qIsFinite(pt.x()) || !qIsFinite(pt.y())) + break; + + if (!bb.contains(pt)) { + if (pt.x() < bb.left()) + bb.setLeft(pt.x()); + + if (pt.x() > bb.right()) + bb.setRight(pt.x()); + + if (pt.y() < bb.top()) + bb.setTop(pt.y()); + + if (pt.y() > bb.bottom()) + bb.setBottom(pt.y()); + } + } + + screenBounds_ = bb; + this->translate( -1 * sourceBounds_.topLeft()); +} + +QDeclarativePolylineMapItem::QDeclarativePolylineMapItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), dirtyMaterial_(true), updatingGeometry_(false) +{ + setFlag(ItemHasContents, true); + QObject::connect(&line_, SIGNAL(colorChanged(QColor)), + this, SLOT(updateAfterLinePropertiesChanged())); + QObject::connect(&line_, SIGNAL(widthChanged(qreal)), + this, SLOT(updateAfterLinePropertiesChanged())); +} + +QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem() +{ +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged() +{ + // mark dirty just in case we're a width change + geometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (map) { + geometry_.markSourceDirty(); + polishAndUpdate(); + } +} + +/*! + \qmlproperty list<coordinate> MapPolyline::path + + This property holds the ordered list of coordinates which + define the polyline. +*/ + +QJSValue QDeclarativePolylineMapItem::path() const +{ + QQmlContext *context = QQmlEngine::contextForObject(this); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped<QV4::ArrayObject> pathArray(scope, v4->newArrayObject(geopath_.path().length())); + for (int i = 0; i < geopath_.path().length(); ++i) { + const QGeoCoordinate &c = geopath_.coordinateAt(i); + + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c))); + pathArray->putIndexed(i, cv); + } + + return QJSValue(v4, pathArray.asReturnedValue()); +} + +void QDeclarativePolylineMapItem::setPath(const QJSValue &value) +{ + if (!value.isArray()) + return; + + QList<QGeoCoordinate> pathList; + quint32 length = value.property(QStringLiteral("length")).toUInt(); + for (quint32 i = 0; i < length; ++i) { + bool ok; + QGeoCoordinate c = parseCoordinate(value.property(i), &ok); + + if (!ok || !c.isValid()) { + qmlWarning(this) << "Unsupported path type"; + return; + } + + pathList.append(c); + } + + setPathFromGeoList(pathList); +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate> &path) +{ + if (geopath_.path() == path) + return; + + geopath_.setPath(path); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod int MapPolyline::pathLength() + + Returns the number of coordinates of the polyline. + + \since Qt Location 5.6 + + \sa path +*/ +int QDeclarativePolylineMapItem::pathLength() const +{ + return geopath_.path().length(); +} + +/*! + \qmlmethod void MapPolyline::addCoordinate(coordinate) + + Adds a coordinate to the end of the path. + + \sa insertCoordinate, removeCoordinate, path +*/ +void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate) +{ + geopath_.addCoordinate(coordinate); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolyline::insertCoordinate(index, coordinate) + + Inserts a \a coordinate to the path at the given \a index. + + \since Qt Location 5.6 + + \sa addCoordinate, removeCoordinate, path +*/ +void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordinate &coordinate) +{ + if (index < 0 || index > geopath_.path().length()) + return; + + geopath_.insertCoordinate(index, coordinate); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolyline::replaceCoordinate(index, coordinate) + + Replaces the coordinate in the current path at the given \a index + with the new \a coordinate. + + \since Qt Location 5.6 + + \sa addCoordinate, insertCoordinate, removeCoordinate, path +*/ +void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordinate &coordinate) +{ + if (index < 0 || index >= geopath_.path().length()) + return; + + geopath_.replaceCoordinate(index, coordinate); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod coordinate MapPolyline::coordinateAt(index) + + Gets the coordinate of the polyline at the given \a index. + If the index is outside the path's bounds then an invalid + coordinate is returned. + + \since Qt Location 5.6 +*/ +QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const +{ + if (index < 0 || index >= geopath_.path().length()) + return QGeoCoordinate(); + + return geopath_.coordinateAt(index); +} + +/*! + \qmlmethod coordinate MapPolyline::containsCoordinate(coordinate) + + Returns true if the given \a coordinate is part of the path. + + \since Qt Location 5.6 +*/ +bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coordinate) +{ + return geopath_.containsCoordinate(coordinate); +} + +/*! + \qmlmethod void MapPolyline::removeCoordinate(coordinate) + + Removes \a coordinate from the path. If there are multiple instances of the + same coordinate, the one added last is removed. + + If \a coordinate is not in the path this method does nothing. + + \sa addCoordinate, insertCoordinate, path +*/ +void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordinate) +{ + int length = geopath_.path().length(); + geopath_.removeCoordinate(coordinate); + if (geopath_.path().length() == length) + return; + geometry_.markSourceDirty(); + polishAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolyline::removeCoordinate(index) + + Removes a coordinate from the path at the given \a index. + + If \a index is invalid then this method does nothing. + + \since Qt Location 5.6 + + \sa addCoordinate, insertCoordinate, path +*/ +void QDeclarativePolylineMapItem::removeCoordinate(int index) +{ + if (index < 0 || index >= geopath_.path().length()) + return; + + geopath_.removeCoordinate(index); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlpropertygroup Location::MapPolyline::line + \qmlproperty int MapPolyline::line.width + \qmlproperty color MapPolyline::line.color + + This property is part of the line property group. The line + property group holds the width and color used to draw the line. + + The width is in pixels and is independent of the zoom level of the map. + The default values correspond to a black border with a width of 1 pixel. + + For no line, use a width of 0 or a transparent color. +*/ + +QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line() +{ + return &line_; +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + // TODO: change the algorithm to preserve the distances and size! + QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false); + QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false); + if (!newCenter.isValid() || !oldCenter.isValid()) + return; + double offsetLongi = newCenter.longitude() - oldCenter.longitude(); + double offsetLati = newCenter.latitude() - oldCenter.latitude(); + if (offsetLati == 0.0 && offsetLongi == 0.0) + return; + + geopath_.translate(offsetLati, offsetLongi); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + return; + + geometry_.setPreserveGeometry(true, geometry_.geoLeftBound()); + markSourceDirtyAndUpdate(); +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::updatePolish() +{ + if (!map() || geopath_.path().length() == 0) + return; + + QScopedValueRollback<bool> rollback(updatingGeometry_); + updatingGeometry_ = true; + + geometry_.updateSourcePoints(*map(), geopath_.path(), geopath_.boundingGeoRectangle().topLeft()); + geometry_.updateScreenPoints(*map(), line_.width()); + + setWidth(geometry_.sourceBoundingBox().width()); + setHeight(geometry_.sourceBoundingBox().height()); + + setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft()); +} + +void QDeclarativePolylineMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \internal +*/ +QSGNode *QDeclarativePolylineMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + + MapPolylineNode *node = static_cast<MapPolylineNode *>(oldNode); + + if (!node) { + node = new MapPolylineNode(); + } + + //TODO: update only material + if (geometry_.isScreenDirty() || dirtyMaterial_ || !oldNode) { + node->update(line_.color(), &geometry_); + geometry_.setPreserveGeometry(false); + geometry_.markClean(); + dirtyMaterial_ = false; + } + return node; +} + +bool QDeclarativePolylineMapItem::contains(const QPointF &point) const +{ + QVector<QPointF> vertices = geometry_.vertices(); + QPolygonF tri; + for (int i = 0; i < vertices.size(); ++i) { + tri << vertices[i]; + if (tri.size() == 3) { + if (tri.containsPoint(point,Qt::OddEvenFill)) + return true; + tri.remove(0); + } + } + + return false; +} + +const QGeoShape &QDeclarativePolylineMapItem::geoShape() const +{ + return geopath_; +} + +////////////////////////////////////////////////////////////////////// + +/*! + \internal +*/ +MapPolylineNode::MapPolylineNode() : + geometry_(QSGGeometry::defaultAttributes_Point2D(),0), + blocked_(true) +{ + geometry_.setDrawingMode(QSGGeometry::DrawTriangleStrip); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&geometry_); +} + + +/*! + \internal +*/ +MapPolylineNode::~MapPolylineNode() +{ +} + +/*! + \internal +*/ +bool MapPolylineNode::isSubtreeBlocked() const +{ + return blocked_; +} + +/*! + \internal +*/ +void MapPolylineNode::update(const QColor &fillColor, + const QGeoMapItemGeometry *shape) +{ + if (shape->size() == 0) { + blocked_ = true; + return; + } else { + blocked_ = false; + } + + QSGGeometry *fill = QSGGeometryNode::geometry(); + shape->allocateAndFill(fill); + markDirty(DirtyGeometry); + + if (fillColor != fill_material_.color()) { + fill_material_.setColor(fillColor); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h b/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h new file mode 100644 index 00000000..abc1df2f --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPOLYLINEMAPITEM +#define QDECLARATIVEPOLYLINEMAPITEM + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qdeclarativegeomapitembase_p.h> +#include <QtLocation/private/qgeomapitemgeometry_p.h> + +#include <QtPositioning/QGeoPath> +#include <QtPositioning/private/qdoublevector2d_p.h> +#include <QSGGeometryNode> +#include <QSGFlatColorMaterial> + +QT_BEGIN_NAMESPACE + +class MapPolylineNode; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeMapLineProperties : public QObject +{ + Q_OBJECT + + Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + +public: + explicit QDeclarativeMapLineProperties(QObject *parent = 0); + + QColor color() const; + void setColor(const QColor &color); + + qreal width() const; + void setWidth(qreal width); + +Q_SIGNALS: + void widthChanged(qreal width); + void colorChanged(const QColor &color); + +private: + qreal width_; + QColor color_; +}; + +class QGeoMapPolylineGeometry : public QGeoMapItemGeometry +{ +public: + QGeoMapPolylineGeometry(); + + void updateSourcePoints(const QGeoMap &map, + const QList<QGeoCoordinate> &path, + const QGeoCoordinate geoLeftBound); + + void updateScreenPoints(const QGeoMap &map, + qreal strokeWidth); + +protected: + QList<QList<QDoubleVector2D> > clipPath(const QGeoMap &map, + const QList<QGeoCoordinate> &path, + QDoubleVector2D &leftBoundWrapped); + + void pathToScreen(const QGeoMap &map, + const QList<QList<QDoubleVector2D> > &clippedPaths, + const QDoubleVector2D &leftBoundWrapped); + +private: + QVector<qreal> srcPoints_; + QVector<QPainterPath::ElementType> srcPointTypes_; + + friend class QDeclarativeCircleMapItem; + friend class QDeclarativePolygonMapItem; + friend class QDeclarativeRectangleMapItem; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItem : public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + + Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *line READ line CONSTANT) + +public: + explicit QDeclarativePolylineMapItem(QQuickItem *parent = 0); + ~QDeclarativePolylineMapItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) Q_DECL_OVERRIDE; + //from QuickItem + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; + + Q_INVOKABLE int pathLength() const; + Q_INVOKABLE void addCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void insertCoordinate(int index, const QGeoCoordinate &coordinate); + Q_INVOKABLE void replaceCoordinate(int index, const QGeoCoordinate &coordinate); + Q_INVOKABLE QGeoCoordinate coordinateAt(int index) const; + Q_INVOKABLE bool containsCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void removeCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void removeCoordinate(int index); + + QJSValue path() const; + virtual void setPath(const QJSValue &value); + + bool contains(const QPointF &point) const Q_DECL_OVERRIDE; + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; + + QDeclarativeMapLineProperties *line(); + +Q_SIGNALS: + void pathChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void setPathFromGeoList(const QList<QGeoCoordinate> &path); + void updatePolish() Q_DECL_OVERRIDE; + +protected Q_SLOTS: + void markSourceDirtyAndUpdate(); + void updateAfterLinePropertiesChanged(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) Q_DECL_OVERRIDE; + +private: + void pathPropertyChanged(); + + QGeoPath geopath_; + QDeclarativeMapLineProperties line_; + QColor color_; + bool dirtyMaterial_; + QGeoMapPolylineGeometry geometry_; + bool updatingGeometry_; +}; + +////////////////////////////////////////////////////////////////////// + +class MapPolylineNode : public QSGGeometryNode +{ + +public: + MapPolylineNode(); + ~MapPolylineNode(); + + void update(const QColor &fillColor, const QGeoMapItemGeometry *shape); + bool isSubtreeBlocked() const; + +private: + QSGFlatColorMaterial fill_material_; + QSGGeometry geometry_; + bool blocked_; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeMapLineProperties) +QML_DECLARE_TYPE(QDeclarativePolylineMapItem) + +#endif /* QDECLARATIVEPOLYLINEMAPITEM_H_ */ diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp b/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp new file mode 100644 index 00000000..8abd58dd --- /dev/null +++ b/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp @@ -0,0 +1,380 @@ +/**************************************************************************** +** +** 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 "qdeclarativerectanglemapitem_p.h" +#include "qdeclarativepolygonmapitem_p.h" +#include "qgeocameracapabilities_p.h" +#include "qlocationutils_p.h" +#include <QPainterPath> +#include <qnumeric.h> +#include <QRectF> +#include <QPointF> +#include <QtLocation/private/qgeomap_p.h> +#include <QtPositioning/private/qdoublevector2d_p.h> +#include <QtCore/QScopedValueRollback> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapRectangle + \instantiates QDeclarativeRectangleMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.5 + + \brief The MapRectangle type displays a rectangle on a Map. + + The MapRectangle type displays a rectangle on a Map. Rectangles are a + special case of Polygon with exactly 4 vertices and 4 "straight" edges. In + this case, "straight" means that the top-left point has the same latitude + as the top-right point (the top edge), and the bottom-left point has the + same latitude as the bottom-right point (the bottom edge). Similarly, the + points on the left side have the same longitude, and the points on the + right side have the same longitude. + + To specify the rectangle, it requires a \l topLeft and \l bottomRight point, + both given by a \l {coordinate}. + + By default, the rectangle is displayed with transparent fill and a 1-pixel + thick black border. This can be changed using the \l color, \l border.color + and \l border.width properties. + + \note Similar to the \l MapPolygon type, MapRectangles are geographic + items, thus dragging a MapRectangle causes its vertices to be recalculated + in the geographic coordinate space. Apparent stretching of the item + occurs when dragged to the a different latitude, however, its edges + remain straight. + + \section2 Performance + + MapRectangles have a rendering cost identical to a MapPolygon with 4 + vertices. + + Like the other map objects, MapRectangle is normally drawn without a smooth + appearance. Setting the \l opacity property will force the object to be + blended, which decreases performance considerably depending on the hardware + in use. + + \section2 Example Usage + + The following snippet shows a map containing a MapRectangle, spanning + from (-27, 153) to (-28, 153.5), near Brisbane, Australia. The rectangle + is filled in green, with a 2 pixel black border. + + \code + Map { + MapRectangle { + color: 'green' + border.width: 2 + topLeft { + latitude: -27 + longitude: 153 + } + bottomRight { + latitude: -28 + longitude: 153.5 + } + } + } + \endcode + + \image api-maprectangle.png +*/ + +QDeclarativeRectangleMapItem::QDeclarativeRectangleMapItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), color_(Qt::transparent), dirtyMaterial_(true), + updatingGeometry_(false) +{ + setFlag(ItemHasContents, true); + QObject::connect(&border_, SIGNAL(colorChanged(QColor)), + this, SLOT(markSourceDirtyAndUpdate())); + QObject::connect(&border_, SIGNAL(widthChanged(qreal)), + this, SLOT(markSourceDirtyAndUpdate())); +} + +QDeclarativeRectangleMapItem::~QDeclarativeRectangleMapItem() +{ +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (map) + markSourceDirtyAndUpdate(); +} + +/*! + \qmlpropertygroup Location::MapRectangle::border + \qmlproperty int MapRectangle::border.width + \qmlproperty color MapRectangle::border.color + + This property is part of the border property group. The border property group + holds the width and color used to draw the border of the rectangle. + The width is in pixels and is independent of the zoom level of the map. + + The default values correspond to a black border with a width of 1 pixel. + For no line, use a width of 0 or a transparent color. +*/ +QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border() +{ + return &border_; +} + +/*! + \qmlproperty coordinate MapRectangle::topLeft + + This property holds the top-left coordinate of the MapRectangle which + can be used to retrieve its longitude, latitude and altitude. +*/ +void QDeclarativeRectangleMapItem::setTopLeft(const QGeoCoordinate &topLeft) +{ + if (rectangle_.topLeft() == topLeft) + return; + + rectangle_.setTopLeft(topLeft); + + markSourceDirtyAndUpdate(); + emit topLeftChanged(topLeft); +} + +QGeoCoordinate QDeclarativeRectangleMapItem::topLeft() +{ + return rectangle_.topLeft(); +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \qmlproperty coordinate MapRectangle::bottomRight + + This property holds the bottom-right coordinate of the MapRectangle which + can be used to retrieve its longitude, latitude and altitude. +*/ +void QDeclarativeRectangleMapItem::setBottomRight(const QGeoCoordinate &bottomRight) +{ + if (rectangle_.bottomRight() == bottomRight) + return; + + rectangle_.setBottomRight(bottomRight); + + markSourceDirtyAndUpdate(); + emit bottomRightChanged(bottomRight); +} + +QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight() +{ + return rectangle_.bottomRight(); +} + +/*! + \qmlproperty color MapRectangle::color + + This property holds the fill color of the rectangle. For no fill, use + a transparent color. +*/ +QColor QDeclarativeRectangleMapItem::color() const +{ + return color_; +} + +void QDeclarativeRectangleMapItem::setColor(const QColor &color) +{ + if (color_ == color) + return; + color_ = color; + dirtyMaterial_ = true; + polishAndUpdate(); + emit colorChanged(color_); +} + +/*! + \qmlproperty real MapRectangle::opacity + + This property holds the opacity of the item. Opacity is specified as a + number between 0 (fully transparent) and 1 (fully opaque). The default is 1. + + An item with 0 opacity will still receive mouse events. To stop mouse events, set the + visible property of the item to false. +*/ + +/*! + \internal +*/ +QSGNode *QDeclarativeRectangleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + + MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode); + + if (!node) { + node = new MapPolygonNode(); + } + + //TODO: update only material + if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { + node->update(color_, border_.color(), &geometry_, &borderGeometry_); + geometry_.setPreserveGeometry(false); + borderGeometry_.setPreserveGeometry(false); + geometry_.markClean(); + borderGeometry_.markClean(); + dirtyMaterial_ = false; + } + return node; +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::updatePolish() +{ + if (!map() || !topLeft().isValid() || !bottomRight().isValid()) + return; + + QScopedValueRollback<bool> rollback(updatingGeometry_); + updatingGeometry_ = true; + + QList<QGeoCoordinate> path; + path << rectangle_.topLeft(); + path << QGeoCoordinate(rectangle_.topLeft().latitude(), rectangle_.bottomRight().longitude()); + path << rectangle_.bottomRight(); + path << QGeoCoordinate(rectangle_.bottomRight().latitude(), rectangle_.topLeft().longitude()); + + geometry_.setPreserveGeometry(true, rectangle_.topLeft()); + geometry_.updateSourcePoints(*map(), path); + geometry_.updateScreenPoints(*map()); + + QList<QGeoMapItemGeometry *> geoms; + geoms << &geometry_; + borderGeometry_.clear(); + + if (border_.color() != Qt::transparent && border_.width() > 0) { + QList<QGeoCoordinate> closedPath = path; + closedPath << closedPath.first(); + + borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); + const QGeoCoordinate &geometryOrigin = geometry_.origin(); + + borderGeometry_.srcPoints_.clear(); + borderGeometry_.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = map()->geoProjection().geoToWrappedMapProjection(geometryOrigin); + borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); + borderGeometry_.updateScreenPoints(*map(), border_.width()); + + geoms << &borderGeometry_; + } else { + borderGeometry_.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + setWidth(combined.width()); + setHeight(combined.height()); + + setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset()); +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + return; + + geometry_.setPreserveGeometry(true, rectangle_.topLeft()); + borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); + markSourceDirtyAndUpdate(); +} + +/*! + \internal +*/ +bool QDeclarativeRectangleMapItem::contains(const QPointF &point) const +{ + return (geometry_.contains(point) || borderGeometry_.contains(point)); +} + +const QGeoShape &QDeclarativeRectangleMapItem::geoShape() const +{ + return rectangle_; +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + // TODO: change the algorithm to preserve the distances and size + QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false); + QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false); + if (!newCenter.isValid() || !oldCenter.isValid()) + return; + double offsetLongi = newCenter.longitude() - oldCenter.longitude(); + double offsetLati = newCenter.latitude() - oldCenter.latitude(); + if (offsetLati == 0.0 && offsetLongi == 0.0) + return; + + rectangle_.translate(offsetLati, offsetLongi); + geometry_.setPreserveGeometry(true, rectangle_.topLeft()); + borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); + markSourceDirtyAndUpdate(); + emit topLeftChanged(rectangle_.topLeft()); + emit bottomRightChanged(rectangle_.bottomRight()); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h b/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h new file mode 100644 index 00000000..6e6ea5b1 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERECTANGLEMAPITEM_H_ +#define QDECLARATIVERECTANGLEMAPITEM_H_ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeomapitembase_p.h> +#include <QtLocation/private/qgeomapitemgeometry_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p.h> + +#include <QSGGeometryNode> +#include <QSGFlatColorMaterial> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItem: public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + + Q_PROPERTY(QGeoCoordinate topLeft READ topLeft WRITE setTopLeft NOTIFY topLeftChanged) + Q_PROPERTY(QGeoCoordinate bottomRight READ bottomRight WRITE setBottomRight NOTIFY bottomRightChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *border READ border) + +public: + explicit QDeclarativeRectangleMapItem(QQuickItem *parent = 0); + ~QDeclarativeRectangleMapItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) Q_DECL_OVERRIDE; + //from QuickItem + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; + + QGeoCoordinate topLeft(); + void setTopLeft(const QGeoCoordinate ¢er); + + QGeoCoordinate bottomRight(); + void setBottomRight(const QGeoCoordinate ¢er); + + QColor color() const; + void setColor(const QColor &color); + + QDeclarativeMapLineProperties *border(); + + bool contains(const QPointF &point) const Q_DECL_OVERRIDE; + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; + +Q_SIGNALS: + void topLeftChanged(const QGeoCoordinate &topLeft); + void bottomRightChanged(const QGeoCoordinate &bottomRight); + void colorChanged(const QColor &color); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; + void updatePolish() Q_DECL_OVERRIDE; + +protected Q_SLOTS: + void markSourceDirtyAndUpdate(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) Q_DECL_OVERRIDE; + +private: + QGeoRectangle rectangle_; + QDeclarativeMapLineProperties border_; + QColor color_; + bool dirtyMaterial_; + QGeoMapPolygonGeometry geometry_; + QGeoMapPolylineGeometry borderGeometry_; + bool updatingGeometry_; +}; + +////////////////////////////////////////////////////////////////////// + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeRectangleMapItem) + +#endif /* QDECLARATIVERECTANGLEMAPITEM_H_ */ diff --git a/src/location/declarativemaps/qdeclarativeroutemapitem.cpp b/src/location/declarativemaps/qdeclarativeroutemapitem.cpp new file mode 100644 index 00000000..1fbeeeb7 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativeroutemapitem.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** 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 "qdeclarativeroutemapitem_p.h" +#include "qdeclarativepolylinemapitem_p.h" +#include "qgeocameracapabilities_p.h" +#include "qdeclarativegeoroute_p.h" + +#include <QtQml/QQmlInfo> +#include <QtGui/QPainter> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapRoute + \instantiates QDeclarativeRouteMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.0 + + \brief The MapRoute type displays a Route on a Map. + + The MapRoute type displays a Route obtained through a RouteModel or + other means, on the Map as a Polyline following the path of the Route. + + MapRoute is really a \l MapPolyline, but with the path specified using the + \l route property instead of directly in \l {coordinate}{coordinates}. + + By default, the route is displayed as a 1-pixel thick black line. This can + be changed using the \l line.width and \l line.color properties. + + \section2 Performance + + For notes about the performance on MapRoute, refer to the documentation for + \l MapPolyline. + + \section2 Example Usage + + Here is how to draw a \l{Route}{route} on a \l{Map}{map}: + + \snippet declarative/maps.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/maps.qml MapRoute +*/ + +/*! + \qmlpropertygroup Location::MapRoute::line + \qmlproperty int MapRoute::line.width + \qmlproperty color MapRoute::line.color + + This property is part of the line property group. The line + property group holds the width and color used to draw the line. + + The width is in pixels and is independent of the zoom level of the map. + The default values correspond to a black border with a width of 1 pixel. + + For no line, use a width of 0 or a transparent color. +*/ + + +QDeclarativeRouteMapItem::QDeclarativeRouteMapItem(QQuickItem *parent) +: QDeclarativePolylineMapItem(parent), route_(0) +{ + setFlag(ItemHasContents, true); +} + +QDeclarativeRouteMapItem::~QDeclarativeRouteMapItem() +{ +} + +/*! + \qmlproperty Route MapRoute::route + + This property holds the route to be drawn which can be used + to represent one geographical route. +*/ +QDeclarativeGeoRoute *QDeclarativeRouteMapItem::route() const +{ + return route_; +} + +void QDeclarativeRouteMapItem::setRoute(QDeclarativeGeoRoute *route) +{ + if (route_ == route) + return; + + route_ = route; + + connect(route_, SIGNAL(pathChanged()), this, SLOT(updateRoutePath())); + + if (route_) + setPathFromGeoList(route_->routePath()); + + emit routeChanged(route_); +} + +void QDeclarativeRouteMapItem::updateRoutePath() +{ + setPathFromGeoList(route_->routePath()); +} + +/*! + \internal void QDeclarativeRouteMapItem::setPath(const QJSValue &value) + + Used to disable path property on the RouteMapItem + */ +void QDeclarativeRouteMapItem::setPath(const QJSValue &value) +{ + Q_UNUSED(value); + qWarning() << "Can not set the path on QDeclarativeRouteMapItem." + << "Please use the route property instead."; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativeroutemapitem_p.h b/src/location/declarativemaps/qdeclarativeroutemapitem_p.h new file mode 100644 index 00000000..ad959837 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativeroutemapitem_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEROUTEMAPITEM_H_ +#define QDECLARATIVEROUTEMAPITEM_H_ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeomapitembase_p.h> +#include <QtLocation/private/qdeclarativegeomap_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QPen> +#include <QBrush> + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoRoute; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRouteMapItem : public QDeclarativePolylineMapItem +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeGeoRoute *route READ route WRITE setRoute NOTIFY routeChanged) + +public: + explicit QDeclarativeRouteMapItem(QQuickItem *parent = 0); + ~QDeclarativeRouteMapItem(); + + QDeclarativeGeoRoute *route() const; + void setRoute(QDeclarativeGeoRoute *route); + +Q_SIGNALS: + void routeChanged(const QDeclarativeGeoRoute *route); + +private slots: + void updateRoutePath(); + +protected: + void setPath(const QJSValue &value) Q_DECL_OVERRIDE; + +private: + QDeclarativeGeoRoute *route_; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeRouteMapItem) + +#endif /* QDECLARATIVEROUTEMAPITEM_H_ */ diff --git a/src/location/declarativemaps/qgeomapitemgeometry.cpp b/src/location/declarativemaps/qgeomapitemgeometry.cpp new file mode 100644 index 00000000..80f3e218 --- /dev/null +++ b/src/location/declarativemaps/qgeomapitemgeometry.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** 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 "qgeomapitemgeometry_p.h" +#include "qdeclarativegeomap_p.h" +#include "qlocationutils_p.h" +#include <QtQuick/QSGGeometry> +#include "qdoublevector2d_p.h" +#include <QtLocation/private/qgeomap_p.h> + +QT_BEGIN_NAMESPACE + +QGeoMapItemGeometry::QGeoMapItemGeometry() +: sourceDirty_(true), screenDirty_(true), clipToViewport_(true), preserveGeometry_(false) +{ +} + +/*! + \internal +*/ +void QGeoMapItemGeometry::translate(const QPointF &offset) +{ + for (int i = 0; i < screenVertices_.size(); ++i) + screenVertices_[i] += offset; + + firstPointOffset_ += offset; + screenOutline_.translate(offset); + screenBounds_.translate(offset); +} + +/*! + \internal +*/ +void QGeoMapItemGeometry::allocateAndFill(QSGGeometry *geom) const +{ + const QVector<QPointF> &vx = screenVertices_; + const QVector<quint32> &ix = screenIndices_; + + if (isIndexed()) { + geom->allocate(vx.size(), ix.size()); + if (geom->indexType() == QSGGeometry::UnsignedShortType) { + quint16 *its = geom->indexDataAsUShort(); + for (int i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } else if (geom->indexType() == QSGGeometry::UnsignedIntType) { + quint32 *its = geom->indexDataAsUInt(); + for (int i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } + } else { + geom->allocate(vx.size()); + } + + QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); + for (int i = 0; i < vx.size(); ++i) + pts[i].set(vx[i].x(), vx[i].y()); +} + +/*! + \internal +*/ +QRectF QGeoMapItemGeometry::translateToCommonOrigin(const QList<QGeoMapItemGeometry *> &geoms) +{ + QGeoCoordinate origin = geoms.at(0)->origin(); + + QPainterPath brects; + + // first get max offset + QPointF maxOffset = geoms.at(0)->firstPointOffset(); + foreach (QGeoMapItemGeometry *g, geoms) { +#ifndef QT_NO_DEBUG + //Q_ASSERT(g->origin() == origin); // this might fail on clipper clipping inaccuracies, so better to remove it in production + if (!qFuzzyCompare(origin.latitude(), g->origin().latitude())) { + qWarning("translateToCommonOrigin: Origins differ!"); + } + if (!qFuzzyCompare(origin.longitude(), g->origin().longitude())) { + qWarning("translateToCommonOrigin: Origins differ!"); + } +#endif + QPointF o = g->firstPointOffset(); + maxOffset.setX(qMax(o.x(), maxOffset.x())); + maxOffset.setY(qMax(o.y(), maxOffset.y())); + } + + // then translate everything + foreach (QGeoMapItemGeometry *g, geoms) { + g->translate(maxOffset - g->firstPointOffset()); + brects.addRect(g->sourceBoundingBox()); + } + + return brects.boundingRect(); +} + +/*! + \internal +*/ +double QGeoMapItemGeometry::geoDistanceToScreenWidth(const QGeoMap &map, + const QGeoCoordinate &fromCoord, + const QGeoCoordinate &toCoord) +{ + // Do not wrap around half the globe + Q_ASSERT(!qFuzzyCompare(fromCoord.longitude(), toCoord.longitude())); + + QGeoCoordinate mapMid = map.geoProjection().itemPositionToCoordinate(QDoubleVector2D(map.viewportWidth()/2.0, 0)); + double halfGeoDist = toCoord.longitude() - fromCoord.longitude(); + if (toCoord.longitude() < fromCoord.longitude()) + halfGeoDist += 360; + halfGeoDist /= 2.0; + QGeoCoordinate geoDelta = QGeoCoordinate(0, + QLocationUtils::wrapLong(mapMid.longitude() + halfGeoDist)); + QDoubleVector2D halfScreenDist = map.geoProjection().coordinateToItemPosition(geoDelta, false) + - QDoubleVector2D(map.viewportWidth()/2.0, 0); + return halfScreenDist.x() * 2.0; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qgeomapitemgeometry_p.h b/src/location/declarativemaps/qgeomapitemgeometry_p.h new file mode 100644 index 00000000..595107ae --- /dev/null +++ b/src/location/declarativemaps/qgeomapitemgeometry_p.h @@ -0,0 +1,150 @@ +/**************************************************************************** + ** + ** 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$ + ** + ****************************************************************************/ + +#ifndef QGEOMAPITEMGEOMETRY_H +#define QGEOMAPITEMGEOMETRY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> + +#include <QPainterPath> +#include <QPointF> +#include <QRectF> +#include <QVector> +#include <QGeoCoordinate> +#include <QVector2D> +#include <QList> + +QT_BEGIN_NAMESPACE + +class QSGGeometry; +class QGeoMap; + +class QGeoMapItemGeometry +{ +public: + QGeoMapItemGeometry(); + + inline bool isSourceDirty() const { return sourceDirty_; } + inline bool isScreenDirty() const { return screenDirty_; } + inline void markSourceDirty() { sourceDirty_ = true; screenDirty_ = true; } + inline void markScreenDirty() { screenDirty_ = true; clipToViewport_ = true; } + inline void markFullScreenDirty() { screenDirty_ = true; clipToViewport_ = false;} + inline void markClean() { screenDirty_ = (sourceDirty_ = false); clipToViewport_ = true;} + + inline void setPreserveGeometry(bool value, const QGeoCoordinate &geoLeftBound = QGeoCoordinate()) + { + preserveGeometry_ = value; + if (preserveGeometry_) + geoLeftBound_ = geoLeftBound; + } + inline QGeoCoordinate geoLeftBound() { return geoLeftBound_; } + + inline QRectF sourceBoundingBox() const { return sourceBounds_; } + inline QRectF screenBoundingBox() const { return screenBounds_; } + + inline QPointF firstPointOffset() const { return firstPointOffset_; } + void translate(const QPointF &offset); + + inline const QGeoCoordinate &origin() const { return srcOrigin_; } + + inline bool contains(const QPointF &screenPoint) const { + return screenOutline_.contains(screenPoint); + } + + inline QVector2D vertex(quint32 index) const { + return QVector2D(screenVertices_[index]); + } + + inline QVector<QPointF> vertices() const { return screenVertices_; } + inline QVector<quint32> indices() const { return screenIndices_; } + + inline bool isIndexed() const { return (!screenIndices_.isEmpty()); } + + /* Size is # of triangles */ + inline quint32 size() const + { + if (isIndexed()) + return screenIndices_.size() / 3; + else + return screenVertices_.size() / 3; + } + + inline void clear() { firstPointOffset_ = QPointF(0,0); + screenVertices_.clear(); screenIndices_.clear(); } + + void allocateAndFill(QSGGeometry *geom) const; + + double geoDistanceToScreenWidth(const QGeoMap &map, + const QGeoCoordinate &fromCoord, + const QGeoCoordinate &toCoord); + + static QRectF translateToCommonOrigin(const QList<QGeoMapItemGeometry *> &geoms); + + +protected: + bool sourceDirty_; + bool screenDirty_; + bool clipToViewport_; + bool preserveGeometry_; + QGeoCoordinate geoLeftBound_; + + QPointF firstPointOffset_; + + QPainterPath screenOutline_; + + QRectF sourceBounds_; + QRectF screenBounds_; + + QGeoCoordinate srcOrigin_; + + QVector<QPointF> screenVertices_; + QVector<quint32> screenIndices_; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPITEMGEOMETRY_H diff --git a/src/location/declarativemaps/qquickgeomapgesturearea.cpp b/src/location/declarativemaps/qquickgeomapgesturearea.cpp new file mode 100644 index 00000000..7b9a48f1 --- /dev/null +++ b/src/location/declarativemaps/qquickgeomapgesturearea.cpp @@ -0,0 +1,1288 @@ +/**************************************************************************** +** +** 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 "qquickgeomapgesturearea_p.h" +#include "qquickgeocoordinateanimation_p.h" +#include "qdeclarativegeomap_p.h" +#include "error_messages.h" + +#include <QtGui/QGuiApplication> +#include <QtGui/qevent.h> +#include <QtGui/QWheelEvent> +#include <QtGui/QStyleHints> +#include <QtQml/qqmlinfo.h> +#include <QtQuick/QQuickWindow> +#include <QPropertyAnimation> +#include <QDebug> +#include "math.h" +#include "qgeomap_p.h" +#include "qdoublevector2d_p.h" + +#define QML_MAP_FLICK_DEFAULTMAXVELOCITY 2500 +#define QML_MAP_FLICK_MINIMUMDECELERATION 500 +#define QML_MAP_FLICK_DEFAULTDECELERATION 2500 +#define QML_MAP_FLICK_MAXIMUMDECELERATION 10000 + +#define QML_MAP_FLICK_VELOCITY_SAMPLE_PERIOD 50 +// FlickThreshold determines how far the "mouse" must have moved +// before we perform a flick. +static const int FlickThreshold = 20; +// Really slow flicks can be annoying. +const qreal MinimumFlickVelocity = 75.0; + +QT_BEGIN_NAMESPACE + + +/*! + \qmltype MapPinchEvent + \instantiates QGeoMapPinchEvent + \inqmlmodule QtLocation + + \brief MapPinchEvent type provides basic information about pinch event. + + MapPinchEvent type provides basic information about pinch event. They are + present in handlers of MapPinch (for example pinchStarted/pinchUpdated). Events are only + guaranteed to be valid for the duration of the handler. + + Except for the \l accepted property, all properties are read-only. + + \section2 Example Usage + + The following example enables the pinch gesture on a map and reacts to the + finished event. + + \code + Map { + id: map + gesture.enabled: true + gesture.onPinchFinished:{ + var coordinate1 = map.toCoordinate(gesture.point1) + var coordinate2 = map.toCoordinate(gesture.point2) + console.log("Pinch started at:") + console.log(" Points (" + gesture.point1.x + ", " + gesture.point1.y + ") - (" + gesture.point2.x + ", " + gesture.point2.y + ")") + console.log(" Coordinates (" + coordinate1.latitude + ", " + coordinate1.longitude + ") - (" + coordinate2.latitude + ", " + coordinate2.longitude + ")") + } + } + \endcode + + \ingroup qml-QtLocation5-maps + \since Qt Location 5.0 +*/ + +/*! + \qmlproperty QPoint QtLocation::MapPinchEvent::center + + This read-only property holds the current center point. +*/ + +/*! + \qmlproperty real QtLocation::MapPinchEvent::angle + + This read-only property holds the current angle between the two points in + the range -180 to 180. Positive values for the angles mean counter-clockwise + while negative values mean the clockwise direction. Zero degrees is at the + 3 o'clock position. +*/ + +/*! + \qmlproperty QPoint QtLocation::MapPinchEvent::point1 + \qmlproperty QPoint QtLocation::MapPinchEvent::point2 + + These read-only properties hold the actual touch points generating the pinch. + The points are not in any particular order. +*/ + +/*! + \qmlproperty int QtLocation::MapPinchEvent::pointCount + + This read-only property holds the number of points currently touched. + The MapPinch will not react until two touch points have initiated a gesture, + but will remain active until all touch points have been released. +*/ + +/*! + \qmlproperty bool QtLocation::MapPinchEvent::accepted + + Setting this property to false in the \c MapPinch::onPinchStarted handler + will result in no further pinch events being generated, and the gesture + ignored. +*/ + +/*! + \qmltype MapGestureArea + \instantiates QQuickGeoMapGestureArea + + \inqmlmodule QtLocation + + \brief The MapGestureArea type provides Map gesture interaction. + + MapGestureArea objects are used as part of a Map, to provide for panning, + flicking and pinch-to-zoom gesture used on touch displays. + + A MapGestureArea is automatically created with a new Map and available with + the \l{Map::gesture}{gesture} property. This is the only way + to create a MapGestureArea, and once created this way cannot be destroyed + without its parent Map. + + The two most commonly used properties of the MapGestureArea are the \l enabled + and \l acceptedGestures properties. Both of these must be set before a + MapGestureArea will have any effect upon interaction with the Map. + The \l flickDeceleration property controls how quickly the map pan slows after contact + is released while panning the map. + + \section2 Performance + + The MapGestureArea, when enabled, must process all incoming touch events in + order to track the shape and size of the "pinch". The overhead added on + touch events can be considered constant time. + + \section2 Example Usage + + The following example enables the pinch and pan gestures on the map, but not flicking. So the + map scrolling will halt immediately on releasing the mouse button / touch. + + \code + Map { + gesture.enabled: true + gesture.acceptedGestures: MapGestureArea.PinchGesture | MapGestureArea.PanGesture + } + \endcode + + \ingroup qml-QtLocation5-maps + \since Qt Location 5.0 +*/ + +/*! + \qmlproperty bool QtLocation::MapGestureArea::enabled + + This property holds whether the gestures are enabled. +*/ + +/*! + \qmlproperty bool QtLocation::MapGestureArea::pinchActive + + This read-only property holds whether pinch gesture is active. +*/ + +/*! + \qmlproperty bool QtLocation::MapGestureArea::panActive + + This read-only property holds whether pan gesture is active. + + \note Change notifications for this property were introduced in Qt 5.5. +*/ + +/*! + \qmlproperty real QtLocation::MapGestureArea::maximumZoomLevelChange + + This property holds the maximum zoom level change per pinch, essentially + meant to be used for setting the zoom sensitivity. + + It is an indicative measure calculated from the dimensions of the + map area, roughly corresponding how much zoom level could change with + maximum pinch zoom. Default value is 4.0, maximum value is 10.0 +*/ + +/*! + \qmlproperty real MapGestureArea::flickDeceleration + + This property holds the rate at which a flick will decelerate. + + The default value is 2500. +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::pinchStarted(PinchEvent event) + + This signal is emitted when a pinch gesture is started. + + The corresponding handler is \c onPinchStarted. + + \sa pinchUpdated, pinchFinished +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::pinchUpdated(PinchEvent event) + + This signal is emitted as the user's fingers move across the map, + after the \l pinchStarted signal is emitted. + + The corresponding handler is \c onPinchUpdated. + + \sa pinchStarted, pinchFinished +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::pinchFinished(PinchEvent event) + + This signal is emitted at the end of a pinch gesture. + + The corresponding handler is \c onPinchFinished. + + \sa pinchStarted, pinchUpdated +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::panStarted() + + This signal is emitted when the map begins to move due to user + interaction. Typically this means that the user is dragging a finger - + or a mouse with one of more mouse buttons pressed - on the map. + + The corresponding handler is \c onPanStarted. +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::panFinished() + + This signal is emitted when the map stops moving due to user + interaction. If a flick was generated, this signal is + emitted before flick starts. If a flick was not + generated, this signal is emitted when the + user stops dragging - that is a mouse or touch release. + + The corresponding handler is \c onPanFinished. + +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::flickStarted() + + This signal is emitted when the map is flicked. A flick + starts from the point where the mouse or touch was released, + while still in motion. + + The corresponding handler is \c onFlichStarted. +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::flickFinished() + + This signal is emitted when the map stops moving due to a flick. + + The corresponding handler is \c onFlickFinished. +*/ + +QQuickGeoMapGestureArea::QQuickGeoMapGestureArea(QDeclarativeGeoMap *map) + : QQuickItem(map), + m_map(0), + m_declarativeMap(map), + m_enabled(true), + m_acceptedGestures(PinchGesture | PanGesture | FlickGesture), + m_preventStealing(false), + m_panEnabled(true) +{ + m_flick.m_enabled = true, + m_flick.m_maxVelocity = QML_MAP_FLICK_DEFAULTMAXVELOCITY; + m_flick.m_deceleration = QML_MAP_FLICK_DEFAULTDECELERATION; + m_flick.m_animation = 0; + m_touchPointState = touchPoints0; + m_pinchState = pinchInactive; + m_flickState = flickInactive; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setMap(QGeoMap *map) +{ + if (m_map || !map) + return; + + m_map = map; + m_flick.m_animation = new QQuickGeoCoordinateAnimation(this); + m_flick.m_animation->setTargetObject(m_declarativeMap); + m_flick.m_animation->setProperty(QStringLiteral("center")); + m_flick.m_animation->setEasing(QEasingCurve(QEasingCurve::OutQuad)); + connect(m_flick.m_animation, &QQuickAbstractAnimation::stopped, this, &QQuickGeoMapGestureArea::handleFlickAnimationStopped); +} + +/*! + \qmlproperty bool QtQuick::MapGestureArea::preventStealing + This property holds whether the mouse events may be stolen from this + MapGestureArea. + + If a Map is placed within an item that filters child mouse + and touch events, such as Flickable, the mouse and touch events + may be stolen from the MapGestureArea if a gesture is recognized + by the parent item, e.g. a flick gesture. If preventStealing is + set to true, no item will steal the mouse and touch events. + + Note that setting preventStealing to true once an item has started + stealing events will have no effect until the next press event. + + By default this property is false. +*/ + +bool QQuickGeoMapGestureArea::preventStealing() const +{ + return m_preventStealing; +} + +void QQuickGeoMapGestureArea::setPreventStealing(bool prevent) +{ + if (prevent != m_preventStealing) { + m_preventStealing = prevent; + m_declarativeMap->setKeepMouseGrab(m_preventStealing && m_enabled); + m_declarativeMap->setKeepTouchGrab(m_preventStealing && m_enabled); + emit preventStealingChanged(); + } +} + +QQuickGeoMapGestureArea::~QQuickGeoMapGestureArea() +{ +} + +/*! + \qmlproperty enumeration QtLocation::MapGestureArea::acceptedGestures + + This property holds the gestures that will be active. By default + the zoom, pan and flick gestures are enabled. + + \list + \li MapGestureArea.NoGesture - Don't support any additional gestures (value: 0x0000). + \li MapGestureArea.PinchGesture - Support the map pinch gesture (value: 0x0001). + \li MapGestureArea.PanGesture - Support the map pan gesture (value: 0x0002). + \li MapGestureArea.FlickGesture - Support the map flick gesture (value: 0x0004). + \endlist +*/ + +QQuickGeoMapGestureArea::AcceptedGestures QQuickGeoMapGestureArea::acceptedGestures() const +{ + return m_acceptedGestures; +} + + +void QQuickGeoMapGestureArea::setAcceptedGestures(AcceptedGestures acceptedGestures) +{ + if (acceptedGestures == m_acceptedGestures) + return; + m_acceptedGestures = acceptedGestures; + + setPanEnabled(acceptedGestures & PanGesture); + setFlickEnabled(acceptedGestures & FlickGesture); + setPinchEnabled(acceptedGestures & PinchGesture); + + emit acceptedGesturesChanged(); +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::isPinchActive() const +{ + return m_pinchState == pinchActive; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::isPanActive() const +{ + return m_flickState == panActive || m_flickState == flickActive; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::enabled() const +{ + return m_enabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setEnabled(bool enabled) +{ + if (enabled == m_enabled) + return; + m_enabled = enabled; + + if (enabled) { + setPanEnabled(m_acceptedGestures & PanGesture); + setFlickEnabled(m_acceptedGestures & FlickGesture); + setPinchEnabled(m_acceptedGestures & PinchGesture); + } else { + setPanEnabled(false); + setFlickEnabled(false); + setPinchEnabled(false); + } + + emit enabledChanged(); +} + + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::pinchEnabled() const +{ + return m_pinch.m_enabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setPinchEnabled(bool enabled) +{ + if (enabled == m_pinch.m_enabled) + return; + m_pinch.m_enabled = enabled; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::panEnabled() const +{ + return m_panEnabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setPanEnabled(bool enabled) +{ + if (enabled == m_flick.m_enabled) + return; + m_panEnabled = enabled; + + // unlike the pinch, the pan existing functionality is to stop immediately + if (!enabled) + stopPan(); +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::flickEnabled() const +{ + return m_flick.m_enabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setFlickEnabled(bool enabled) +{ + if (enabled == m_flick.m_enabled) + return; + m_flick.m_enabled = enabled; + // unlike the pinch, the flick existing functionality is to stop immediately + if (!enabled) { + stopFlick(); + } +} + +/*! + \internal + Used internally to set the minimum zoom level of the gesture area. + The caller is responsible to only send values that are valid + for the map plugin. Negative values are ignored. + */ +void QQuickGeoMapGestureArea::setMinimumZoomLevel(qreal min) +{ + if (min >= 0) + m_pinch.m_zoom.m_minimum = min; +} + +/*! + \internal + */ +qreal QQuickGeoMapGestureArea::minimumZoomLevel() const +{ + return m_pinch.m_zoom.m_minimum; +} + +/*! + \internal + Used internally to set the maximum zoom level of the gesture area. + The caller is responsible to only send values that are valid + for the map plugin. Negative values are ignored. + */ +void QQuickGeoMapGestureArea::setMaximumZoomLevel(qreal max) +{ + if (max >= 0) + m_pinch.m_zoom.m_maximum = max; +} + +/*! + \internal + */ +qreal QQuickGeoMapGestureArea::maximumZoomLevel() const +{ + return m_pinch.m_zoom.m_maximum; +} + +/*! + \internal +*/ +qreal QQuickGeoMapGestureArea::maximumZoomLevelChange() const +{ + return m_pinch.m_zoom.maximumChange; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setMaximumZoomLevelChange(qreal maxChange) +{ + if (maxChange == m_pinch.m_zoom.maximumChange || maxChange < 0.1 || maxChange > 10.0) + return; + m_pinch.m_zoom.maximumChange = maxChange; + emit maximumZoomLevelChangeChanged(); +} + +/*! + \internal +*/ +qreal QQuickGeoMapGestureArea::flickDeceleration() const +{ + return m_flick.m_deceleration; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setFlickDeceleration(qreal deceleration) +{ + if (deceleration < QML_MAP_FLICK_MINIMUMDECELERATION) + deceleration = QML_MAP_FLICK_MINIMUMDECELERATION; + else if (deceleration > QML_MAP_FLICK_MAXIMUMDECELERATION) + deceleration = QML_MAP_FLICK_MAXIMUMDECELERATION; + if (deceleration == m_flick.m_deceleration) + return; + m_flick.m_deceleration = deceleration; + emit flickDecelerationChanged(); +} + +/*! + \internal +*/ +QTouchEvent::TouchPoint* createTouchPointFromMouseEvent(QMouseEvent *event, Qt::TouchPointState state) +{ + // this is only partially filled. But since it is only partially used it works + // more robust would be to store a list of QPointFs rather than TouchPoints + QTouchEvent::TouchPoint* newPoint = new QTouchEvent::TouchPoint(); + newPoint->setPos(event->localPos()); + newPoint->setScenePos(event->windowPos()); + newPoint->setScreenPos(event->screenPos()); + newPoint->setState(state); + newPoint->setId(0); + return newPoint; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleMousePressEvent(QMouseEvent *event) +{ + m_mousePoint.reset(createTouchPointFromMouseEvent(event, Qt::TouchPointPressed)); + if (m_touchPoints.isEmpty()) update(); + event->accept(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleMouseMoveEvent(QMouseEvent *event) +{ + m_mousePoint.reset(createTouchPointFromMouseEvent(event, Qt::TouchPointMoved)); + if (m_touchPoints.isEmpty()) update(); + event->accept(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleMouseReleaseEvent(QMouseEvent *event) +{ + if (!m_mousePoint.isNull()) { + //this looks super ugly , however is required in case we do not get synthesized MouseReleaseEvent + //and we reset the point already in handleTouchUngrabEvent + m_mousePoint.reset(createTouchPointFromMouseEvent(event, Qt::TouchPointReleased)); + if (m_touchPoints.isEmpty()) update(); + } + event->accept(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleMouseUngrabEvent() +{ + + if (m_touchPoints.isEmpty() && !m_mousePoint.isNull()) { + m_mousePoint.reset(); + update(); + } else { + m_mousePoint.reset(); + } +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleTouchUngrabEvent() +{ + m_touchPoints.clear(); + //this is needed since in some cases mouse release is not delivered + //(second touch point breaks mouse synthesized events) + m_mousePoint.reset(); + update(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleTouchEvent(QTouchEvent *event) +{ + m_touchPoints.clear(); + m_mousePoint.reset(); + + for (int i = 0; i < event->touchPoints().count(); ++i) { + auto point = event->touchPoints().at(i); + if (point.state() != Qt::TouchPointReleased) + m_touchPoints << point; + } + if (event->touchPoints().count() >= 2) + event->accept(); + else + event->ignore(); + update(); +} + +void QQuickGeoMapGestureArea::handleWheelEvent(QWheelEvent *event) +{ + if (!m_map) + return; + + QGeoCoordinate wheelGeoPos = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(event->posF()), false); + QPointF preZoomPoint = m_map->geoProjection().coordinateToItemPosition(wheelGeoPos, false).toPointF(); + + double zoomLevelDelta = event->angleDelta().y() * qreal(0.001); + m_declarativeMap->setZoomLevel(m_declarativeMap->zoomLevel() + zoomLevelDelta); + QPointF postZoomPoint = m_map->geoProjection().coordinateToItemPosition(wheelGeoPos, false).toPointF(); + + if (preZoomPoint != postZoomPoint) + { + qreal dx = postZoomPoint.x() - preZoomPoint.x(); + qreal dy = postZoomPoint.y() - preZoomPoint.y(); + QPointF mapCenterPoint(m_map->viewportWidth() / 2.0 + dx, m_map->viewportHeight() / 2.0 + dy); + + QGeoCoordinate mapCenterCoordinate = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(mapCenterPoint), false); + m_declarativeMap->setCenter(mapCenterCoordinate); + } + event->accept(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::clearTouchData() +{ + m_velocityX = 0; + m_velocityY = 0; + m_sceneCenter.setX(0); + m_sceneCenter.setY(0); + m_touchCenterCoord.setLongitude(0); + m_touchCenterCoord.setLatitude(0); + m_startCoord.setLongitude(0); + m_startCoord.setLatitude(0); +} + + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updateVelocityList(const QPointF &pos) +{ + // Take velocity samples every sufficient period of time, used later to determine the flick + // duration and speed (when mouse is released). + qreal elapsed = qreal(m_lastPosTime.elapsed()); + + if (elapsed >= QML_MAP_FLICK_VELOCITY_SAMPLE_PERIOD) { + elapsed /= 1000.; + int dyFromLastPos = pos.y() - m_lastPos.y(); + int dxFromLastPos = pos.x() - m_lastPos.x(); + m_lastPos = pos; + m_lastPosTime.restart(); + qreal velX = qreal(dxFromLastPos) / elapsed; + qreal velY = qreal(dyFromLastPos) / elapsed; + m_velocityX = qBound<qreal>(-m_flick.m_maxVelocity, velX, m_flick.m_maxVelocity); + m_velocityY = qBound<qreal>(-m_flick.m_maxVelocity, velY, m_flick.m_maxVelocity); + } +} + +/*! + \internal +*/ + +bool QQuickGeoMapGestureArea::isActive() const +{ + return isPanActive() || isPinchActive(); +} + +/*! + \internal +*/ +// simplify the gestures by using a state-machine format (easy to move to a future state machine) +void QQuickGeoMapGestureArea::update() +{ + if (!m_map) + return; + + // First state machine is for the number of touch points + + //combine touch with mouse event + m_allPoints.clear(); + m_allPoints << m_touchPoints; + if (m_allPoints.isEmpty() && !m_mousePoint.isNull()) + m_allPoints << *m_mousePoint.data(); + + touchPointStateMachine(); + + // Parallel state machine for pinch + if (isPinchActive() || (m_enabled && m_pinch.m_enabled && (m_acceptedGestures & (PinchGesture)))) + pinchStateMachine(); + + // Parallel state machine for pan (since you can pan at the same time as pinching) + // The stopPan function ensures that pan stops immediately when disabled, + // but the line below allows pan continue its current gesture if you disable + // the whole gesture (enabled_ flag), this keeps the enabled_ consistent with the pinch + if (isPanActive() || (m_enabled && m_flick.m_enabled && (m_acceptedGestures & (PanGesture | FlickGesture)))) + panStateMachine(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::touchPointStateMachine() +{ + // Transitions: + switch (m_touchPointState) { + case touchPoints0: + if (m_allPoints.count() == 1) { + clearTouchData(); + startOneTouchPoint(); + m_touchPointState = touchPoints1; + } else if (m_allPoints.count() >= 2) { + clearTouchData(); + startTwoTouchPoints(); + m_touchPointState = touchPoints2; + } + break; + case touchPoints1: + if (m_allPoints.count() == 0) { + m_touchPointState = touchPoints0; + } else if (m_allPoints.count() == 2) { + m_touchCenterCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_sceneCenter), false); + startTwoTouchPoints(); + m_touchPointState = touchPoints2; + } + break; + case touchPoints2: + if (m_allPoints.count() == 0) { + m_touchPointState = touchPoints0; + } else if (m_allPoints.count() == 1) { + m_touchCenterCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_sceneCenter), false); + startOneTouchPoint(); + m_touchPointState = touchPoints1; + } + break; + }; + + // Update + switch (m_touchPointState) { + case touchPoints0: + break; // do nothing if no touch points down + case touchPoints1: + updateOneTouchPoint(); + break; + case touchPoints2: + updateTwoTouchPoints(); + break; + } +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startOneTouchPoint() +{ + m_sceneStartPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_lastPos = m_sceneStartPoint1; + m_lastPosTime.start(); + QGeoCoordinate startCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_sceneStartPoint1), false); + // ensures a smooth transition for panning + m_startCoord.setLongitude(m_startCoord.longitude() + startCoord.longitude() - + m_touchCenterCoord.longitude()); + m_startCoord.setLatitude(m_startCoord.latitude() + startCoord.latitude() - + m_touchCenterCoord.latitude()); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updateOneTouchPoint() +{ + m_sceneCenter = mapFromScene(m_allPoints.at(0).scenePos()); + updateVelocityList(m_sceneCenter); +} + + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startTwoTouchPoints() +{ + m_sceneStartPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_sceneStartPoint2 = mapFromScene(m_allPoints.at(1).scenePos()); + QPointF startPos = (m_sceneStartPoint1 + m_sceneStartPoint2) * 0.5; + m_lastPos = startPos; + m_lastPosTime.start(); + QGeoCoordinate startCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(startPos), false); + m_startCoord.setLongitude(m_startCoord.longitude() + startCoord.longitude() - + m_touchCenterCoord.longitude()); + m_startCoord.setLatitude(m_startCoord.latitude() + startCoord.latitude() - + m_touchCenterCoord.latitude()); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updateTwoTouchPoints() +{ + QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos()); + QPointF p2 = mapFromScene(m_allPoints.at(1).scenePos()); + qreal dx = p1.x() - p2.x(); + qreal dy = p1.y() - p2.y(); + m_distanceBetweenTouchPoints = sqrt(dx * dx + dy * dy); + m_sceneCenter = (p1 + p2) / 2; + updateVelocityList(m_sceneCenter); + + m_twoTouchAngle = QLineF(p1, p2).angle(); + if (m_twoTouchAngle > 180) + m_twoTouchAngle -= 360; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::pinchStateMachine() +{ + PinchState lastState = m_pinchState; + // Transitions: + switch (m_pinchState) { + case pinchInactive: + if (m_allPoints.count() >= 2) { + if (canStartPinch()) { + m_declarativeMap->setKeepMouseGrab(true); + m_declarativeMap->setKeepTouchGrab(true); + startPinch(); + m_pinchState = pinchActive; + } else { + m_pinchState = pinchInactiveTwoPoints; + } + } + break; + case pinchInactiveTwoPoints: + if (m_allPoints.count() <= 1) { + m_pinchState = pinchInactive; + } else { + if (canStartPinch()) { + m_declarativeMap->setKeepMouseGrab(true); + m_declarativeMap->setKeepTouchGrab(true); + startPinch(); + m_pinchState = pinchActive; + } + } + break; + case pinchActive: + if (m_allPoints.count() <= 1) { + m_pinchState = pinchInactive; + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + m_declarativeMap->setKeepTouchGrab(m_preventStealing); + endPinch(); + } + break; + } + // This line implements an exclusive state machine, where the transitions and updates don't + // happen on the same frame + if (m_pinchState != lastState) { + emit pinchActiveChanged(); + return; + } + + // Update + switch (m_pinchState) { + case pinchInactive: + case pinchInactiveTwoPoints: + break; // do nothing + case pinchActive: + updatePinch(); + break; + } +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::canStartPinch() +{ + const int startDragDistance = qApp->styleHints()->startDragDistance(); + + if (m_allPoints.count() >= 2) { + QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos()); + QPointF p2 = mapFromScene(m_allPoints.at(1).scenePos()); + if (qAbs(p1.x()-m_sceneStartPoint1.x()) > startDragDistance + || qAbs(p1.y()-m_sceneStartPoint1.y()) > startDragDistance + || qAbs(p2.x()-m_sceneStartPoint2.x()) > startDragDistance + || qAbs(p2.y()-m_sceneStartPoint2.y()) > startDragDistance) { + m_pinch.m_event.setCenter(mapFromScene(m_sceneCenter)); + m_pinch.m_event.setAngle(m_twoTouchAngle); + m_pinch.m_event.setPoint1(p1); + m_pinch.m_event.setPoint2(p2); + m_pinch.m_event.setPointCount(m_allPoints.count()); + m_pinch.m_event.setAccepted(true); + emit pinchStarted(&m_pinch.m_event); + return m_pinch.m_event.accepted(); + } + } + return false; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startPinch() +{ + m_pinch.m_startDist = m_distanceBetweenTouchPoints; + m_pinch.m_zoom.m_previous = m_declarativeMap->zoomLevel(); + m_pinch.m_lastAngle = m_twoTouchAngle; + + m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePos()); + + m_pinch.m_zoom.m_start = m_declarativeMap->zoomLevel(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updatePinch() +{ + // Calculate the new zoom level if we have distance ( >= 2 touchpoints), otherwise stick with old. + qreal newZoomLevel = m_pinch.m_zoom.m_previous; + if (m_distanceBetweenTouchPoints) { + newZoomLevel = + // How much further/closer the current touchpoints are (in pixels) compared to pinch start + ((m_distanceBetweenTouchPoints - m_pinch.m_startDist) * + // How much one pixel corresponds in units of zoomlevel (and multiply by above delta) + (m_pinch.m_zoom.maximumChange / ((width() + height()) / 2))) + + // Add to starting zoom level. Sign of (dist-pinchstartdist) takes care of zoom in / out + m_pinch.m_zoom.m_start; + } + + m_pinch.m_event.setCenter(mapFromScene(m_sceneCenter)); + m_pinch.m_event.setAngle(m_twoTouchAngle); + + m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePos()); + m_pinch.m_event.setPoint1(m_pinch.m_lastPoint1); + m_pinch.m_event.setPoint2(m_pinch.m_lastPoint2); + m_pinch.m_event.setPointCount(m_allPoints.count()); + m_pinch.m_event.setAccepted(true); + + m_pinch.m_lastAngle = m_twoTouchAngle; + emit pinchUpdated(&m_pinch.m_event); + + if (m_acceptedGestures & PinchGesture) { + // Take maximum and minimumzoomlevel into account + qreal perPinchMinimumZoomLevel = qMax(m_pinch.m_zoom.m_start - m_pinch.m_zoom.maximumChange, m_pinch.m_zoom.m_minimum); + qreal perPinchMaximumZoomLevel = qMin(m_pinch.m_zoom.m_start + m_pinch.m_zoom.maximumChange, m_pinch.m_zoom.m_maximum); + newZoomLevel = qMin(qMax(perPinchMinimumZoomLevel, newZoomLevel), perPinchMaximumZoomLevel); + m_declarativeMap->setZoomLevel(newZoomLevel); + m_pinch.m_zoom.m_previous = newZoomLevel; + } +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::endPinch() +{ + QPointF p1 = mapFromScene(m_pinch.m_lastPoint1); + QPointF p2 = mapFromScene(m_pinch.m_lastPoint2); + m_pinch.m_event.setCenter((p1 + p2) / 2); + m_pinch.m_event.setAngle(m_pinch.m_lastAngle); + m_pinch.m_event.setPoint1(p1); + m_pinch.m_event.setPoint2(p2); + m_pinch.m_event.setAccepted(true); + m_pinch.m_event.setPointCount(0); + emit pinchFinished(&m_pinch.m_event); + m_pinch.m_startDist = 0; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::panStateMachine() +{ + FlickState lastState = m_flickState; + + // Transitions + switch (m_flickState) { + case flickInactive: + if (canStartPan()) { + // Update startCoord_ to ensure smooth start for panning when going over startDragDistance + QGeoCoordinate newStartCoord = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_sceneCenter), false); + m_startCoord.setLongitude(newStartCoord.longitude()); + m_startCoord.setLatitude(newStartCoord.latitude()); + m_declarativeMap->setKeepMouseGrab(true); + m_flickState = panActive; + } + break; + case panActive: + if (m_allPoints.count() == 0) { + if (!tryStartFlick()) + { + m_flickState = flickInactive; + // mark as inactive for use by camera + if (m_pinchState == pinchInactive) { + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + m_map->prefetchData(); + } + emit panFinished(); + } else { + m_flickState = flickActive; + emit panFinished(); + emit flickStarted(); + } + } + break; + case flickActive: + if (m_allPoints.count() > 0) { // re touched before movement ended + stopFlick(); + m_declarativeMap->setKeepMouseGrab(true); + m_flickState = panActive; + } + break; + } + + if (m_flickState != lastState) + emit panActiveChanged(); + + // Update + switch (m_flickState) { + case flickInactive: // do nothing + break; + case panActive: + updatePan(); + // this ensures 'panStarted' occurs after the pan has actually started + if (lastState != panActive) + emit panStarted(); + break; + case flickActive: + break; + } +} +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::canStartPan() +{ + if (m_allPoints.count() == 0 || (m_acceptedGestures & PanGesture) == 0) + return false; + + // Check if thresholds for normal panning are met. + // (normal panning vs flicking: flicking will start from mouse release event). + const int startDragDistance = qApp->styleHints()->startDragDistance() * 2; + QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos()); + int dyFromPress = int(p1.y() - m_sceneStartPoint1.y()); + int dxFromPress = int(p1.x() - m_sceneStartPoint1.x()); + if ((qAbs(dyFromPress) >= startDragDistance || qAbs(dxFromPress) >= startDragDistance)) + return true; + return false; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updatePan() +{ + QPointF startPoint = m_map->geoProjection().coordinateToItemPosition(m_startCoord, false).toPointF(); + int dx = static_cast<int>(m_sceneCenter.x() - startPoint.x()); + int dy = static_cast<int>(m_sceneCenter.y() - startPoint.y()); + QPointF mapCenterPoint; + mapCenterPoint.setY(m_map->viewportHeight() / 2.0 - dy); + mapCenterPoint.setX(m_map->viewportWidth() / 2.0 - dx); + QGeoCoordinate animationStartCoordinate = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(mapCenterPoint), false); + m_declarativeMap->setCenter(animationStartCoordinate); +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::tryStartFlick() +{ + if ((m_acceptedGestures & FlickGesture) == 0) + return false; + // if we drag then pause before release we should not cause a flick. + qreal velocityX = 0.0; + qreal velocityY = 0.0; + if (m_lastPosTime.elapsed() < QML_MAP_FLICK_VELOCITY_SAMPLE_PERIOD) { + velocityY = m_velocityY; + velocityX = m_velocityX; + } + int flickTimeY = 0; + int flickTimeX = 0; + int flickPixelsX = 0; + int flickPixelsY = 0; + if (qAbs(velocityY) > MinimumFlickVelocity && qAbs(m_sceneCenter.y() - m_sceneStartPoint1.y()) > FlickThreshold) { + // calculate Y flick animation values + qreal acceleration = m_flick.m_deceleration; + if ((velocityY > 0.0f) == (m_flick.m_deceleration > 0.0f)) + acceleration = acceleration * -1.0f; + flickTimeY = static_cast<int>(-1000 * velocityY / acceleration); + flickPixelsY = (flickTimeY * velocityY) / (1000.0 * 2); + } + if (qAbs(velocityX) > MinimumFlickVelocity && qAbs(m_sceneCenter.x() - m_sceneStartPoint1.x()) > FlickThreshold) { + // calculate X flick animation values + qreal acceleration = m_flick.m_deceleration; + if ((velocityX > 0.0f) == (m_flick.m_deceleration > 0.0f)) + acceleration = acceleration * -1.0f; + flickTimeX = static_cast<int>(-1000 * velocityX / acceleration); + flickPixelsX = (flickTimeX * velocityX) / (1000.0 * 2); + } + int flickTime = qMax(flickTimeY, flickTimeX); + if (flickTime > 0) { + startFlick(flickPixelsX, flickPixelsY, flickTime); + return true; + } + return false; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startFlick(int dx, int dy, int timeMs) +{ + if (!m_flick.m_animation) + return; + if (timeMs < 0) + return; + + QGeoCoordinate animationStartCoordinate = m_declarativeMap->center(); + + if (m_flick.m_animation->isRunning()) + m_flick.m_animation->stop(); + QGeoCoordinate animationEndCoordinate = m_declarativeMap->center(); + m_flick.m_animation->setDuration(timeMs); + + double zoom = pow(2.0, m_declarativeMap->zoomLevel()); + double longitude = animationStartCoordinate.longitude() - (dx / zoom); + double latitude = animationStartCoordinate.latitude() + (dy / zoom); + + if (dx > 0) + m_flick.m_animation->setDirection(QQuickGeoCoordinateAnimation::East); + else + m_flick.m_animation->setDirection(QQuickGeoCoordinateAnimation::West); + + //keep animation in correct bounds + if (latitude > 85.05113) + latitude = 85.05113; + else if (latitude < -85.05113) + latitude = -85.05113; + + if (longitude > 180) + longitude = longitude - 360; + else if (longitude < -180) + longitude = longitude + 360; + + animationEndCoordinate.setLongitude(longitude); + animationEndCoordinate.setLatitude(latitude); + + m_flick.m_animation->setFrom(animationStartCoordinate); + m_flick.m_animation->setTo(animationEndCoordinate); + m_flick.m_animation->start(); +} + +void QQuickGeoMapGestureArea::stopPan() +{ + if (m_flickState == flickActive) { + stopFlick(); + } else if (m_flickState == panActive) { + m_velocityX = 0; + m_velocityY = 0; + m_flickState = flickInactive; + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + emit panFinished(); + emit panActiveChanged(); + m_map->prefetchData(); + } +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::stopFlick() +{ + if (!m_flick.m_animation) + return; + m_velocityX = 0; + m_velocityY = 0; + if (m_flick.m_animation->isRunning()) + m_flick.m_animation->stop(); + else + handleFlickAnimationStopped(); +} + +void QQuickGeoMapGestureArea::handleFlickAnimationStopped() +{ + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + if (m_flickState == flickActive) { + m_flickState = flickInactive; + emit flickFinished(); + emit panActiveChanged(); + m_map->prefetchData(); + } +} + +#include "moc_qquickgeomapgesturearea_p.cpp" + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qquickgeomapgesturearea_p.h b/src/location/declarativemaps/qquickgeomapgesturearea_p.h new file mode 100644 index 00000000..5d3efc8d --- /dev/null +++ b/src/location/declarativemaps/qquickgeomapgesturearea_p.h @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QQUICKGEOMAPGESTUREAREA_P_H +#define QQUICKGEOMAPGESTUREAREA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> + +#include <QtQuick/QQuickItem> +#include <QTouchEvent> +#include <QDebug> +#include <QElapsedTimer> +#include <QtPositioning/qgeocoordinate.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsSceneMouseEvent; +class QQuickGeoCoordinateAnimation; +class QDeclarativeGeoMap; +class QTouchEvent; +class QWheelEvent; +class QGeoMap; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPinchEvent : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPointF center READ center) + Q_PROPERTY(qreal angle READ angle) + Q_PROPERTY(QPointF point1 READ point1) + Q_PROPERTY(QPointF point2 READ point2) + Q_PROPERTY(int pointCount READ pointCount) + Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) + +public: + QGeoMapPinchEvent(const QPointF ¢er, qreal angle, + const QPointF &point1, const QPointF &point2, + int pointCount = 0, bool accepted = true) + : QObject(), m_center(center), m_angle(angle), + m_point1(point1), m_point2(point2), + m_pointCount(pointCount), m_accepted(accepted) {} + QGeoMapPinchEvent() + : QObject(), + m_angle(0.0), + m_pointCount(0), + m_accepted(true) {} + + QPointF center() const { return m_center; } + void setCenter(const QPointF ¢er) { m_center = center; } + qreal angle() const { return m_angle; } + void setAngle(qreal angle) { m_angle = angle; } + QPointF point1() const { return m_point1; } + void setPoint1(const QPointF &p) { m_point1 = p; } + QPointF point2() const { return m_point2; } + void setPoint2(const QPointF &p) { m_point2 = p; } + int pointCount() const { return m_pointCount; } + void setPointCount(int count) { m_pointCount = count; } + bool accepted() const { return m_accepted; } + void setAccepted(bool a) { m_accepted = a; } + +private: + QPointF m_center; + qreal m_angle; + QPointF m_point1; + QPointF m_point2; + int m_pointCount; + bool m_accepted; +}; + +class Q_LOCATION_PRIVATE_EXPORT QQuickGeoMapGestureArea: public QQuickItem +{ + Q_OBJECT + Q_ENUMS(GeoMapGesture) + Q_FLAGS(AcceptedGestures) + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool pinchActive READ isPinchActive NOTIFY pinchActiveChanged) + Q_PROPERTY(bool panActive READ isPanActive NOTIFY panActiveChanged) + Q_PROPERTY(AcceptedGestures acceptedGestures READ acceptedGestures WRITE setAcceptedGestures NOTIFY acceptedGesturesChanged) + Q_PROPERTY(qreal maximumZoomLevelChange READ maximumZoomLevelChange WRITE setMaximumZoomLevelChange NOTIFY maximumZoomLevelChangeChanged) + Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) + Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged REVISION 1) + +public: + QQuickGeoMapGestureArea(QDeclarativeGeoMap *map); + ~QQuickGeoMapGestureArea(); + + enum GeoMapGesture { + NoGesture = 0x0000, + PinchGesture = 0x0001, + PanGesture = 0x0002, + FlickGesture = 0x004 + }; + + Q_DECLARE_FLAGS(AcceptedGestures, GeoMapGesture) + + AcceptedGestures acceptedGestures() const; + void setAcceptedGestures(AcceptedGestures acceptedGestures); + + bool isPinchActive() const; + bool isPanActive() const; + bool isActive() const; + + bool enabled() const; + void setEnabled(bool enabled); + + qreal maximumZoomLevelChange() const; + void setMaximumZoomLevelChange(qreal maxChange); + + qreal flickDeceleration() const; + void setFlickDeceleration(qreal deceleration); + + void handleTouchEvent(QTouchEvent *event); + void handleWheelEvent(QWheelEvent *event); + void handleMousePressEvent(QMouseEvent *event); + void handleMouseMoveEvent(QMouseEvent *event); + void handleMouseReleaseEvent(QMouseEvent *event); + void handleMouseUngrabEvent(); + void handleTouchUngrabEvent(); + + void setMinimumZoomLevel(qreal min); + qreal minimumZoomLevel() const; + + void setMaximumZoomLevel(qreal max); + qreal maximumZoomLevel() const; + + void setMap(QGeoMap *map); + + bool preventStealing() const; + void setPreventStealing(bool prevent); + +Q_SIGNALS: + void panActiveChanged(); + void pinchActiveChanged(); + void enabledChanged(); + void maximumZoomLevelChangeChanged(); + void acceptedGesturesChanged(); + void flickDecelerationChanged(); + void pinchStarted(QGeoMapPinchEvent *pinch); + void pinchUpdated(QGeoMapPinchEvent *pinch); + void pinchFinished(QGeoMapPinchEvent *pinch); + void panStarted(); + void panFinished(); + void flickStarted(); + void flickFinished(); + void preventStealingChanged(); +private: + void update(); + + // Create general data relating to the touch points + void touchPointStateMachine(); + void startOneTouchPoint(); + void updateOneTouchPoint(); + void startTwoTouchPoints(); + void updateTwoTouchPoints(); + + // All pinch related code, which encompasses zoom + void pinchStateMachine(); + bool canStartPinch(); + void startPinch(); + void updatePinch(); + void endPinch(); + + // Pan related code (regardles of number of touch points), + // includes the flick based panning after letting go + void panStateMachine(); + bool canStartPan(); + void updatePan(); + bool tryStartFlick(); + void startFlick(int dx, int dy, int timeMs = 0); + void stopFlick(); + + bool pinchEnabled() const; + void setPinchEnabled(bool enabled); + bool panEnabled() const; + void setPanEnabled(bool enabled); + bool flickEnabled() const; + void setFlickEnabled(bool enabled); + +private Q_SLOTS: + void handleFlickAnimationStopped(); + + +private: + void stopPan(); + void clearTouchData(); + void updateVelocityList(const QPointF &pos); + +private: + QGeoMap *m_map; + QDeclarativeGeoMap *m_declarativeMap; + bool m_enabled; + + struct Pinch + { + Pinch() : m_enabled(true), m_startDist(0), m_lastAngle(0.0) {} + + QGeoMapPinchEvent m_event; + bool m_enabled; + struct Zoom + { + Zoom() : m_minimum(0.0), m_maximum(30.0), m_start(0.0), m_previous(0.0), + maximumChange(4.0) {} + qreal m_minimum; + qreal m_maximum; + qreal m_start; + qreal m_previous; + qreal maximumChange; + } m_zoom; + + QPointF m_lastPoint1; + QPointF m_lastPoint2; + qreal m_startDist; + qreal m_lastAngle; + } m_pinch; + + AcceptedGestures m_acceptedGestures; + + struct Pan + { + qreal m_maxVelocity; + qreal m_deceleration; + QQuickGeoCoordinateAnimation *m_animation; + bool m_enabled; + } m_flick; + + + // these are calculated regardless of gesture or number of touch points + qreal m_velocityX; + qreal m_velocityY; + QElapsedTimer m_lastPosTime; + QPointF m_lastPos; + QList<QTouchEvent::TouchPoint> m_allPoints; + QList<QTouchEvent::TouchPoint> m_touchPoints; + QScopedPointer<QTouchEvent::TouchPoint> m_mousePoint; + QPointF m_sceneStartPoint1; + + // only set when two points in contact + QPointF m_sceneStartPoint2; + QGeoCoordinate m_startCoord; + QGeoCoordinate m_touchCenterCoord; + qreal m_twoTouchAngle; + qreal m_distanceBetweenTouchPoints; + QPointF m_sceneCenter; + bool m_preventStealing; + bool m_panEnabled; + + // prototype state machine... + enum TouchPointState + { + touchPoints0, + touchPoints1, + touchPoints2 + } m_touchPointState; + + enum PinchState + { + pinchInactive, + pinchInactiveTwoPoints, + pinchActive + } m_pinchState; + + enum FlickState + { + flickInactive, + panActive, + flickActive + } m_flickState; +}; + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QQuickGeoMapGestureArea) + +#endif // QQUICKGEOMAPGESTUREAREA_P_H diff --git a/src/location/declarativeplaces/declarativeplaces.pri b/src/location/declarativeplaces/declarativeplaces.pri new file mode 100644 index 00000000..82f60c23 --- /dev/null +++ b/src/location/declarativeplaces/declarativeplaces.pri @@ -0,0 +1,51 @@ +INCLUDEPATH += declarativeplaces + +SOURCES += \ +#models + declarativeplaces/qdeclarativeplacecontentmodel.cpp \ + declarativeplaces/qdeclarativesupportedcategoriesmodel.cpp \ + declarativeplaces/qdeclarativesearchsuggestionmodel.cpp \ + declarativeplaces/qdeclarativesearchresultmodel.cpp \ + declarativeplaces/qdeclarativereviewmodel.cpp \ + declarativeplaces/qdeclarativeplaceimagemodel.cpp \ + declarativeplaces/qdeclarativeplaceeditorialmodel.cpp \ +#data + declarativeplaces/qdeclarativecontactdetail.cpp \ + declarativeplaces/qdeclarativecategory.cpp \ + declarativeplaces/qdeclarativeplace.cpp \ + declarativeplaces/qdeclarativeplaceattribute.cpp \ + declarativeplaces/qdeclarativeplaceicon.cpp \ + declarativeplaces/qdeclarativeplaceuser.cpp \ + declarativeplaces/qdeclarativeratings.cpp \ + declarativeplaces/qdeclarativesupplier.cpp \ + declarativeplaces/qdeclarativesearchmodelbase.cpp + +PRIVATE_HEADERS += \ +#models + declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h \ + declarativeplaces/qdeclarativesearchsuggestionmodel_p.h \ + declarativeplaces/qdeclarativesearchresultmodel_p.h \ + declarativeplaces/qdeclarativereviewmodel_p.h \ + declarativeplaces/qdeclarativeplaceimagemodel_p.h \ +#data + declarativeplaces/qdeclarativecontactdetail_p.h \ + declarativeplaces/qdeclarativecategory_p.h \ + declarativeplaces/qdeclarativeplace_p.h \ + declarativeplaces/qdeclarativeplaceattribute_p.h \ + declarativeplaces/qdeclarativeplaceicon_p.h \ + declarativeplaces/qdeclarativeplaceuser_p.h \ + declarativeplaces/qdeclarativeratings_p.h \ + declarativeplaces/qdeclarativesupplier_p.h \ + declarativeplaces/qdeclarativesearchmodelbase_p.h \ + declarativeplaces/qdeclarativeplacecontentmodel_p.h \ + declarativeplaces/qdeclarativeplaceeditorialmodel_p.h + + + + + + + + + + diff --git a/src/location/declarativeplaces/qdeclarativecategory.cpp b/src/location/declarativeplaces/qdeclarativecategory.cpp new file mode 100644 index 00000000..c58ec3a2 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativecategory.cpp @@ -0,0 +1,458 @@ +/**************************************************************************** +** +** 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 "qdeclarativecategory_p.h" +#include "qdeclarativeplaceicon_p.h" +#include "qdeclarativegeoserviceprovider_p.h" +#include "error_messages.h" + +#include <QtQml/QQmlInfo> +#include <QtLocation/QGeoServiceProvider> +#include <QtLocation/QPlaceManager> +#include <QCoreApplication> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Category + \instantiates QDeclarativeCategory + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + + \since Qt Location 5.5 + + \brief The Category type represents a category that a \l Place can be associated with. + + Categories are used to search for places based on the categories they are associated with. The + list of available categories can be obtained from the \l CategoryModel. The + \l PlaceSearchModel has a \l {PlaceSearchModel::categories}{categories} property that is used + to limit the search results to places with the specified categories. + + If the \l Plugin supports it, categories can be created or removed. To create a new category + construct a new Category object and set its properties, then invoke the \l save() method. + + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml Category + \dots 0 + \snippet declarative/places.qml Category save + + To remove a category ensure that the \l plugin and categoryId properties are set and call the + \l remove() method. + + \sa CategoryModel +*/ + +QDeclarativeCategory::QDeclarativeCategory(QObject *parent) +: QObject(parent), m_icon(0), m_plugin(0), m_reply(0), m_complete(false), m_status(Ready) +{ +} + +QDeclarativeCategory::QDeclarativeCategory(const QPlaceCategory &category, + QDeclarativeGeoServiceProvider *plugin, + QObject *parent) +: QObject(parent), m_category(category), m_icon(0), m_plugin(plugin), m_reply(0), + m_complete(false), m_status(Ready) +{ + setCategory(category); +} + +QDeclarativeCategory::~QDeclarativeCategory() {} + +// From QQmlParserStatus +void QDeclarativeCategory::componentComplete() +{ + // delayed instantiation of QObject based properties. + if (!m_icon) { + m_icon = new QDeclarativePlaceIcon(this); + m_icon->setPlugin(m_plugin); + } + + m_complete = true; +} + +/*! + \qmlproperty Plugin Category::plugin + + This property holds the location based service to which the category belongs. +*/ +void QDeclarativeCategory::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + m_plugin = plugin; + if (m_complete) + emit pluginChanged(); + + if (m_icon && m_icon->parent() == this && !m_icon->plugin()) + m_icon->setPlugin(m_plugin); + + if (!m_plugin) + return; + + if (m_plugin->isAttached()) { + pluginReady(); + } else { + connect(m_plugin, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +QDeclarativeGeoServiceProvider *QDeclarativeCategory::plugin() const +{ + return m_plugin; +} + +/*! + \internal +*/ +void QDeclarativeCategory::pluginReady() +{ + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return; + } +} + + +/*! + \qmlproperty QPlaceCategory Category::category + \keyword Category::category + + For details on how to use this property to interface between C++ and QML see + "\l {Category - QPlaceCategory} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativeCategory::setCategory(const QPlaceCategory &category) +{ + QPlaceCategory previous = m_category; + m_category = category; + + if (category.name() != previous.name()) + emit nameChanged(); + + if (category.categoryId() != previous.categoryId()) + emit categoryIdChanged(); + + if (m_icon && m_icon->parent() == this) { + m_icon->setPlugin(m_plugin); + m_icon->setIcon(m_category.icon()); + } else if (!m_icon || m_icon->parent() != this) { + m_icon = new QDeclarativePlaceIcon(m_category.icon(), m_plugin, this); + emit iconChanged(); + } +} + +QPlaceCategory QDeclarativeCategory::category() +{ + m_category.setIcon(m_icon ? m_icon->icon() : QPlaceIcon()); + return m_category; +} + +/*! + \qmlproperty string Category::categoryId + + This property holds the identifier of the category. The categoryId is a string which uniquely + identifies this category within the categories \l plugin. +*/ +void QDeclarativeCategory::setCategoryId(const QString &id) +{ + if (m_category.categoryId() != id) { + m_category.setCategoryId(id); + emit categoryIdChanged(); + } +} + +QString QDeclarativeCategory::categoryId() const +{ + return m_category.categoryId(); +} + +/*! + \qmlproperty string Category::name + + This property holds string based name of the category. +*/ +void QDeclarativeCategory::setName(const QString &name) +{ + if (m_category.name() != name) { + m_category.setName(name); + emit nameChanged(); + } +} + +QString QDeclarativeCategory::name() const +{ + return m_category.name(); +} + +/*! + \qmlproperty enumeration Category::visibility + + This property holds the visibility of the category. It can be one of: + + \table + \row + \li Category.UnspecifiedVisibility + \li The visibility of the category is unspecified. If saving a category, the + plugin will automatically set a default visibility to the category saved in the backend. + This default is dependent on the plugin implementation. + \row + \li Category.DeviceVisibility + \li The category is limited to the current device. The category will not be transferred + off of the device. + \row + \li Category.PrivateVisibility + \li The category is private to the current user. The category may be transferred to an + online service but is only ever visible to the current user. + \row + \li Category.PublicVisibility + \li The category is public. + \endtable + + Note that visibility does not affect how \l{Place}s associated with + the category are displayed in the user-interface of an application + on the device. Instead, it defines the sharing semantics of the + category. +*/ +QDeclarativeCategory::Visibility QDeclarativeCategory::visibility() const +{ + return static_cast<QDeclarativeCategory::Visibility>(m_category.visibility()); +} + +void QDeclarativeCategory::setVisibility(Visibility visibility) +{ + if (static_cast<QDeclarativeCategory::Visibility>(m_category.visibility()) == visibility) + return; + + m_category.setVisibility(static_cast<QLocation::Visibility>(visibility)); + emit visibilityChanged(); +} + +/*! + \qmlproperty PlaceIcon Category::icon + + This property holds the image source associated with the category. To display the icon you can use + the \l Image type. +*/ +QDeclarativePlaceIcon *QDeclarativeCategory::icon() const +{ + return m_icon; +} + +void QDeclarativeCategory::setIcon(QDeclarativePlaceIcon *icon) +{ + if (m_icon == icon) + return; + + if (m_icon && m_icon->parent() == this) + delete m_icon; + + m_icon = icon; + emit iconChanged(); +} + +/*! + \qmlmethod string Category::errorString() + + Returns a string description of the error of the last operation. + If the last operation completed successfully then the string is empty. +*/ +QString QDeclarativeCategory::errorString() const +{ + return m_errorString; +} + +void QDeclarativeCategory::setStatus(Status status, const QString &errorString) +{ + Status originalStatus = m_status; + m_status = status; + m_errorString = errorString; + + if (originalStatus != m_status) + emit statusChanged(); +} + +/*! + \qmlproperty enumeration Category::status + + This property holds the status of the category. It can be one of: + + \table + \row + \li Category.Ready + \li No error occurred during the last operation, further operations may be performed on + the category. + \row + \li Category.Saving + \li The category is currently being saved, no other operations may be performed until the + current operation completes. + \row + \li Category.Removing + \li The category is currently being removed, no other operations can be performed until + the current operation completes. + \row + \li Category.Error + \li An error occurred during the last operation, further operations can still be + performed on the category. + \endtable +*/ +QDeclarativeCategory::Status QDeclarativeCategory::status() const +{ + return m_status; +} + +/*! + \qmlmethod void Category::save() + + This method saves the category to the backend service. +*/ +void QDeclarativeCategory::save(const QString &parentId) +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->saveCategory(category(), parentId); + connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished())); + setStatus(QDeclarativeCategory::Saving); +} + +/*! + \qmlmethod void Category::remove() + + This method permanently removes the category from the backend service. +*/ +void QDeclarativeCategory::remove() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->removeCategory(m_category.categoryId()); + connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished())); + setStatus(QDeclarativeCategory::Removing); +} + +/*! + \internal +*/ +void QDeclarativeCategory::replyFinished() +{ + if (!m_reply) + return; + + if (m_reply->error() == QPlaceReply::NoError) { + switch (m_reply->type()) { + case (QPlaceReply::IdReply) : { + QPlaceIdReply *idReply = qobject_cast<QPlaceIdReply *>(m_reply); + + switch (idReply->operationType()) { + case QPlaceIdReply::SaveCategory: + setCategoryId(idReply->id()); + break; + case QPlaceIdReply::RemoveCategory: + setCategoryId(QString()); + break; + default: + //Other operation types shouldn't ever be received. + break; + } + break; + } + default: + //other types of replies shouldn't ever be received. + break; + } + + m_errorString.clear(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativeCategory::Ready); + } else { + QString errorString = m_reply->errorString(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativeCategory::Error, errorString); + } +} + +/*! + \internal + Helper function to return the manager, this manager is intended to be used to perform the next + operation. Sets status to Error and an appropriate m_errorString if the manager cannot be + obtained. +*/ +QPlaceManager *QDeclarativeCategory::manager() +{ + if (m_status != QDeclarativeCategory::Ready && m_status != QDeclarativeCategory::Error) + return 0; + + if (m_reply) { + m_reply->abort(); + m_reply->deleteLater(); + m_reply = 0; + } + + if (!m_plugin) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROPERTY_NOT_SET)); + return 0; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_NOT_VALID)); + return 0; + } + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return 0; + } + + return placeManager; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativecategory_p.h b/src/location/declarativeplaces/qdeclarativecategory_p.h new file mode 100644 index 00000000..c32072f4 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativecategory_p.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECATEGORY_P_H +#define QDECLARATIVECATEGORY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtQml/qqml.h> +#include <QtQml/QQmlParserStatus> +#include <QObject> + +#include <QtLocation/qplacecategory.h> + +#include <QtLocation/private/qdeclarativegeoserviceprovider_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativePlaceIcon; +class QPlaceReply; +class QPlaceManager; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCategory : public QObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_ENUMS(Status Visibility) + + + Q_PROPERTY(QPlaceCategory category READ category WRITE setCategory) + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(QString categoryId READ categoryId WRITE setCategoryId NOTIFY categoryIdChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged) + Q_PROPERTY(QDeclarativePlaceIcon *icon READ icon WRITE setIcon NOTIFY iconChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativeCategory(QObject *parent = 0); + QDeclarativeCategory(const QPlaceCategory &category, QDeclarativeGeoServiceProvider *plugin, QObject *parent = 0); + ~QDeclarativeCategory(); + + enum Visibility { + UnspecifiedVisibility = QLocation::UnspecifiedVisibility, + DeviceVisibility = QLocation::DeviceVisibility, + PrivateVisibility = QLocation::PrivateVisibility, + PublicVisibility = QLocation::PublicVisibility + }; + enum Status {Ready, Saving, Removing, Error}; + + //From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + QPlaceCategory category(); + void setCategory(const QPlaceCategory &category); + + QString categoryId() const; + void setCategoryId(const QString &catID); + QString name() const; + void setName(const QString &name); + + Visibility visibility() const; + void setVisibility(Visibility visibility); + + QDeclarativePlaceIcon *icon() const; + void setIcon(QDeclarativePlaceIcon *icon); + + Q_INVOKABLE QString errorString() const; + + Status status() const; + void setStatus(Status status, const QString &errorString = QString()); + + Q_INVOKABLE void save(const QString &parentId = QString()); + Q_INVOKABLE void remove(); + +Q_SIGNALS: + void pluginChanged(); + void categoryIdChanged(); + void nameChanged(); + void visibilityChanged(); + void iconChanged(); + void statusChanged(); + +private Q_SLOTS: + void replyFinished(); + void pluginReady(); + +private: + QPlaceManager *manager(); + + QPlaceCategory m_category; + QDeclarativePlaceIcon *m_icon; + QDeclarativeGeoServiceProvider *m_plugin; + QPlaceReply *m_reply; + bool m_complete; + Status m_status; + QString m_errorString; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeCategory) + +#endif // QDECLARATIVECATEGORY_P_H diff --git a/src/location/declarativeplaces/qdeclarativecontactdetail.cpp b/src/location/declarativeplaces/qdeclarativecontactdetail.cpp new file mode 100644 index 00000000..7a7a4c33 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativecontactdetail.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** 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 "qdeclarativecontactdetail_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ContactDetails + \instantiates QDeclarativeContactDetails + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief The ContactDetails type holds contact details for a \l Place. + + The ContactDetails type is a map of \l {QtLocation::ContactDetail}{ContactDetail} objects. + To access contact details in the map use the \l keys() method to get the list of keys stored in + the map and then use the \c {[]} operator to access the + \l {QtLocation::ContactDetail}{ContactDetail} items. + + The following keys are defined in the API. \l Plugin implementations are free to define + additional keys. + + \list + \li phone + \li fax + \li email + \li website + \endlist + + ContactDetails instances are only ever used in the context of \l {Place}{Places}. It is not possible + to create a ContactDetails instance directly or re-assign ContactDetails instances to \l {Place}{Places}. + Modification of ContactDetails can only be accomplished via Javascript. + + \section1 Examples + + The following example shows how to access all \l {QtLocation::ContactDetail}{ContactDetails} + and print them to the console: + + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ContactDetails read + + The returned list of contact details is an \l {QObjectList-based model}{object list} and so can be used directly as a data model. For example, the + following demonstrates how to display a list of contact phone numbers in a list view: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ContactDetails phoneList + + The following example demonstrates how to assign a single phone number to a place in JavaScript: + \snippet declarative/places.qml ContactDetails write single + + The following demonstrates how to assign multiple phone numbers to a place in JavaScript: + \snippet declarative/places.qml ContactDetails write multiple +*/ + +/*! + \qmlmethod variant ContactDetails::keys() + + Returns an array of contact detail keys currently stored in the map. +*/ +QDeclarativeContactDetails::QDeclarativeContactDetails(QObject *parent) + : QQmlPropertyMap(parent) +{ +} + +QVariant QDeclarativeContactDetails::updateValue(const QString &, const QVariant &input) +{ + if (input.userType() == QMetaType::QObjectStar) { + QDeclarativeContactDetail *detail = + qobject_cast<QDeclarativeContactDetail *>(input.value<QObject *>()); + if (detail) { + QVariantList varList; + varList.append(input); + return varList; + } + } + + return input; +} + +/*! + \qmltype ContactDetail + \instantiates QDeclarativeContactDetail + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief The ContactDetail type holds a contact detail such as a phone number or a website + address. + + The ContactDetail provides a single detail on how one could contact a \l Place. The + ContactDetail consists of a \l label, which is a localized string describing the contact + method, and a \l value representing the actual contact detail. + + \section1 Examples + + The following example demonstrates how to assign a single phone number to a place in JavaScript: + \snippet declarative/places.qml ContactDetails write single + + The following demonstrates how to assign multiple phone numbers to a place in JavaScript: + \snippet declarative/places.qml ContactDetails write multiple + + Note, due to limitations of the QQmlPropertyMap, it is not possible + to declaratively specify the contact details in QML, it can only be accomplished + via JavaScript. +*/ +QDeclarativeContactDetail::QDeclarativeContactDetail(QObject *parent) + : QObject(parent) +{ +} + +QDeclarativeContactDetail::QDeclarativeContactDetail(const QPlaceContactDetail &src, QObject *parent) + : QObject(parent), m_contactDetail(src) +{ +} + +QDeclarativeContactDetail::~QDeclarativeContactDetail() +{ +} + +/*! + \qmlproperty QPlaceContactDetail QtLocation::ContactDetail::contactDetail + + For details on how to use this property to interface between C++ and QML see + "\l {ContactDetail - QDeclarativeContactDetail} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativeContactDetail::setContactDetail(const QPlaceContactDetail &src) +{ + QPlaceContactDetail prevContactDetail = m_contactDetail; + m_contactDetail = src; + + if (m_contactDetail.label() != prevContactDetail.label()) + emit labelChanged(); + if (m_contactDetail.value() != prevContactDetail.value()) + emit valueChanged(); +} + +QPlaceContactDetail QDeclarativeContactDetail::contactDetail() const +{ + return m_contactDetail; +} + +/*! + \qmlproperty string QtLocation::ContactDetail::label + + This property holds a label describing the contact detail. + + The label can potentially be localized. The language is dependent on the entity that sets it, + typically this is the \l {Plugin}. The \l {Plugin::locales} property defines + what language is used. +*/ +QString QDeclarativeContactDetail::label() const +{ + return m_contactDetail.label(); +} + +void QDeclarativeContactDetail::setLabel(const QString &label) +{ + if (m_contactDetail.label() != label) { + m_contactDetail.setLabel(label); + emit labelChanged(); + } +} + +/*! + \qmlproperty string QtLocation::ContactDetail::value + + This property holds the value of the contact detail which may be a phone number, an email + address, a website url and so on. +*/ +QString QDeclarativeContactDetail::value() const +{ + return m_contactDetail.value(); +} + +void QDeclarativeContactDetail::setValue(const QString &value) +{ + if (m_contactDetail.value() != value) { + m_contactDetail.setValue(value); + emit valueChanged(); + } +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativecontactdetail_p.h b/src/location/declarativeplaces/qdeclarativecontactdetail_p.h new file mode 100644 index 00000000..ad60c3b5 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativecontactdetail_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONTACTDETAIL_P_H +#define QDECLARATIVECONTACTDETAIL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtCore/QObject> +#include <QtLocation/QPlaceContactDetail> +#include <QtQml/QQmlPropertyMap> +#include <QtQml/qqml.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeContactDetails : public QQmlPropertyMap +{ + Q_OBJECT + +public: + explicit QDeclarativeContactDetails(QObject *parent = 0); + virtual QVariant updateValue(const QString &key, const QVariant &input); +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeContactDetail : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceContactDetail contactDetail READ contactDetail WRITE setContactDetail) + Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) + Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged) + +public: + explicit QDeclarativeContactDetail(QObject *parent = 0); + explicit QDeclarativeContactDetail(const QPlaceContactDetail &src, QObject *parent = 0); + ~QDeclarativeContactDetail(); + + QPlaceContactDetail contactDetail() const; + void setContactDetail(const QPlaceContactDetail &contactDetail); + + QString label() const; + void setLabel(const QString &label); + + QString value() const; + void setValue(const QString &value); + +Q_SIGNALS: + void labelChanged(); + void valueChanged(); + +private: + QPlaceContactDetail m_contactDetail; + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeContactDetail) +QML_DECLARE_TYPE(QDeclarativeContactDetails) + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeperiod_p.h b/src/location/declarativeplaces/qdeclarativeperiod_p.h new file mode 100644 index 00000000..3ded0109 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeperiod_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPERIOD_P_H +#define QDECLARATIVEPERIOD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qplaceperiod.h> +#include <QtQml/qqml.h> + +#include <QObject> + +QT_BEGIN_NAMESPACE + +class QDeclarativePeriod : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDate startDate READ startDate WRITE setStartDate NOTIFY startDateChanged) + Q_PROPERTY(QTime startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) + Q_PROPERTY(QDate endDate READ endDate WRITE setEndDate NOTIFY endDateChanged) + Q_PROPERTY(QTime endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged) + +public: + explicit QDeclarativePeriod(QObject *parent = 0); + explicit QDeclarativePeriod(const QPlacePeriod &period, QObject *parent = 0); + ~QDeclarativePeriod(); + + QPlacePeriod period() const; + void setPeriod(const QPlacePeriod &period); + + QDate startDate() const; + void setStartDate(const QDate &data); + QTime startTime() const; + void setStartTime(const QTime &data); + QDate endDate() const; + void setEndDate(const QDate &data); + QTime endTime() const; + void setEndTime(const QTime &data); + +Q_SIGNALS: + void startDateChanged(); + void startTimeChanged(); + void endDateChanged(); + void endTimeChanged(); + +private: + QPlacePeriod m_period; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QT_PREPEND_NAMESPACE(QDeclarativePeriod)); + +#endif // QDECLARATIVEPERIOD_P_H diff --git a/src/location/declarativeplaces/qdeclarativeplace.cpp b/src/location/declarativeplaces/qdeclarativeplace.cpp new file mode 100644 index 00000000..91ef2026 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplace.cpp @@ -0,0 +1,1229 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeplace_p.h" +#include "qdeclarativecontactdetail_p.h" +#include "qdeclarativegeoserviceprovider_p.h" +#include "qdeclarativeplaceattribute_p.h" +#include "qdeclarativeplaceicon_p.h" +#include "error_messages.h" + +#include <QtCore/QCoreApplication> +#include <QtCore/QMetaObject> +#include <QtQml/QQmlEngine> +#include <QtQml/QQmlInfo> +#include <QtLocation/QGeoServiceProvider> +#include <QtLocation/QPlaceManager> +#include <QtLocation/QPlaceDetailsReply> +#include <QtLocation/QPlaceReply> +#include <QtLocation/QPlaceIdReply> +#include <QtLocation/QPlaceContactDetail> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Place + \instantiates QDeclarativePlace + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief The Place type represents a location that is a position of interest. + + The Place type represents a physical location with additional metadata describing that + location. Contrasted with \l Location, \l Address, and + \l {coordinate} type which are used to describe where a location is. + The basic properties of a Place are its \l name and \l location. + + Place objects are typically obtained from a search model and will generally only have their + basic properties set. The \l detailsFetched property can be used to test if further property + values need to be fetched from the \l Plugin. This can be done by invoking the \l getDetails() + method. Progress of the fetching operation can be monitored with the \l status property, which + will be set to Place.Fetching when the details are being fetched. + + The Place type has many properties holding information about the location. Details on how to + contact the place are available from the \l contactDetails property. Convenience properties + for obtaining the primary \l {primaryPhone}{phone}, \l {primaryFax}{fax}, + \l {primaryEmail}{email} and \l {primaryWebsite}{website} are also available. + + Each place is assigned zero or more \l categories. Categories are typically used when + searching for a particular kind of place, such as a restaurant or hotel. Some places have a + \l ratings object, which gives an indication of the quality of the place. + + Place metadata is provided by a \l supplier who may require that an \l attribution message be + displayed to the user when the place details are viewed. + + Places have an associated \l icon which can be used to represent a place on a map or to + decorate a delegate in a view. + + Places may have additional rich content associated with them. The currently supported rich + content include editorial descriptions, reviews and images. These are exposed as a set of + models for retrieving the content. Editorial descriptions of the place are available from the + \l editorialModel property. Reviews of the place are available from the \l reviewModel + property. A gallery of pictures of the place can be accessed using the \l imageModel property. + + Places may have additional attributes which are not covered in the formal API. The + \l extendedAttributes property provides access to these. The type of extended attributes + available is specific to each \l Plugin. + + A Place is almost always tied to a \l plugin. The \l plugin property must be set before it is + possible to call \l save(), \l remove() or \l getDetails(). The \l reviewModel, \l imageModel + and \l editorialModel are only valid then the \l plugin property is set. + + \section2 Saving a Place + + If the \l Plugin supports it, the Place type can be used to save a place. First create a new + Place and set its properties: + + \snippet declarative/places.qml Place savePlace def + + Then invoke the \l save() method: + + \snippet declarative/places.qml Place savePlace + + The \l status property will change to Place.Saving and then to Place.Ready if the save was + successful or to Place.Error if an error occurs. + + If the \l placeId property is set, the backend will update an existing place otherwise it will + create a new place. On success the \l placeId property will be updated with the identifier of the newly + saved place. + + \section3 Caveats + \input place-caveats.qdocinc + + \section3 Saving Between Plugins + When saving places between plugins, there are a few things to be aware of. + Some fields of a place such as the id, categories and icons are plugin specific entities. For example + the categories in one manager may not be recognised in another. + Therefore trying to save a place directly from one plugin to another is not possible. + + It is generally recommended that saving across plugins be handled as saving \l {Favorites}{favorites} + as explained in the Favorites section. However there is another approach which is to create a new place, + set its (destination) plugin and then use the \l copyFrom() method to copy the details of the original place. + Using \l copyFrom() only copies data that is supported by the destination plugin, + plugin specific data such as the place identifier is not copied over. Once the copy is done, + the place is in a suitable state to be saved. + + The following snippet provides an example of saving a place to a different plugin + using the \l copyFrom method: + + \snippet declarative/places.qml Place save to different plugin + + \section2 Removing a Place + + To remove a place, ensure that a Place object with a valid \l placeId property exists and call + its \l remove() method. The \l status property will change to Place.Removing and then to + Place.Ready if the save was successful or to Place.Error if an error occurs. + + \section2 Favorites + The Places API supports the concept of favorites. Favorites are generally implemented + by using two plugins, the first plugin is typically a read-only source of places (origin plugin) and a second + read/write plugin (destination plugin) is used to store places from the origin as favorites. + + Each Place has a favorite property which is intended to contain the corresponding place + from the destination plugin (the place itself is sourced from the origin plugin). Because both the original + place and favorite instances are available, the developer can choose which + properties to show to the user. For example the favorite may have a modified name which should + be displayed rather than the original name. + + \snippet declarative/places.qml Place favorite + + The following demonstrates how to save a new favorite instance. A call is made + to create/initialize the favorite instance and then the instance is saved. + + \snippet declarative/places.qml Place saveFavorite + + The following demonstrates favorite removal: + + \snippet declarative/places.qml Place removeFavorite 1 + \dots + \snippet declarative/places.qml Place removeFavorite 2 + + The PlaceSearchModel has a favoritesPlugin property. If the property is set, any places found + during a search are checked against the favoritesPlugin to see if there is a corresponding + favorite place. If so, the favorite property of the Place is set, otherwise the favorite + property is remains null. + + \sa PlaceSearchModel +*/ + +QDeclarativePlace::QDeclarativePlace(QObject *parent) +: QObject(parent), m_location(0), m_ratings(0), m_supplier(0), m_icon(0), + m_reviewModel(0), m_imageModel(0), m_editorialModel(0), + m_extendedAttributes(new QQmlPropertyMap(this)), + m_contactDetails(new QDeclarativeContactDetails(this)), m_reply(0), m_plugin(0), + m_complete(false), m_favorite(0), m_status(QDeclarativePlace::Ready) +{ + connect(m_contactDetails, SIGNAL(valueChanged(QString,QVariant)), + this, SLOT(contactsModified(QString,QVariant))); + + setPlace(QPlace()); +} + +QDeclarativePlace::QDeclarativePlace(const QPlace &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent) +: QObject(parent), m_location(0), m_ratings(0), m_supplier(0), m_icon(0), + m_reviewModel(0), m_imageModel(0), m_editorialModel(0), + m_extendedAttributes(new QQmlPropertyMap(this)), + m_contactDetails(new QDeclarativeContactDetails(this)), m_reply(0), m_plugin(plugin), + m_complete(false), m_favorite(0), m_status(QDeclarativePlace::Ready) +{ + Q_ASSERT(plugin); + + connect(m_contactDetails, SIGNAL(valueChanged(QString,QVariant)), + this, SLOT(contactsModified(QString,QVariant))); + + setPlace(src); +} + +QDeclarativePlace::~QDeclarativePlace() +{ +} + +// From QQmlParserStatus +void QDeclarativePlace::componentComplete() +{ + m_complete = true; +} + +/*! + \qmlproperty Plugin Place::plugin + + This property holds the \l Plugin that provided this place which can be used to retrieve more information about the service. +*/ +void QDeclarativePlace::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + m_plugin = plugin; + if (m_complete) + emit pluginChanged(); + + if (m_plugin->isAttached()) { + pluginReady(); + } else { + connect(m_plugin, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +void QDeclarativePlace::pluginReady() +{ + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return; + } +} + +QDeclarativeGeoServiceProvider *QDeclarativePlace::plugin() const +{ + return m_plugin; +} + +/*! + \qmlproperty ReviewModel Place::reviewModel + + This property holds a model which can be used to retrieve reviews about the place. +*/ +QDeclarativeReviewModel *QDeclarativePlace::reviewModel() +{ + if (!m_reviewModel) { + m_reviewModel = new QDeclarativeReviewModel(this); + m_reviewModel->setPlace(this); + } + + return m_reviewModel; +} + +/*! + \qmlproperty ImageModel Place::imageModel + + This property holds a model which can be used to retrieve images of the place. +*/ +QDeclarativePlaceImageModel *QDeclarativePlace::imageModel() +{ + if (!m_imageModel) { + m_imageModel = new QDeclarativePlaceImageModel(this); + m_imageModel->setPlace(this); + } + + return m_imageModel; +} + +/*! + \qmlproperty EditorialModel Place::editorialModel + + This property holds a model which can be used to retrieve editorial descriptions of the place. +*/ +QDeclarativePlaceEditorialModel *QDeclarativePlace::editorialModel() +{ + if (!m_editorialModel) { + m_editorialModel = new QDeclarativePlaceEditorialModel(this); + m_editorialModel->setPlace(this); + } + + return m_editorialModel; +} + +/*! + \qmlproperty QPlace Place::place + + For details on how to use this property to interface between C++ and QML see + "\l {Place - QPlace} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativePlace::setPlace(const QPlace &src) +{ + QPlace previous = m_src; + m_src = src; + + if (previous.categories() != m_src.categories()) { + synchronizeCategories(); + emit categoriesChanged(); + } + + if (m_location && m_location->parent() == this) { + m_location->setLocation(m_src.location()); + } else if (!m_location || m_location->parent() != this) { + m_location = new QDeclarativeGeoLocation(m_src.location(), this); + emit locationChanged(); + } + + if (m_ratings && m_ratings->parent() == this) { + m_ratings->setRatings(m_src.ratings()); + } else if (!m_ratings || m_ratings->parent() != this) { + m_ratings = new QDeclarativeRatings(m_src.ratings(), this); + emit ratingsChanged(); + } + + if (m_supplier && m_supplier->parent() == this) { + m_supplier->setSupplier(m_src.supplier(), m_plugin); + } else if (!m_supplier || m_supplier->parent() != this) { + m_supplier = new QDeclarativeSupplier(m_src.supplier(), m_plugin, this); + emit supplierChanged(); + } + + if (m_icon && m_icon->parent() == this) { + m_icon->setPlugin(m_plugin); + m_icon->setIcon(m_src.icon()); + } else if (!m_icon || m_icon->parent() != this) { + m_icon = new QDeclarativePlaceIcon(m_src.icon(), m_plugin, this); + emit iconChanged(); + } + + if (previous.name() != m_src.name()) { + emit nameChanged(); + } + if (previous.placeId() != m_src.placeId()) { + emit placeIdChanged(); + } + if (previous.attribution() != m_src.attribution()) { + emit attributionChanged(); + } + if (previous.detailsFetched() != m_src.detailsFetched()) { + emit detailsFetchedChanged(); + } + if (previous.primaryPhone() != m_src.primaryPhone()) { + emit primaryPhoneChanged(); + } + if (previous.primaryFax() != m_src.primaryFax()) { + emit primaryFaxChanged(); + } + if (previous.primaryEmail() != m_src.primaryEmail()) { + emit primaryEmailChanged(); + } + if (previous.primaryWebsite() != m_src.primaryWebsite()) { + emit primaryWebsiteChanged(); + } + + if (m_reviewModel && m_src.totalContentCount(QPlaceContent::ReviewType) >= 0) { + m_reviewModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ReviewType), + m_src.content(QPlaceContent::ReviewType)); + } + if (m_imageModel && m_src.totalContentCount(QPlaceContent::ImageType) >= 0) { + m_imageModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ImageType), + m_src.content(QPlaceContent::ImageType)); + } + if (m_editorialModel && m_src.totalContentCount(QPlaceContent::EditorialType) >= 0) { + m_editorialModel->initializeCollection(m_src.totalContentCount(QPlaceContent::EditorialType), + m_src.content(QPlaceContent::EditorialType)); + } + + synchronizeExtendedAttributes(); + synchronizeContacts(); +} + +QPlace QDeclarativePlace::place() +{ + // The following properties are not stored in m_src but instead stored in QDeclarative* objects + + QPlace result = m_src; + + // Categories + QList<QPlaceCategory> categories; + foreach (QDeclarativeCategory *value, m_categories) + categories.append(value->category()); + + result.setCategories(categories); + + // Location + result.setLocation(m_location ? m_location->location() : QGeoLocation()); + + // Rating + result.setRatings(m_ratings ? m_ratings->ratings() : QPlaceRatings()); + + // Supplier + result.setSupplier(m_supplier ? m_supplier->supplier() : QPlaceSupplier()); + + // Icon + result.setIcon(m_icon ? m_icon->icon() : QPlaceIcon()); + + //contact details + QList<QPlaceContactDetail> cppDetails; + foreach (const QString &key, m_contactDetails->keys()) { + cppDetails.clear(); + if (m_contactDetails->value(key).type() == QVariant::List) { + QVariantList detailsVarList = m_contactDetails->value(key).toList(); + foreach (const QVariant &detailVar, detailsVarList) { + QDeclarativeContactDetail *detail = qobject_cast<QDeclarativeContactDetail *>(detailVar.value<QObject *>()); + if (detail) + cppDetails.append(detail->contactDetail()); + } + } else { + QDeclarativeContactDetail *detail = qobject_cast<QDeclarativeContactDetail *>(m_contactDetails->value(key).value<QObject *>()); + if (detail) + cppDetails.append(detail->contactDetail()); + } + result.setContactDetails(key, cppDetails); + } + + return result; +} + +/*! + \qmlproperty QtPositioning::Location Place::location + + This property holds the location of the place which can be used to retrieve the coordinate, + address and the bounding box. +*/ +void QDeclarativePlace::setLocation(QDeclarativeGeoLocation *location) +{ + if (m_location == location) + return; + + if (m_location && m_location->parent() == this) + delete m_location; + + m_location = location; + emit locationChanged(); +} + +QDeclarativeGeoLocation *QDeclarativePlace::location() +{ + return m_location; +} + +/*! + \qmlproperty Ratings Place::ratings + + This property holds ratings of the place. The ratings provide an indication of the quality of a + place. +*/ +void QDeclarativePlace::setRatings(QDeclarativeRatings *rating) +{ + if (m_ratings == rating) + return; + + if (m_ratings && m_ratings->parent() == this) + delete m_ratings; + + m_ratings = rating; + emit ratingsChanged(); +} + +QDeclarativeRatings *QDeclarativePlace::ratings() +{ + + return m_ratings; +} + +/*! + \qmlproperty Supplier Place::supplier + + This property holds the supplier of the place data. + The supplier is typically a business or organization that collected the data about the place. +*/ +void QDeclarativePlace::setSupplier(QDeclarativeSupplier *supplier) +{ + if (m_supplier == supplier) + return; + + if (m_supplier && m_supplier->parent() == this) + delete m_supplier; + + m_supplier = supplier; + emit supplierChanged(); +} + +QDeclarativeSupplier *QDeclarativePlace::supplier() const +{ + return m_supplier; +} + +/*! + \qmlproperty Icon Place::icon + + This property holds a graphical icon which can be used to represent the place. +*/ +QDeclarativePlaceIcon *QDeclarativePlace::icon() const +{ + return m_icon; +} + +void QDeclarativePlace::setIcon(QDeclarativePlaceIcon *icon) +{ + if (m_icon == icon) + return; + + if (m_icon && m_icon->parent() == this) + delete m_icon; + + m_icon = icon; + emit iconChanged(); +} + +/*! + \qmlproperty string Place::name + + This property holds the name of the place which can be used to represent the place. +*/ +void QDeclarativePlace::setName(const QString &name) +{ + if (m_src.name() != name) { + m_src.setName(name); + emit nameChanged(); + } +} + +QString QDeclarativePlace::name() const +{ + return m_src.name(); +} + +/*! + \qmlproperty string Place::placeId + + This property holds the unique identifier of the place. The place identifier is only meaningful to the + \l Plugin that generated it and is not transferable between \l {Plugin}{Plugins}. The place id + is not guaranteed to be universally unique, but unique within the \l Plugin that generated it. + + If only the place identifier is known, all other place data can fetched from the \l Plugin. + + \snippet declarative/places.qml Place placeId +*/ +void QDeclarativePlace::setPlaceId(const QString &placeId) +{ + if (m_src.placeId() != placeId) { + m_src.setPlaceId(placeId); + emit placeIdChanged(); + } +} + +QString QDeclarativePlace::placeId() const +{ + return m_src.placeId(); +} + +/*! + \qmlproperty string Place::attribution + + This property holds a rich text attribution string for the place. + Some providers may require that the attribution be shown to the user + whenever a place is displayed. The contents of this property should + be shown to the user if it is not empty. +*/ +void QDeclarativePlace::setAttribution(const QString &attribution) +{ + if (m_src.attribution() != attribution) { + m_src.setAttribution(attribution); + emit attributionChanged(); + } +} + +QString QDeclarativePlace::attribution() const +{ + return m_src.attribution(); +} + +/*! + \qmlproperty bool Place::detailsFetched + + This property indicates whether the details of the place have been fetched. If this property + is false, the place details have not yet been fetched. Fetching can be done by invoking the + \l getDetails() method. + + \sa getDetails() +*/ +bool QDeclarativePlace::detailsFetched() const +{ + return m_src.detailsFetched(); +} + +/*! + \qmlproperty enumeration Place::status + + This property holds the status of the place. It can be one of: + + \table + \row + \li Place.Ready + \li No error occurred during the last operation, further operations may be performed on + the place. + \row + \li Place.Saving + \li The place is currently being saved, no other operation may be performed until + complete. + \row + \li Place.Fetching + \li The place details are currently being fetched, no other operations may be performed + until complete. + \row + \li Place.Removing + \li The place is currently being removed, no other operations can be performed until + complete. + \row + \li Place.Error + \li An error occurred during the last operation, further operations can still be + performed on the place. + \endtable + + The status of a place can be checked by connecting the status property + to a handler function, and then have the handler function process the change + in status. + + \snippet declarative/places.qml Place checkStatus + \dots + \snippet declarative/places.qml Place checkStatus handler + +*/ +void QDeclarativePlace::setStatus(Status status, const QString &errorString) +{ + Status originalStatus = m_status; + m_status = status; + m_errorString = errorString; + + if (originalStatus != m_status) + emit statusChanged(); +} + +QDeclarativePlace::Status QDeclarativePlace::status() const +{ + return m_status; +} + +/*! + \internal +*/ +void QDeclarativePlace::finished() +{ + if (!m_reply) + return; + + if (m_reply->error() == QPlaceReply::NoError) { + switch (m_reply->type()) { + case (QPlaceReply::IdReply) : { + QPlaceIdReply *idReply = qobject_cast<QPlaceIdReply *>(m_reply); + + switch (idReply->operationType()) { + case QPlaceIdReply::SavePlace: + setPlaceId(idReply->id()); + break; + case QPlaceIdReply::RemovePlace: + break; + default: + //Other operation types shouldn't ever be received. + break; + } + break; + } + case (QPlaceReply::DetailsReply): { + QPlaceDetailsReply *detailsReply = qobject_cast<QPlaceDetailsReply *>(m_reply); + setPlace(detailsReply->place()); + break; + } + default: + //other types of replies shouldn't ever be received. + break; + } + + m_errorString.clear(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativePlace::Ready); + } else { + QString errorString = m_reply->errorString(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativePlace::Error, errorString); + } +} + +/*! + \internal +*/ +void QDeclarativePlace::contactsModified(const QString &key, const QVariant &) +{ + primarySignalsEmission(key); +} + +/*! + \internal +*/ +void QDeclarativePlace::cleanupDeletedCategories() +{ + foreach (QDeclarativeCategory * category, m_categoriesToBeDeleted) { + if (category->parent() == this) + delete category; + } + m_categoriesToBeDeleted.clear(); +} + +/*! + \qmlmethod void Place::getDetails() + + This method starts fetching place details. + + The \l status property will change to Place.Fetching while the fetch is in progress. On + success the object's properties will be updated, \l status will be set to Place.Ready and + \l detailsFetched will be set to true. On error \l status will be set to Place.Error. The + \l errorString() method can be used to get the details of the error. +*/ +void QDeclarativePlace::getDetails() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->getPlaceDetails(placeId()); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + setStatus(QDeclarativePlace::Fetching); +} + +/*! + \qmlmethod void Place::save() + + This method performs a save operation on the place. + + The \l status property will change to Place.Saving while the save operation is in progress. On + success the \l status will be set to Place.Ready. On error \l status will be set to Place.Error. + The \l errorString() method can be used to get the details of the error. + + If the \l placeId property was previously empty, it will be assigned a valid value automatically + during a successful save operation. + + Note that a \l PlaceSearchModel will call Place::getDetails on any place that it detects an update + on. A consequence of this is that whenever a Place from a \l PlaceSearchModel is successfully saved, + it will be followed by a fetch of place details, leading to a sequence of state changes + of \c Saving, \c Ready, \c Fetching, \c Ready. + +*/ +void QDeclarativePlace::save() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->savePlace(place()); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + setStatus(QDeclarativePlace::Saving); +} + +/*! + \qmlmethod void Place::remove() + + This method performs a remove operation on the place. + + The \l status property will change to Place.Removing while the save operation is in progress. + On success \l status will be set to Place.Ready. On error \l status will be set to + Place.Error. The \l errorString() method can be used to get the details of the error. +*/ +void QDeclarativePlace::remove() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->removePlace(place().placeId()); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + setStatus(QDeclarativePlace::Removing); +} + +/*! + \qmlmethod string Place::errorString() + + Returns a string description of the error of the last operation. If the last operation + completed successfully then the string is empty. +*/ +QString QDeclarativePlace::errorString() const +{ + return m_errorString; +} + +/*! + \qmlproperty string Place::primaryPhone + + This property holds the primary phone number of the place. If no "phone" contact detail is + defined for this place, this property will be an empty string. It is equivalent to: + + + \snippet declarative/places.qml Place primaryPhone +*/ +QString QDeclarativePlace::primaryPhone() const +{ + return primaryValue(QPlaceContactDetail::Phone); +} + +/*! + \qmlproperty string Place::primaryFax + + This property holds the primary fax number of the place. If no "fax" contact detail is + defined for this place this property will be an empty string. It is equivalent to + + \snippet declarative/places.qml Place primaryFax +*/ +QString QDeclarativePlace::primaryFax() const +{ + return primaryValue(QPlaceContactDetail::Fax); +} + +/*! + \qmlproperty string Place::primaryEmail + + This property holds the primary email address of the place. If no "email" contact detail is + defined for this place this property will be an empty string. It is equivalent to + + \snippet declarative/places.qml Place primaryEmail +*/ +QString QDeclarativePlace::primaryEmail() const +{ + return primaryValue(QPlaceContactDetail::Email); +} + +/*! + \qmlproperty string Place::primaryWebsite + + This property holds the primary website url of the place. If no "website" contact detail is + defined for this place this property will be an empty string. It is equivalent to + + \snippet declarative/places.qml Place primaryWebsite +*/ + +QUrl QDeclarativePlace::primaryWebsite() const +{ + return QUrl(primaryValue(QPlaceContactDetail::Website)); +} + +/*! + \qmlproperty ExtendedAttributes Place::extendedAttributes + + This property holds the extended attributes of a place. Extended attributes are additional + information about a place not covered by the place's properties. +*/ +QQmlPropertyMap *QDeclarativePlace::extendedAttributes() const +{ + return m_extendedAttributes; +} + +/*! + \qmlproperty ContactDetails Place::contactDetails + + This property holds the contact information for this place, for example a phone number or + a website URL. This property is a map of \l ContactDetail objects. +*/ +QDeclarativeContactDetails *QDeclarativePlace::contactDetails() const +{ + return m_contactDetails; +} + +/*! + \qmlproperty list<Category> Place::categories + + This property holds the list of categories this place is a member of. The categories that can + be assigned to a place are specific to each \l plugin. +*/ +QQmlListProperty<QDeclarativeCategory> QDeclarativePlace::categories() +{ + return QQmlListProperty<QDeclarativeCategory>(this, + 0, // opaque data parameter + category_append, + category_count, + category_at, + category_clear); +} + +/*! + \internal +*/ +void QDeclarativePlace::category_append(QQmlListProperty<QDeclarativeCategory> *prop, + QDeclarativeCategory *value) +{ + QDeclarativePlace *object = static_cast<QDeclarativePlace *>(prop->object); + + if (object->m_categoriesToBeDeleted.contains(value)) + object->m_categoriesToBeDeleted.removeAll(value); + + if (!object->m_categories.contains(value)) { + object->m_categories.append(value); + QList<QPlaceCategory> list = object->m_src.categories(); + list.append(value->category()); + object->m_src.setCategories(list); + + emit object->categoriesChanged(); + } +} + +/*! + \internal +*/ +int QDeclarativePlace::category_count(QQmlListProperty<QDeclarativeCategory> *prop) +{ + return static_cast<QDeclarativePlace *>(prop->object)->m_categories.count(); +} + +/*! + \internal +*/ +QDeclarativeCategory *QDeclarativePlace::category_at(QQmlListProperty<QDeclarativeCategory> *prop, + int index) +{ + QDeclarativePlace *object = static_cast<QDeclarativePlace *>(prop->object); + QDeclarativeCategory *res = NULL; + if (object->m_categories.count() > index && index > -1) { + res = object->m_categories[index]; + } + return res; +} + +/*! + \internal +*/ +void QDeclarativePlace::category_clear(QQmlListProperty<QDeclarativeCategory> *prop) +{ + QDeclarativePlace *object = static_cast<QDeclarativePlace *>(prop->object); + if (object->m_categories.isEmpty()) + return; + + for (int i = 0; i < object->m_categories.count(); ++i) { + if (object->m_categories.at(i)->parent() == object) + object->m_categoriesToBeDeleted.append(object->m_categories.at(i)); + } + + object->m_categories.clear(); + object->m_src.setCategories(QList<QPlaceCategory>()); + emit object->categoriesChanged(); + QMetaObject::invokeMethod(object, "cleanupDeletedCategories", Qt::QueuedConnection); +} + +/*! + \internal +*/ +void QDeclarativePlace::synchronizeCategories() +{ + qDeleteAll(m_categories); + m_categories.clear(); + foreach (const QPlaceCategory &value, m_src.categories()) { + QDeclarativeCategory *declarativeValue = new QDeclarativeCategory(value, m_plugin, this); + m_categories.append(declarativeValue); + } +} + +/*! + \qmlproperty enumeration Place::visibility + + This property holds the visibility of the place. It can be one of: + + \table + \row + \li Place.UnspecifiedVisibility + \li The visibility of the place is unspecified, the default visibility of the \l Plugin + will be used. + \row + \li Place.DeviceVisibility + \li The place is limited to the current device. The place will not be transferred off + of the device. + \row + \li Place.PrivateVisibility + \li The place is private to the current user. The place may be transferred to an online + service but is only ever visible to the current user. + \row + \li Place.PublicVisibility + \li The place is public. + \endtable + + Note that visibility does not affect how the place is displayed + in the user-interface of an application on the device. Instead, + it defines the sharing semantics of the place. +*/ +QDeclarativePlace::Visibility QDeclarativePlace::visibility() const +{ + return static_cast<QDeclarativePlace::Visibility>(m_src.visibility()); +} + +void QDeclarativePlace::setVisibility(Visibility visibility) +{ + if (static_cast<QDeclarativePlace::Visibility>(m_src.visibility()) == visibility) + return; + + m_src.setVisibility(static_cast<QLocation::Visibility>(visibility)); + emit visibilityChanged(); +} + +/*! + \qmlproperty Place Place::favorite + + This property holds the favorite instance of a place. +*/ +QDeclarativePlace *QDeclarativePlace::favorite() const +{ + return m_favorite; +} + +void QDeclarativePlace::setFavorite(QDeclarativePlace *favorite) +{ + + if (m_favorite == favorite) + return; + + if (m_favorite && m_favorite->parent() == this) + delete m_favorite; + + m_favorite = favorite; + emit favoriteChanged(); +} + +/*! + \qmlmethod void Place::copyFrom(Place original) + + Copies data from an \a original place into this place. Only data that is supported by this + place's plugin is copied over and plugin specific data such as place identifier is not copied over. +*/ +void QDeclarativePlace::copyFrom(QDeclarativePlace *original) +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + setPlace(placeManager->compatiblePlace(original->place())); +} + +/*! + \qmlmethod void Place::initializeFavorite(Plugin destinationPlugin) + + Creates a favorite instance for the place which is to be saved into the + \a destination plugin. This method does nothing if the favorite property is + not null. +*/ +void QDeclarativePlace::initializeFavorite(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_favorite == 0) { + QDeclarativePlace *place = new QDeclarativePlace(this); + place->setPlugin(plugin); + place->copyFrom(this); + setFavorite(place); + } +} + +/*! + \internal +*/ +void QDeclarativePlace::synchronizeExtendedAttributes() +{ + QStringList keys = m_extendedAttributes->keys(); + foreach (const QString &key, keys) + m_extendedAttributes->clear(key); + + QStringList attributeTypes = m_src.extendedAttributeTypes(); + foreach (const QString &attributeType, attributeTypes) { + m_extendedAttributes->insert(attributeType, + qVariantFromValue(new QDeclarativePlaceAttribute(m_src.extendedAttribute(attributeType)))); + } + + emit extendedAttributesChanged(); +} + +/*! + \internal +*/ +void QDeclarativePlace::synchronizeContacts() +{ + //clear out contact data + foreach (const QString &contactType, m_contactDetails->keys()) { + QList<QVariant> contacts = m_contactDetails->value(contactType).toList(); + foreach (const QVariant &var, contacts) { + QObject *obj = var.value<QObject *>(); + if (obj->parent() == this) + delete obj; + } + m_contactDetails->insert(contactType, QVariantList()); + } + + //insert new contact data from source place + foreach (const QString &contactType, m_src.contactTypes()) { + QList<QPlaceContactDetail> sourceContacts = m_src.contactDetails(contactType); + QVariantList declContacts; + foreach (const QPlaceContactDetail &sourceContact, sourceContacts) { + QDeclarativeContactDetail *declContact = new QDeclarativeContactDetail(this); + declContact->setContactDetail(sourceContact); + declContacts.append(QVariant::fromValue(qobject_cast<QObject *>(declContact))); + } + m_contactDetails->insert(contactType, declContacts); + } + primarySignalsEmission(); +} + +/*! + \internal + Helper function to emit the signals for the primary___() + fields. It is expected that the values of the primary___() + functions have already been modified to new values. +*/ +void QDeclarativePlace::primarySignalsEmission(const QString &type) +{ + if (type.isEmpty() || type == QPlaceContactDetail::Phone) { + if (m_prevPrimaryPhone != primaryPhone()) { + m_prevPrimaryPhone = primaryPhone(); + emit primaryPhoneChanged(); + } + if (!type.isEmpty()) + return; + } + + if (type.isEmpty() || type == QPlaceContactDetail::Email) { + if (m_prevPrimaryEmail != primaryEmail()) { + m_prevPrimaryEmail = primaryEmail(); + emit primaryEmailChanged(); + } + if (!type.isEmpty()) + return; + } + + if (type.isEmpty() || type == QPlaceContactDetail::Website) { + if (m_prevPrimaryWebsite != primaryWebsite()) { + m_prevPrimaryWebsite = primaryWebsite(); + emit primaryWebsiteChanged(); + } + if (!type.isEmpty()) + return; + } + + if (type.isEmpty() || type == QPlaceContactDetail::Fax) { + if (m_prevPrimaryFax != primaryFax()) { + m_prevPrimaryFax = primaryFax(); + emit primaryFaxChanged(); + } + } +} + +/*! + \internal + Helper function to return the manager, this manager is intended to be used + to perform the next operation. If a an operation is currently underway + then return a null pointer. +*/ +QPlaceManager *QDeclarativePlace::manager() +{ + if (m_status != QDeclarativePlace::Ready && m_status != QDeclarativePlace::Error) + return 0; + + if (m_reply) { + m_reply->abort(); + m_reply->deleteLater(); + m_reply = 0; + } + + if (!m_plugin) { + qmlWarning(this) << QStringLiteral("Plugin is not assigned to place."); + return 0; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider) + return 0; + + QPlaceManager *placeManager = serviceProvider->placeManager(); + + if (!placeManager) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return 0; + } + + return placeManager; +} + +/*! + \internal +*/ +QString QDeclarativePlace::primaryValue(const QString &contactType) const +{ + QVariant value = m_contactDetails->value(contactType); + if (value.userType() == qMetaTypeId<QJSValue>()) + value = value.value<QJSValue>().toVariant(); + + if (value.userType() == QVariant::List) { + QVariantList detailList = m_contactDetails->value(contactType).toList(); + if (!detailList.isEmpty()) { + QDeclarativeContactDetail *primaryDetail = qobject_cast<QDeclarativeContactDetail *>(detailList.at(0).value<QObject *>()); + if (primaryDetail) + return primaryDetail->value(); + } + } else if (value.userType() == QMetaType::QObjectStar) { + QDeclarativeContactDetail *primaryDetail = qobject_cast<QDeclarativeContactDetail *>(m_contactDetails->value(contactType).value<QObject *>()); + if (primaryDetail) + return primaryDetail->value(); + } + + return QString(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplace_p.h b/src/location/declarativeplaces/qdeclarativeplace_p.h new file mode 100644 index 00000000..5a1470fe --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplace_p.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACE_P_H +#define QDECLARATIVEPLACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtCore/QObject> +#include <QtQml/QQmlListProperty> +#include <QtQml/QQmlParserStatus> +#include <QtQml/QQmlPropertyMap> +#include <QtLocation/QPlace> + +#include <QtPositioning/private/qdeclarativegeolocation_p.h> +#include <QtLocation/private/qdeclarativecategory_p.h> +#include <QtLocation/private/qdeclarativecontactdetail_p.h> +#include <QtLocation/private/qdeclarativesupplier_p.h> +#include <QtLocation/private/qdeclarativeratings_p.h> +#include <QtLocation/private/qdeclarativereviewmodel_p.h> +#include <QtLocation/private/qdeclarativeplaceimagemodel_p.h> +#include <QtLocation/private/qdeclarativeplaceeditorialmodel_p.h> + +QT_BEGIN_NAMESPACE + +class QPlaceReply; + +class QPlaceManager; +class QDeclarativePlaceIcon; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlace : public QObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_ENUMS(Status Visibility) + + Q_PROPERTY(QPlace place READ place WRITE setPlace) + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(QQmlListProperty<QDeclarativeCategory> categories READ categories NOTIFY categoriesChanged) + Q_PROPERTY(QDeclarativeGeoLocation *location READ location WRITE setLocation NOTIFY locationChanged) + Q_PROPERTY(QDeclarativeRatings *ratings READ ratings WRITE setRatings NOTIFY ratingsChanged) + Q_PROPERTY(QDeclarativeSupplier *supplier READ supplier WRITE setSupplier NOTIFY supplierChanged) + Q_PROPERTY(QDeclarativePlaceIcon *icon READ icon WRITE setIcon NOTIFY iconChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString placeId READ placeId WRITE setPlaceId NOTIFY placeIdChanged) + Q_PROPERTY(QString attribution READ attribution WRITE setAttribution NOTIFY attributionChanged) + + Q_PROPERTY(QDeclarativeReviewModel *reviewModel READ reviewModel NOTIFY reviewModelChanged) + Q_PROPERTY(QDeclarativePlaceImageModel *imageModel READ imageModel NOTIFY imageModelChanged) + Q_PROPERTY(QDeclarativePlaceEditorialModel *editorialModel READ editorialModel NOTIFY editorialModelChanged) + + Q_PROPERTY(QObject *extendedAttributes READ extendedAttributes NOTIFY extendedAttributesChanged) + Q_PROPERTY(QObject *contactDetails READ contactDetails NOTIFY contactDetailsChanged) + Q_PROPERTY(bool detailsFetched READ detailsFetched NOTIFY detailsFetchedChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + + Q_PROPERTY(QString primaryPhone READ primaryPhone NOTIFY primaryPhoneChanged) + Q_PROPERTY(QString primaryFax READ primaryFax NOTIFY primaryFaxChanged) + Q_PROPERTY(QString primaryEmail READ primaryEmail NOTIFY primaryEmailChanged) + Q_PROPERTY(QUrl primaryWebsite READ primaryWebsite NOTIFY primaryWebsiteChanged) + + Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged) + Q_PROPERTY(QDeclarativePlace *favorite READ favorite WRITE setFavorite NOTIFY favoriteChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativePlace(QObject *parent = 0); + QDeclarativePlace(const QPlace &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent = 0); + ~QDeclarativePlace(); + + enum Status {Ready, Saving, Fetching, Removing, Error}; + enum Visibility { + UnspecifiedVisibility = QLocation::UnspecifiedVisibility, + DeviceVisibility = QLocation::DeviceVisibility, + PrivateVisibility = QLocation::PrivateVisibility, + PublicVisibility = QLocation::PublicVisibility + }; + + //From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + QDeclarativeReviewModel *reviewModel(); + QDeclarativePlaceImageModel *imageModel(); + QDeclarativePlaceEditorialModel *editorialModel(); + + QPlace place(); + void setPlace(const QPlace &src); + + QQmlListProperty<QDeclarativeCategory> categories(); + static void category_append(QQmlListProperty<QDeclarativeCategory> *prop, + QDeclarativeCategory *value); + static int category_count(QQmlListProperty<QDeclarativeCategory> *prop); + static QDeclarativeCategory *category_at(QQmlListProperty<QDeclarativeCategory> *prop, int index); + static void category_clear(QQmlListProperty<QDeclarativeCategory> *prop); + + QDeclarativeGeoLocation *location(); + void setLocation(QDeclarativeGeoLocation *location); + QDeclarativeRatings *ratings(); + void setRatings(QDeclarativeRatings *ratings); + QDeclarativeSupplier *supplier() const; + void setSupplier(QDeclarativeSupplier *supplier); + QDeclarativePlaceIcon *icon() const; + void setIcon(QDeclarativePlaceIcon *icon); + QString name() const; + void setName(const QString &name); + QString placeId() const; + void setPlaceId(const QString &placeId); + QString attribution() const; + void setAttribution(const QString &attribution); + bool detailsFetched() const; + + Status status() const; + void setStatus(Status status, const QString &errorString = QString()); + + Q_INVOKABLE void getDetails(); + Q_INVOKABLE void save(); + Q_INVOKABLE void remove(); + Q_INVOKABLE QString errorString() const; + + QString primaryPhone() const; + QString primaryFax() const; + QString primaryEmail() const; + QUrl primaryWebsite() const; + + QQmlPropertyMap *extendedAttributes() const; + + QDeclarativeContactDetails *contactDetails() const; + + Visibility visibility() const; + void setVisibility(Visibility visibility); + + QDeclarativePlace *favorite() const; + void setFavorite(QDeclarativePlace *favorite); + + Q_INVOKABLE void copyFrom(QDeclarativePlace *original); + Q_INVOKABLE void initializeFavorite(QDeclarativeGeoServiceProvider *plugin); + +Q_SIGNALS: + void pluginChanged(); + void categoriesChanged(); + void locationChanged(); + void ratingsChanged(); + void supplierChanged(); + void iconChanged(); + void nameChanged(); + void placeIdChanged(); + void attributionChanged(); + void detailsFetchedChanged(); + void reviewModelChanged(); + void imageModelChanged(); + void editorialModelChanged(); + + void primaryPhoneChanged(); + void primaryFaxChanged(); + void primaryEmailChanged(); + void primaryWebsiteChanged(); + + void extendedAttributesChanged(); + void contactDetailsChanged(); + void statusChanged(); + void visibilityChanged(); + void favoriteChanged(); + +private Q_SLOTS: + void finished(); + void contactsModified(const QString &, const QVariant &); + void pluginReady(); + void cleanupDeletedCategories(); +private: + void synchronizeCategories(); + void synchronizeExtendedAttributes(); + void synchronizeContacts(); + void primarySignalsEmission(const QString &type = QString()); + QString primaryValue(const QString &contactType) const; + +private: + QPlaceManager *manager(); + + QList<QDeclarativeCategory *> m_categories; + QDeclarativeGeoLocation *m_location; + QDeclarativeRatings *m_ratings; + QDeclarativeSupplier *m_supplier; + QDeclarativePlaceIcon *m_icon; + QDeclarativeReviewModel *m_reviewModel; + QDeclarativePlaceImageModel *m_imageModel; + QDeclarativePlaceEditorialModel *m_editorialModel; + QQmlPropertyMap *m_extendedAttributes; + QDeclarativeContactDetails *m_contactDetails; + + QPlace m_src; + + QPlaceReply *m_reply; + + QDeclarativeGeoServiceProvider *m_plugin; + bool m_complete; + + QString m_prevPrimaryPhone; + QString m_prevPrimaryEmail; + QString m_prevPrimaryFax; + QUrl m_prevPrimaryWebsite; + + QDeclarativePlace *m_favorite; + + Status m_status; + QString m_errorString; + + QList<QDeclarativeCategory *>m_categoriesToBeDeleted; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePlace) + +#endif // QDECLARATIVEPLACE_P_H diff --git a/src/location/declarativeplaces/qdeclarativeplaceattribute.cpp b/src/location/declarativeplaces/qdeclarativeplaceattribute.cpp new file mode 100644 index 00000000..20adbafb --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceattribute.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** 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 "qdeclarativeplaceattribute_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ExtendedAttributes + \instantiates QQmlPropertyMap + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief The ExtendedAttributes type holds additional data about a \l Place. + + The ExtendedAttributes type is a map of \l {PlaceAttribute}{PlaceAttributes}. To access + attributes in the map use the \l keys() method to get the list of keys stored in the map and + use the \c {[]} operator to access the \l PlaceAttribute items. + + The following are standard keys that are defined by the API. \l Plugin + implementations are free to define additional keys. Custom keys should + be qualified by a unique prefix to avoid clashes. + \table + \header + \li key + \li description + \row + \li openingHours + \li The trading hours of the place + \row + \li payment + \li The types of payment the place accepts, for example visa, mastercard. + \row + \li x_provider + \li The name of the provider that a place is sourced from + \row + \li x_id_<provider> (for example x_id_here) + \li An alternative identifier which identifies the place from the + perspective of the specified provider. + \endtable + + Some plugins may not support attributes at all, others may only support a + certain set, others still may support a dynamically changing set of attributes + over time or even allow attributes to be arbitrarily defined by the client + application. The attributes could also vary on a place by place basis, + for example one place may have opening hours while another does not. + Consult the \l {Plugin References and Parameters}{plugin + references} for details. + + Some attributes may not be intended to be readable by end users, the label field + of such attributes is empty to indicate this fact. + + \note ExtendedAttributes instances are only ever used in the context of \l {Place}s. It is not + possible to create an ExtendedAttributes instance directly or re-assign a \l {Place}'s + ExtendedAttributes property. Modification of ExtendedAttributes can only be accomplished + via Javascript. + + The following example shows how to access all \l {PlaceAttribute}{PlaceAttributes} and print + them to the console: + + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ExtendedAttributes read + + The following example shows how to assign and modify an attribute: + \snippet declarative/places.qml ExtendedAttributes write + + \sa PlaceAttribute, QQmlPropertyMap +*/ + +/*! + \qmlmethod variant ExtendedAttributes::keys() + + Returns an array of place attribute keys currently stored in the map. +*/ + +/*! + \qmlsignal void ExtendedAttributes::valueChanged(string key, variant value) + + This signal is emitted when the set of attributes changes. \a key is the key + corresponding to the \a value that was changed. + + The corresponding handler is \c onValueChanged. +*/ + +/*! + \qmltype PlaceAttribute + \instantiates QDeclarativePlaceAttribute + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief The PlaceAttribute type holds generic place attribute information. + + A place attribute stores an additional piece of information about a \l Place that is not + otherwise exposed through the \l Place type. A PlaceAttribute is a textual piece of data, + accessible through the \l text property, and a \l label. Both the \l text and \l label + properties are intended to be displayed to the user. PlaceAttributes are stored in an + \l ExtendedAttributes map with a unique key. + + The following example shows how to display all attributes in a list: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ExtendedAttributes + + The following example shows how to assign and modify an attribute: + \snippet declarative/places.qml ExtendedAttributes write +*/ + +QDeclarativePlaceAttribute::QDeclarativePlaceAttribute(QObject *parent) + : QObject(parent) +{ +} + +QDeclarativePlaceAttribute::QDeclarativePlaceAttribute(const QPlaceAttribute &src, QObject *parent) + : QObject(parent),m_attribute(src) +{ +} + +QDeclarativePlaceAttribute::~QDeclarativePlaceAttribute() +{ +} + +/*! + \qmlproperty QPlaceAttribute PlaceAttribute::attribute + + For details on how to use this property to interface between C++ and QML see + "\l {PlaceAttribute - QPlaceAttribute} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativePlaceAttribute::setAttribute(const QPlaceAttribute &src) +{ + QPlaceAttribute prevAttribute = m_attribute; + m_attribute = src; + + if (m_attribute.label() != prevAttribute.label()) + emit labelChanged(); + if (m_attribute.text() != prevAttribute.text()) + emit textChanged(); +} + +QPlaceAttribute QDeclarativePlaceAttribute::attribute() const +{ + return m_attribute; +} + +/*! + \qmlproperty string PlaceAttribute::label + + This property holds the attribute label which is a user visible string + describing the attribute. +*/ +void QDeclarativePlaceAttribute::setLabel(const QString &label) +{ + if (m_attribute.label() != label) { + m_attribute.setLabel(label); + emit labelChanged(); + } +} + +QString QDeclarativePlaceAttribute::label() const +{ + return m_attribute.label(); +} + +/*! + \qmlproperty string PlaceAttribute::text + + This property holds the attribute text which can be used to show additional information about the place. +*/ +void QDeclarativePlaceAttribute::setText(const QString &text) +{ + if (m_attribute.text() != text) { + m_attribute.setText(text); + emit textChanged(); + } +} + +QString QDeclarativePlaceAttribute::text() const +{ + return m_attribute.text(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceattribute_p.h b/src/location/declarativeplaces/qdeclarativeplaceattribute_p.h new file mode 100644 index 00000000..8079df9c --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceattribute_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEATTRIBUTE_P_H +#define QDECLARATIVEPLACEATTRIBUTE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QObject> +#include <QtQml/qqml.h> +#include <QString> + +#include <QtLocation/qplaceattribute.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceAttribute : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceAttribute attribute READ attribute WRITE setAttribute) + Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + +public: + explicit QDeclarativePlaceAttribute(QObject *parent = 0); + explicit QDeclarativePlaceAttribute(const QPlaceAttribute &src, QObject *parent = 0); + ~QDeclarativePlaceAttribute(); + + QPlaceAttribute attribute() const; + void setAttribute(const QPlaceAttribute &place); + + QString text() const; + void setText(const QString &text); + + + QString label() const; + void setLabel(const QString &label); + +Q_SIGNALS: + void labelChanged(); + void textChanged(); + +private: + QPlaceAttribute m_attribute; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePlaceAttribute) + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeplacecontentmodel.cpp b/src/location/declarativeplaces/qdeclarativeplacecontentmodel.cpp new file mode 100644 index 00000000..faf7e418 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplacecontentmodel.cpp @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** 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 "qdeclarativeplacecontentmodel_p.h" +#include "qdeclarativeplace_p.h" +#include "qdeclarativegeoserviceprovider_p.h" +#include "qdeclarativeplaceuser_p.h" +#include "error_messages.h" + +#include <QtQml/QQmlInfo> +#include <QtLocation/QGeoServiceProvider> +#include <QtLocation/QPlaceManager> +#include <QtLocation/QPlaceContentRequest> + +QT_BEGIN_NAMESPACE + +QDeclarativePlaceContentModel::QDeclarativePlaceContentModel(QPlaceContent::Type type, + QObject *parent) +: QAbstractListModel(parent), m_place(0), m_type(type), m_batchSize(1), m_contentCount(-1), + m_reply(0), m_complete(false) +{ +} + +QDeclarativePlaceContentModel::~QDeclarativePlaceContentModel() +{ +} + +/*! + \internal +*/ +QDeclarativePlace *QDeclarativePlaceContentModel::place() const +{ + return m_place; +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::setPlace(QDeclarativePlace *place) +{ + if (m_place != place) { + beginResetModel(); + + int initialCount = m_contentCount; + clearData(); + m_place = place; + endResetModel(); + + emit placeChanged(); + if (initialCount != -1) + emit totalCountChanged(); + + fetchMore(QModelIndex()); + } +} + +/*! + \internal +*/ +int QDeclarativePlaceContentModel::batchSize() const +{ + return m_batchSize; +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::setBatchSize(int batchSize) +{ + if (m_batchSize != batchSize) { + m_batchSize = batchSize; + emit batchSizeChanged(); + } +} + +/*! + \internal +*/ +int QDeclarativePlaceContentModel::totalCount() const +{ + return m_contentCount; +} + +/*! + \internal + Clears the model data but does not reset it. +*/ +void QDeclarativePlaceContentModel::clearData() +{ + qDeleteAll(m_users); + m_users.clear(); + + qDeleteAll(m_suppliers); + m_suppliers.clear(); + + m_content.clear(); + + m_contentCount = -1; + + if (m_reply) { + m_reply->abort(); + m_reply->deleteLater(); + m_reply = 0; + } + + m_nextRequest.clear(); +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::initializeCollection(int totalCount, const QPlaceContent::Collection &collection) +{ + beginResetModel(); + + int initialCount = m_contentCount; + clearData(); + + QMapIterator<int, QPlaceContent> i(collection); + while (i.hasNext()) { + i.next(); + + const QPlaceContent &content = i.value(); + if (content.type() != m_type) + continue; + + m_content.insert(i.key(), content); + if (!m_suppliers.contains(content.supplier().supplierId())) { + m_suppliers.insert(content.supplier().supplierId(), + new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this)); + } + if (!m_users.contains(content.user().userId())) { + m_users.insert(content.user().userId(), + new QDeclarativePlaceUser(content.user(), this)); + } + } + + m_contentCount = totalCount; + + if (initialCount != totalCount) + emit totalCountChanged(); + + endResetModel(); +} + +/*! + \internal +*/ +int QDeclarativePlaceContentModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return m_content.count(); +} + +/*! + \internal +*/ +QVariant QDeclarativePlaceContentModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + const QPlaceContent &content = m_content.value(index.row()); + + switch (role) { + case SupplierRole: + return QVariant::fromValue(static_cast<QObject *>(m_suppliers.value(content.supplier().supplierId()))); + case PlaceUserRole: + return QVariant::fromValue(static_cast<QObject *>(m_users.value(content.user().userId()))); + case AttributionRole: + return content.attribution(); + default: + return QVariant(); + } +} + +QHash<int, QByteArray> QDeclarativePlaceContentModel::roleNames() const +{ + QHash<int, QByteArray> roles = QAbstractListModel::roleNames(); + roles.insert(SupplierRole, "supplier"); + roles.insert(PlaceUserRole, "user"); + roles.insert(AttributionRole, "attribution"); + return roles; +} + +/*! + \internal +*/ +bool QDeclarativePlaceContentModel::canFetchMore(const QModelIndex &parent) const +{ + if (parent.isValid()) + return false; + + if (!m_place) + return false; + + if (m_contentCount == -1) + return true; + + return m_content.count() != m_contentCount; +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::fetchMore(const QModelIndex &parent) +{ + if (parent.isValid()) + return; + + if (!m_place) + return; + + if (m_reply) + return; + + if (!m_place->plugin()) + return; + + QDeclarativeGeoServiceProvider *plugin = m_place->plugin(); + + QGeoServiceProvider *serviceProvider = plugin->sharedGeoServiceProvider(); + if (!serviceProvider) + return; + + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) + return; + + if (m_nextRequest == QPlaceContentRequest()) { + QPlaceContentRequest request; + request.setContentType(m_type); + request.setPlaceId(m_place->place().placeId()); + request.setLimit(m_batchSize); + + m_reply = placeManager->getPlaceContent(request); + } else { + m_reply = placeManager->getPlaceContent(m_nextRequest); + } + + connect(m_reply, SIGNAL(finished()), this, SLOT(fetchFinished()), Qt::QueuedConnection); +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::classBegin() +{ +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::componentComplete() +{ + m_complete = true; + fetchMore(QModelIndex()); +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::fetchFinished() +{ + if (!m_reply) + return; + + QPlaceContentReply *reply = m_reply; + m_reply = 0; + + m_nextRequest = reply->nextPageRequest(); + + if (m_contentCount != reply->totalCount()) { + m_contentCount = reply->totalCount(); + emit totalCountChanged(); + } + + if (!reply->content().isEmpty()) { + QPlaceContent::Collection contents = reply->content(); + + //find out which indexes are new and which ones have changed. + QMapIterator<int, QPlaceContent> it(contents); + QList<int> changedIndexes; + QList<int> newIndexes; + while (it.hasNext()) { + it.next(); + if (!m_content.contains(it.key())) + newIndexes.append(it.key()); + else if (it.value() != m_content.value(it.key())) + changedIndexes.append(it.key()); + } + + //insert new indexes in blocks where within each + //block, the indexes are consecutive. + QListIterator<int> newIndexesIter(newIndexes); + int startIndex = -1; + while (newIndexesIter.hasNext()) { + int currentIndex = newIndexesIter.next(); + if (startIndex == -1) + startIndex = currentIndex; + + if (!newIndexesIter.hasNext() || (newIndexesIter.hasNext() && (newIndexesIter.peekNext() > (currentIndex + 1)))) { + beginInsertRows(QModelIndex(),startIndex,currentIndex); + for (int i = startIndex; i <= currentIndex; ++i) { + const QPlaceContent &content = contents.value(i); + + m_content.insert(i, content); + if (!m_suppliers.contains(content.supplier().supplierId())) { + m_suppliers.insert(content.supplier().supplierId(), + new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this)); + } + if (!m_users.contains(content.user().userId())) { + m_users.insert(content.user().userId(), + new QDeclarativePlaceUser(content.user(), this)); + } + } + endInsertRows(); + startIndex = -1; + } + } + + //modify changed indexes in blocks where within each + //block, the indexes are consecutive. + startIndex = -1; + QListIterator<int> changedIndexesIter(changedIndexes); + while (changedIndexesIter.hasNext()) { + int currentIndex = changedIndexesIter.next(); + if (startIndex == -1) + startIndex = currentIndex; + + if (!changedIndexesIter.hasNext() || (changedIndexesIter.hasNext() && changedIndexesIter.peekNext() > (currentIndex + 1))) { + for (int i = startIndex; i <= currentIndex; ++i) { + const QPlaceContent &content = contents.value(i); + m_content.insert(i, content); + if (!m_suppliers.contains(content.supplier().supplierId())) { + m_suppliers.insert(content.supplier().supplierId(), + new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this)); + } + if (!m_users.contains(content.user().userId())) { + m_users.insert(content.user().userId(), + new QDeclarativePlaceUser(content.user(), this)); + } + } + emit dataChanged(index(startIndex),index(currentIndex)); + startIndex = -1; + } + } + + // The fetch didn't add any new content and we haven't fetched all content yet. This is + // likely due to the model being prepopulated by Place::getDetails(). Keep fetching more + // data until new content is available. + if (newIndexes.isEmpty() && m_content.count() != m_contentCount) + fetchMore(QModelIndex()); + } + + reply->deleteLater(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h b/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h new file mode 100644 index 00000000..a8ed2fdb --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACECONTENTMODEL_H +#define QDECLARATIVEPLACECONTENTMODEL_H + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtCore/QAbstractListModel> +#include <QtQml/QQmlParserStatus> +#include <QtLocation/QPlaceContent> +#include <QtLocation/QPlaceContentReply> + +QT_BEGIN_NAMESPACE + +class QDeclarativePlace; +class QDeclarativeGeoServiceProvider; +class QGeoServiceProvider; +class QDeclarativeSupplier; +class QDeclarativePlaceUser; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceContentModel : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativePlace *place READ place WRITE setPlace NOTIFY placeChanged) + Q_PROPERTY(int batchSize READ batchSize WRITE setBatchSize NOTIFY batchSizeChanged) + Q_PROPERTY(int totalCount READ totalCount NOTIFY totalCountChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativePlaceContentModel(QPlaceContent::Type type, QObject *parent = 0); + ~QDeclarativePlaceContentModel(); + + QDeclarativePlace *place() const; + void setPlace(QDeclarativePlace *place); + + int batchSize() const; + void setBatchSize(int batchSize); + + int totalCount() const; + + void clearData(); + + void initializeCollection(int totalCount, const QPlaceContent::Collection &collection); + + // from QAbstractListModel + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QHash<int, QByteArray> roleNames() const; + + enum Roles { + SupplierRole = Qt::UserRole, + PlaceUserRole, + AttributionRole, + UserRole //indicator for next conten type specific role + }; + + bool canFetchMore(const QModelIndex &parent) const; + void fetchMore(const QModelIndex &parent); + + // from QQmlParserStatus + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void placeChanged(); + void batchSizeChanged(); + void totalCountChanged(); + +private Q_SLOTS: + void fetchFinished(); + +protected: + QPlaceContent::Collection m_content; + QMap<QString, QDeclarativeSupplier *> m_suppliers; + QMap<QString, QDeclarativePlaceUser *>m_users; + +private: + QDeclarativePlace *m_place; + QPlaceContent::Type m_type; + int m_batchSize; + int m_contentCount; + + QPlaceContentReply *m_reply; + QPlaceContentRequest m_nextRequest; + + bool m_complete; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPLACECONTENTMODEL_H diff --git a/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel.cpp b/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel.cpp new file mode 100644 index 00000000..dbc23737 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** 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 "qdeclarativeplaceeditorialmodel_p.h" + +#include <QtCore/QUrl> +#include <QtLocation/QPlaceEditorial> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype EditorialModel + \instantiates QDeclarativePlaceEditorialModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since Qt Location 5.5 + + \brief The EditorialModel type provides a model of place editorials. + + The EditorialModel is a read-only model used to fetch editorials related to a \l Place. + Binding a \l Place via \l EditorialModel::place initiates an initial fetch of editorials. + The model performs fetches incrementally and is intended to be used in conjunction + with a View such as a \l ListView. When the View reaches the last of the editorials + currently in the model, a fetch is performed to retrieve more if they are available. + The View is automatically updated as the editorials are received. The number of + editorials which are fetched at a time is specified by the \l batchSize property. + The total number of editorials available can be accessed via the \l totalCount property. + + The model returns data for the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li text + \li string + \li The editorial's textual description of the place. It can be either rich (HTML based) text or plain text + depending upon the provider. + \row + \li title + \li string + \li The title of the editorial. + \row + \li language + \li string + \li The language that the editorial is written in. + \row + \li supplier + \li \l Supplier + \li The supplier of the editorial. + \row + \li user + \li \l {QtLocation::User}{User} + \li The user who contributed the editorial. + \row + \li attribution + \li string + \li Attribution text which must be displayed when displaying the editorial. + \endtable + + \section1 Example + + The following example shows how to display editorials for a place: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml EditorialModel + +*/ + +/*! + \qmlproperty Place EditorialModel::place + + This property holds the Place that the editorials are for. +*/ + +/*! + \qmlproperty int EditorialModel::batchSize + + This property holds the batch size to use when fetching more editorials items. +*/ + +/*! + \qmlproperty int EditorialModel::totalCount + + This property holds the total number of editorial items for the place. +*/ + +QDeclarativePlaceEditorialModel::QDeclarativePlaceEditorialModel(QObject *parent) +: QDeclarativePlaceContentModel(QPlaceContent::EditorialType, parent) +{ +} + +QDeclarativePlaceEditorialModel::~QDeclarativePlaceEditorialModel() +{ +} + +/*! + \internal +*/ +QVariant QDeclarativePlaceEditorialModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + const QPlaceEditorial &description = m_content.value(index.row()); + + switch (role) { + case TextRole: + return description.text(); + case TitleRole: + return description.title(); + case LanguageRole: + return description.language(); + } + + return QDeclarativePlaceContentModel::data(index, role); +} + +QHash<int, QByteArray> QDeclarativePlaceEditorialModel::roleNames() const +{ + QHash<int, QByteArray> roleNames = QDeclarativePlaceContentModel::roleNames(); + roleNames.insert(TextRole, "text"); + roleNames.insert(TitleRole, "title"); + roleNames.insert(LanguageRole, "language"); + return roleNames; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel_p.h b/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel_p.h new file mode 100644 index 00000000..f574677a --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel_p.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEEDITORIALMODEL_H +#define QDECLARATIVEPLACEEDITORIALMODEL_H + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativeplacecontentmodel_p.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceEditorialModel : public QDeclarativePlaceContentModel +{ + Q_OBJECT + +public: + explicit QDeclarativePlaceEditorialModel(QObject *parent = 0); + ~QDeclarativePlaceEditorialModel(); + + QVariant data(const QModelIndex &index, int role) const; + QHash<int, QByteArray> roleNames() const; + + enum Roles { + TextRole = UserRole, + TitleRole, + LanguageRole + }; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPLACEEDITORIALMODEL_H diff --git a/src/location/declarativeplaces/qdeclarativeplaceicon.cpp b/src/location/declarativeplaces/qdeclarativeplaceicon.cpp new file mode 100644 index 00000000..24891138 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceicon.cpp @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** 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 "qdeclarativeplaceicon_p.h" +#include "error_messages.h" + +#include <QtLocation/QGeoServiceProvider> +#include <QtLocation/QPlaceManager> +#include <QtQml/QQmlInfo> +#include <QCoreApplication> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Icon + \instantiates QDeclarativePlaceIcon + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief The Icon type represents an icon image source which can have multiple sizes. + + The Icon type can be used in conjunction with an \l Image type to display an icon. + The \l url() function is used to construct an icon URL of a requested size, + the icon which most closely matches the requested size is returned. + + The Icon type also has a parameters map which is a set of key value pairs. The precise + keys to use depend on the + \l {Qt Location#Plugin References and Parameters}{plugin} being used. + The parameters map is used by the \l Plugin to determine which URL to return. + + In the case where an icon can only possibly have one image URL, the + parameter key of \c "singleUrl" can be used with a QUrl value. Any Icon with this + parameter will always return the specified URL regardless of the requested icon + size and not defer to any Plugin. + + The following code shows how to display a 64x64 pixel icon: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml Icon + + Alternatively, a default sized icon can be specified like so: + \snippet declarative/places.qml Icon default +*/ + +QDeclarativePlaceIcon::QDeclarativePlaceIcon(QObject *parent) +: QObject(parent), m_plugin(0), m_parameters(new QQmlPropertyMap(this)) +{ +} + +QDeclarativePlaceIcon::QDeclarativePlaceIcon(const QPlaceIcon &icon, QDeclarativeGeoServiceProvider *plugin, QObject *parent) +: QObject(parent), m_parameters(new QQmlPropertyMap(this)) +{ + if (icon.isEmpty()) + m_plugin = 0; + else + m_plugin = plugin; + + initParameters(icon.parameters()); +} + +QDeclarativePlaceIcon::~QDeclarativePlaceIcon() +{ +} + +/*! + \qmlproperty QPlaceIcon Icon::icon + + For details on how to use this property to interface between C++ and QML see + "\l {Icon - QPlaceIcon} {Interfaces between C++ and QML Code}". +*/ +QPlaceIcon QDeclarativePlaceIcon::icon() const +{ + QPlaceIcon result; + + if (m_plugin) + result.setManager(manager()); + else + result.setManager(0); + + QVariantMap params; + foreach (const QString &key, m_parameters->keys()) { + const QVariant value = m_parameters->value(key); + if (value.isValid()) { + params.insert(key, value); + } + } + + result.setParameters(params); + + return result; +} + +void QDeclarativePlaceIcon::setIcon(const QPlaceIcon &src) +{ + initParameters(src.parameters()); +} + +/*! + \qmlmethod url Icon::url(size size) + + Returns a URL for the icon image that most closely matches the given \a size. + + If no plugin has been assigned to the icon, and the parameters do not contain the 'singleUrl' key, a default constructed URL + is returned. + +*/ +QUrl QDeclarativePlaceIcon::url(const QSize &size) const +{ + return icon().url(size); +} + +/*! + \qmlproperty Object Icon::parameters + + This property holds the parameters of the icon and is a map. These parameters + are used by the plugin to return the appropriate URL when url() is called and to + specify locations to save to when saving icons. + + Consult the \l {Qt Location#Plugin References and Parameters}{plugin documentation} + for what parameters are supported and how they should be used. + + Note, due to limitations of the QQmlPropertyMap, it is not possible + to declaratively specify the parameters in QML, assignment of parameters keys + and values can only be accomplished by JavaScript. + +*/ +QQmlPropertyMap *QDeclarativePlaceIcon::parameters() const +{ + return m_parameters; +} + +/*! + \qmlproperty Plugin Icon::plugin + + The property holds the plugin that is responsible for managing this icon. +*/ +QDeclarativeGeoServiceProvider *QDeclarativePlaceIcon::plugin() const +{ + return m_plugin; +} + +void QDeclarativePlaceIcon::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + m_plugin = plugin; + emit pluginChanged(); + + if (!m_plugin) + return; + + if (m_plugin->isAttached()) { + pluginReady(); + } else { + connect(m_plugin, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +/*! + \internal +*/ +void QDeclarativePlaceIcon::pluginReady() +{ + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) { + qmlWarning(this) << QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString()); + return; + } +} + +/*! + \internal + Helper function to return the manager from the plugin +*/ +QPlaceManager *QDeclarativePlaceIcon::manager() const +{ + if (!m_plugin) { + qmlWarning(this) << QStringLiteral("Plugin is not assigned to place."); + return 0; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider) + return 0; + + QPlaceManager *placeManager = serviceProvider->placeManager(); + + if (!placeManager) + return 0; + + return placeManager; +} + +/*! + \internal +*/ +void QDeclarativePlaceIcon::initParameters(const QVariantMap ¶meterMap) +{ + //clear out old parameters + foreach (const QString &key, m_parameters->keys()) + m_parameters->clear(key); + + foreach (const QString &key, parameterMap.keys()) { + QVariant value = parameterMap.value(key); + m_parameters->insert(key, value); + } +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceicon_p.h b/src/location/declarativeplaces/qdeclarativeplaceicon_p.h new file mode 100644 index 00000000..535d98eb --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceicon_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEICON_P_H +#define QDECLARATIVEPLACEICON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeoserviceprovider_p.h> + +#include <QtLocation/qplaceicon.h> +#include <QtQml/qqml.h> +#include <QtQml/QQmlPropertyMap> + +#include <QObject> + +QT_BEGIN_NAMESPACE + +class QQmlPropertyMap; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceIcon : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceIcon icon READ icon WRITE setIcon) + Q_PROPERTY(QObject *parameters READ parameters NOTIFY parametersChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + +public: + explicit QDeclarativePlaceIcon(QObject *parent = 0); + QDeclarativePlaceIcon(const QPlaceIcon &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent = 0); + ~QDeclarativePlaceIcon(); + + QPlaceIcon icon() const; + void setIcon(const QPlaceIcon &src); + + Q_INVOKABLE QUrl url(const QSize &size = QSize()) const; + + QQmlPropertyMap *parameters() const; + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + +Q_SIGNALS: + void pluginChanged(); + void parametersChanged(); //in practice is never emitted since parameters cannot be re-assigned + //the declaration is needed to avoid warnings about non-notifyable properties + +private Q_SLOTS: + void pluginReady(); + +private: + QPlaceManager *manager() const; + void initParameters(const QVariantMap ¶meterMap); + QDeclarativeGeoServiceProvider *m_plugin; + QQmlPropertyMap *m_parameters; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeplaceimagemodel.cpp b/src/location/declarativeplaces/qdeclarativeplaceimagemodel.cpp new file mode 100644 index 00000000..4da37081 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceimagemodel.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** 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 "qdeclarativeplaceimagemodel_p.h" +#include "qdeclarativesupplier_p.h" + +#include <QtCore/QUrl> +#include <QtLocation/QPlaceImage> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ImageModel + \instantiates QDeclarativePlaceImageModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since Qt Location 5.5 + + \brief The ImageModel type provides a model of place images. + + The ImageModel is a read-only model used to fetch images related to a \l Place. + Binding a \l Place via \l ImageModel::place initiates an initial fetch of images. + The model performs fetches incrementally and is intended to be used in conjunction + with a View such as a \l ListView. When the View reaches the last of the images + currently in the model, a fetch is performed to retrieve more if they are available. + The View is automatically updated as the images are received. The number of images + which are fetched at a time is specified by the \l batchSize property. The total number + of images available can be accessed via the \l totalCount property. + + The model returns data for the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li url + \li url + \li The URL of the image. + \row + \li imageId + \li string + \li The identifier of the image. + \row + \li mimeType + \li string + \li The MIME type of the image. + \row + \li supplier + \li \l Supplier + \li The supplier of the image. + \row + \li user + \li \l {QtLocation::User}{User} + \li The user who contributed the image. + \row + \li attribution + \li string + \li Attribution text which must be displayed when displaying the image. + \endtable + + + \section1 Example + + The following example shows how to display images for a place: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ImageModel +*/ + +/*! + \qmlproperty Place ImageModel::place + + This property holds the Place that the images are for. +*/ + +/*! + \qmlproperty int ImageModel::batchSize + + This property holds the batch size to use when fetching more image items. +*/ + +/*! + \qmlproperty int ImageModel::totalCount + + This property holds the total number of image items for the place. +*/ + +QDeclarativePlaceImageModel::QDeclarativePlaceImageModel(QObject *parent) +: QDeclarativePlaceContentModel(QPlaceContent::ImageType, parent) +{ +} + +QDeclarativePlaceImageModel::~QDeclarativePlaceImageModel() +{ + qDeleteAll(m_suppliers); +} + +/*! + \internal +*/ +QVariant QDeclarativePlaceImageModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + const QPlaceImage &image = m_content.value(index.row()); + + switch (role) { + case UrlRole: + return image.url(); + case ImageIdRole: + return image.imageId(); + case MimeTypeRole: + return image.mimeType(); + } + + return QDeclarativePlaceContentModel::data(index, role); +} + +QHash<int, QByteArray> QDeclarativePlaceImageModel::roleNames() const +{ + QHash<int, QByteArray> roles = QDeclarativePlaceContentModel::roleNames(); + roles.insert(UrlRole, "url"); + roles.insert(ImageIdRole, "imageId"); + roles.insert(MimeTypeRole, "mimeType"); + return roles; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceimagemodel_p.h b/src/location/declarativeplaces/qdeclarativeplaceimagemodel_p.h new file mode 100644 index 00000000..2c244219 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceimagemodel_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEIMAGEMODEL_P_H +#define QDECLARATIVEPLACEIMAGEMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativeplacecontentmodel_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeSupplier; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceImageModel : public QDeclarativePlaceContentModel +{ + Q_OBJECT + +public: + explicit QDeclarativePlaceImageModel(QObject *parent = 0); + ~QDeclarativePlaceImageModel(); + + QVariant data(const QModelIndex &index, int role) const; + QHash<int, QByteArray> roleNames() const; + + enum Roles { + UrlRole = UserRole, + ImageIdRole, + MimeTypeRole + }; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeplaceuser.cpp b/src/location/declarativeplaces/qdeclarativeplaceuser.cpp new file mode 100644 index 00000000..86901a98 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceuser.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** 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 "qdeclarativeplaceuser_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype User + \instantiates QDeclarativePlaceUser + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief The User type identifies a user who contributed a particular \l Place content item. + + Each \l Place content item has an associated user who contributed the content. This type + provides information about that user. + + \sa ImageModel, ReviewModel, EditorialModel + + \section1 Example + + The following example shows how to display information about the user who + submitted an editorial: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml EditorialModel +*/ + +QDeclarativePlaceUser::QDeclarativePlaceUser(QObject *parent) + : QObject(parent) {} + +QDeclarativePlaceUser::QDeclarativePlaceUser(const QPlaceUser &user, + QObject *parent) + : QObject(parent), + m_user(user) {} + +QDeclarativePlaceUser::~QDeclarativePlaceUser() {} + +/*! + \qmlproperty QPlaceUser QtLocation::User::user + + For details on how to use this property to interface between C++ and QML see + "\l {User - QPlaceUser} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativePlaceUser::setUser(const QPlaceUser &user) +{ + QPlaceUser previousUser = m_user; + m_user = user; + + if (m_user.userId() != previousUser.userId()) + emit userIdChanged(); + + if (m_user.name() != previousUser.name()) + emit nameChanged(); +} + +QPlaceUser QDeclarativePlaceUser::user() const +{ + return m_user; +} + +/*! + \qmlproperty string QtLocation::User::userId + + This property holds the unique identifier of the user. +*/ + +void QDeclarativePlaceUser::setUserId(const QString &id) +{ + if (m_user.userId() == id) + return; + + m_user.setUserId(id); + emit userIdChanged(); +} + +QString QDeclarativePlaceUser::userId() const +{ + return m_user.userId(); +} + +/*! + \qmlproperty string QtLocation::User::name + + This property holds the name of a user. +*/ +void QDeclarativePlaceUser::setName(const QString &name) +{ + if (m_user.name() == name) + return; + + m_user.setName(name); + emit nameChanged(); +} + +QString QDeclarativePlaceUser::name() const +{ + return m_user.name(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceuser_p.h b/src/location/declarativeplaces/qdeclarativeplaceuser_p.h new file mode 100644 index 00000000..8cd64493 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceuser_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEUSER_P_H +#define QDECLARATIVEPLACEUSER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtCore/QObject> +#include <QtQml/qqml.h> +#include <QtLocation/QPlaceUser> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceUser : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceUser user READ user WRITE setUser) + Q_PROPERTY(QString userId READ userId WRITE setUserId NOTIFY userIdChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + +public: + explicit QDeclarativePlaceUser(QObject *parent = 0); + explicit QDeclarativePlaceUser(const QPlaceUser &src, QObject *parent = 0); + ~QDeclarativePlaceUser(); + + QPlaceUser user() const; + void setUser(const QPlaceUser &src); + + QString userId() const; + void setUserId(const QString &id); + + QString name() const; + void setName(const QString &name); + +Q_SIGNALS: + void userIdChanged(); + void nameChanged(); + +private: + QPlaceUser m_user; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePlaceUser) + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeratings.cpp b/src/location/declarativeplaces/qdeclarativeratings.cpp new file mode 100644 index 00000000..150e5966 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeratings.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** 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 "qdeclarativeratings_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Ratings + \instantiates QDeclarativeRatings + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief The Ratings type holds place rating information. + + Rating information is used to describe how \e good a place is conceived to be. Typically this + information is visualized as a number of stars. The \l average property gives an aggregated + ratings value out of a possible maximum as given by the \l maximum property. + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml Ratings +*/ + +QDeclarativeRatings::QDeclarativeRatings(QObject *parent) + : QObject(parent) {} + +QDeclarativeRatings::QDeclarativeRatings(const QPlaceRatings &rating, + QObject *parent) + : QObject(parent), + m_ratings(rating) {} + +QDeclarativeRatings::~QDeclarativeRatings() {} + +/*! + \qmlproperty QPlaceRatings Ratings::ratings + + For details on how to use this property to interface between C++ and QML see + "\l {Ratings - QPlaceRatings} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativeRatings::setRatings(const QPlaceRatings &ratings) +{ + QPlaceRatings previous = m_ratings; + m_ratings = ratings; + + if (ratings.average() != previous.average()) { + emit averageChanged(); + } + if (ratings.count() != previous.count()) { + emit countChanged(); + } +} + +QPlaceRatings QDeclarativeRatings::ratings() const +{ + return m_ratings; +} + +/*! + \qmlproperty real Ratings::average + + This property holds the average of the individual ratings. + + \sa maximum +*/ +void QDeclarativeRatings::setAverage(qreal average) +{ + if (m_ratings.average() != average) { + m_ratings.setAverage(average); + emit averageChanged(); + } +} + +qreal QDeclarativeRatings::average() const +{ + return m_ratings.average(); +} + +/*! + \qmlproperty real Ratings::maximum + + This property holds the maximum rating value. +*/ +void QDeclarativeRatings::setMaximum(qreal max) +{ + if (m_ratings.maximum() == max) + return; + + m_ratings.setMaximum(max); + emit maximumChanged(); +} + +qreal QDeclarativeRatings::maximum() const +{ + return m_ratings.maximum(); +} + +/*! + \qmlproperty int Ratings::count + + This property holds the total number of individual user ratings + used in determining the overall ratings \l average. +*/ +void QDeclarativeRatings::setCount(int count) +{ + if (m_ratings.count() != count) { + m_ratings.setCount(count); + emit countChanged(); + } +} + +int QDeclarativeRatings::count() const +{ + return m_ratings.count(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeratings_p.h b/src/location/declarativeplaces/qdeclarativeratings_p.h new file mode 100644 index 00000000..5ee530dc --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeratings_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERATINGS_P_H +#define QDECLARATIVERATINGS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/qplaceratings.h> +#include <QtQml/qqml.h> + +#include <QObject> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRatings : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceRatings ratings READ ratings WRITE setRatings) + Q_PROPERTY(qreal average READ average WRITE setAverage NOTIFY averageChanged) + Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged) + Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged) + +public: + explicit QDeclarativeRatings(QObject *parent = 0); + explicit QDeclarativeRatings(const QPlaceRatings &src, QObject *parent = 0); + ~QDeclarativeRatings(); + + QPlaceRatings ratings() const; + void setRatings(const QPlaceRatings &src); + + qreal average() const; + void setAverage(qreal average); + + qreal maximum() const; + void setMaximum(qreal max); + + int count() const; + void setCount(int count); + +Q_SIGNALS: + void averageChanged(); + void maximumChanged(); + void countChanged(); + +private: + QPlaceRatings m_ratings; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeRatings) + +#endif // QDECLARATIVERATING_P_H diff --git a/src/location/declarativeplaces/qdeclarativereviewmodel.cpp b/src/location/declarativeplaces/qdeclarativereviewmodel.cpp new file mode 100644 index 00000000..b7237bc9 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativereviewmodel.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** 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 "qdeclarativereviewmodel_p.h" +#include "qdeclarativesupplier_p.h" + +#include <QtCore/QDateTime> +#include <QtLocation/QPlaceReview> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ReviewModel + \instantiates QDeclarativeReviewModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since Qt Location 5.5 + + \brief Provides access to reviews of a \l Place. + + The ReviewModel is a read-only model used to fetch reviews about a \l Place. The model + incrementally fetches. The number of reviews which are fetched at a time is specified + by the \l batchSize property. The total number of reviews available can be accessed via the + \l totalCount property. + + To use the ReviewModel we need a view and a delegate. In this snippet we + see the setting up of a ListView with a ReviewModel model and a delegate. + + \snippet places/views/ReviewView.qml ReviewModel delegate + + The model returns data for the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li dateTime + \li datetime + \li The date and time that the review was posted. + \row + \li text + \li string + \li The review's textual description of the place. It can be either rich (HTML based) text or plain text + depending on the provider. + \row + \li language + \li string + \li The language that the review is written in. + \row + \li rating + \li real + \li The rating that the reviewer gave to the place. + \row + \li reviewId + \li string + \li The identifier of the review. + \row + \li title + \li string + \li The title of the review. + \row + \li supplier + \li \l Supplier + \li The supplier of the review. + \row + \li user + \li \l {QtLocation::User}{User} + \li The user who contributed the review. + \row + \li attribution + \li string + \li Attribution text which must be displayed when displaying the review. + \endtable +*/ + +/*! + \qmlproperty Place QtLocation::ReviewModel::place + + This property holds the Place that the reviews are for. +*/ + +/*! + \qmlproperty int QtLocation::ReviewModel::batchSize + + This property holds the batch size to use when fetching more reviews. +*/ + +/*! + \qmlproperty int QtLocation::ReviewModel::totalCount + + This property holds the total number of reviews for the place. +*/ + +QDeclarativeReviewModel::QDeclarativeReviewModel(QObject *parent) +: QDeclarativePlaceContentModel(QPlaceContent::ReviewType, parent) +{ +} + +QDeclarativeReviewModel::~QDeclarativeReviewModel() +{ + qDeleteAll(m_suppliers); +} + +/*! + \internal +*/ +QVariant QDeclarativeReviewModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + const QPlaceReview &review = m_content.value(index.row()); + + switch (role) { + case DateTimeRole: + return review.dateTime(); + case TextRole: + return review.text(); + case LanguageRole: + return review.language(); + case RatingRole: + return review.rating(); + case ReviewIdRole: + return review.reviewId(); + case TitleRole: + return review.title(); + } + + return QDeclarativePlaceContentModel::data(index, role); +} + +QHash<int, QByteArray> QDeclarativeReviewModel::roleNames() const +{ + QHash<int, QByteArray> roles = QDeclarativePlaceContentModel::roleNames(); + roles.insert(DateTimeRole, "dateTime"); + roles.insert(TextRole, "text"); + roles.insert(LanguageRole, "language"); + roles.insert(RatingRole, "rating"); + roles.insert(ReviewIdRole, "reviewId"); + roles.insert(TitleRole, "title"); + return roles; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativereviewmodel_p.h b/src/location/declarativeplaces/qdeclarativereviewmodel_p.h new file mode 100644 index 00000000..e6d2bd95 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativereviewmodel_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEREVIEWMODEL_P_H +#define QDECLARATIVEREVIEWMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativeplacecontentmodel_p.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeReviewModel : public QDeclarativePlaceContentModel +{ + Q_OBJECT + +public: + explicit QDeclarativeReviewModel(QObject *parent = 0); + ~QDeclarativeReviewModel(); + + QVariant data(const QModelIndex &index, int role) const; + QHash<int, QByteArray> roleNames() const; + enum Roles { + DateTimeRole = UserRole, + TextRole, + LanguageRole, + RatingRole, + ReviewIdRole, + TitleRole + }; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEREVIEWMODEL_P_H diff --git a/src/location/declarativeplaces/qdeclarativesearchmodelbase.cpp b/src/location/declarativeplaces/qdeclarativesearchmodelbase.cpp new file mode 100644 index 00000000..3a3faa56 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchmodelbase.cpp @@ -0,0 +1,362 @@ +/**************************************************************************** +** +** 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 "qdeclarativesearchmodelbase_p.h" +#include "qdeclarativeplace_p.h" +#include "error_messages.h" + +#include <QtCore/QCoreApplication> +#include <QtQml/QQmlInfo> +#include <QtLocation/QGeoServiceProvider> +#include <QtLocation/QPlaceManager> +#include <QtLocation/QPlaceSearchRequest> +#include <QtLocation/QPlaceSearchReply> +#include <QtPositioning/QGeoCircle> + +QT_BEGIN_NAMESPACE + +QDeclarativeSearchModelBase::QDeclarativeSearchModelBase(QObject *parent) +: QAbstractListModel(parent), m_plugin(0), m_reply(0), m_complete(false), m_status(Null) +{ +} + +QDeclarativeSearchModelBase::~QDeclarativeSearchModelBase() +{ +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider *QDeclarativeSearchModelBase::plugin() const +{ + return m_plugin; +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + initializePlugin(plugin); + + if (m_complete) + emit pluginChanged(); +} + +/*! + \internal +*/ +QVariant QDeclarativeSearchModelBase::searchArea() const +{ + QGeoShape s = m_request.searchArea(); + if (s.type() == QGeoShape::RectangleType) + return QVariant::fromValue(QGeoRectangle(s)); + else if (s.type() == QGeoShape::CircleType) + return QVariant::fromValue(QGeoCircle(s)); + else + return QVariant::fromValue(s); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setSearchArea(const QVariant &searchArea) +{ + QGeoShape s; + + if (searchArea.userType() == qMetaTypeId<QGeoRectangle>()) + s = searchArea.value<QGeoRectangle>(); + else if (searchArea.userType() == qMetaTypeId<QGeoCircle>()) + s = searchArea.value<QGeoCircle>(); + else if (searchArea.userType() == qMetaTypeId<QGeoShape>()) + s = searchArea.value<QGeoShape>(); + + if (m_request.searchArea() == s) + return; + + m_request.setSearchArea(s); + emit searchAreaChanged(); +} + +/*! + \internal +*/ +int QDeclarativeSearchModelBase::limit() const +{ + return m_request.limit(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setLimit(int limit) +{ + if (m_request.limit() == limit) + return; + + m_request.setLimit(limit); + emit limitChanged(); +} + +/*! + \internal +*/ +bool QDeclarativeSearchModelBase::previousPagesAvailable() const +{ + return m_previousPageRequest != QPlaceSearchRequest(); +} + +/*! + \internal +*/ +bool QDeclarativeSearchModelBase::nextPagesAvailable() const +{ + return m_nextPageRequest != QPlaceSearchRequest(); +} + +/*! + \internal +*/ +QDeclarativeSearchModelBase::Status QDeclarativeSearchModelBase::status() const +{ + return m_status; +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setStatus(Status status, const QString &errorString) +{ + Status prevStatus = m_status; + + m_status = status; + m_errorString = errorString; + + if (prevStatus != m_status) + emit statusChanged(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::update() +{ + if (m_reply) + return; + + setStatus(Loading); + + if (!m_plugin) { + clearData(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROPERTY_NOT_SET)); + return; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider) { + clearData(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROVIDER_ERROR) + .arg(m_plugin->name())); + return; + } + + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) { + clearData(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return; + } + + m_reply = sendQuery(placeManager, m_request); + if (!m_reply) { + clearData(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, UNABLE_TO_MAKE_REQUEST)); + return; + } + + m_reply->setParent(this); + connect(m_reply, SIGNAL(finished()), this, SLOT(queryFinished())); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::cancel() +{ + if (!m_reply) + return; + + if (!m_reply->isFinished()) + m_reply->abort(); + + if (m_reply) { + m_reply->deleteLater(); + m_reply = 0; + } + + setStatus(Ready); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::reset() +{ + beginResetModel(); + clearData(); + setStatus(Null); + endResetModel(); +} + +/*! + \internal +*/ +QString QDeclarativeSearchModelBase::errorString() const +{ + return m_errorString; +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::previousPage() +{ + if (m_previousPageRequest == QPlaceSearchRequest()) + return; + + m_request = m_previousPageRequest; + update(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::nextPage() +{ + if (m_nextPageRequest == QPlaceSearchRequest()) + return; + + m_request = m_nextPageRequest; + update(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::clearData(bool suppressSignal) +{ + Q_UNUSED(suppressSignal) +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::classBegin() +{ +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::componentComplete() +{ + m_complete = true; +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::initializePlugin(QDeclarativeGeoServiceProvider *plugin) +{ + beginResetModel(); + if (plugin != m_plugin) { + if (m_plugin) + disconnect(m_plugin, SIGNAL(nameChanged(QString)), this, SLOT(pluginNameChanged())); + if (plugin) + connect(plugin, SIGNAL(nameChanged(QString)), this, SLOT(pluginNameChanged())); + m_plugin = plugin; + } + + if (m_plugin) { + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (serviceProvider) { + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (placeManager) { + if (placeManager->childCategoryIds().isEmpty()) { + QPlaceReply *reply = placeManager->initializeCategories(); + connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); + } + } + } + } + + endResetModel(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::pluginNameChanged() +{ + initializePlugin(m_plugin); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setPreviousPageRequest(const QPlaceSearchRequest &previous) +{ + if (m_previousPageRequest == previous) + return; + + m_previousPageRequest = previous; + emit previousPagesAvailableChanged(); +} + +void QDeclarativeSearchModelBase::setNextPageRequest(const QPlaceSearchRequest &next) +{ + if (m_nextPageRequest == next) + return; + + m_nextPageRequest = next; + emit nextPagesAvailableChanged(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativesearchmodelbase_p.h b/src/location/declarativeplaces/qdeclarativesearchmodelbase_p.h new file mode 100644 index 00000000..cb8e4032 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchmodelbase_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESEARCHMODELBASE_H +#define QDECLARATIVESEARCHMODELBASE_H + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeoserviceprovider_p.h> +#include <QtCore/QAbstractListModel> +#include <QtQml/QQmlParserStatus> +#include <QtLocation/QPlaceSearchRequest> +#include <QtLocation/QPlaceSearchResult> +#include <QtLocation/QPlaceReply> + +QT_BEGIN_NAMESPACE + +class QPlaceManager; +class QPlaceSearchRequest; +class QPlaceSearchReply; +class QDeclarativePlace; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSearchModelBase : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(QVariant searchArea READ searchArea WRITE setSearchArea NOTIFY searchAreaChanged) + Q_PROPERTY(int limit READ limit WRITE setLimit NOTIFY limitChanged) + Q_PROPERTY(bool previousPagesAvailable READ previousPagesAvailable NOTIFY previousPagesAvailableChanged) + Q_PROPERTY(bool nextPagesAvailable READ nextPagesAvailable NOTIFY nextPagesAvailableChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + + Q_ENUMS(Status) + + Q_INTERFACES(QQmlParserStatus) + +public: + enum Status { + Null, + Ready, + Loading, + Error + }; + + explicit QDeclarativeSearchModelBase(QObject *parent = 0); + ~QDeclarativeSearchModelBase(); + + QDeclarativeGeoServiceProvider *plugin() const; + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + + QVariant searchArea() const; + void setSearchArea(const QVariant &searchArea); + + int limit() const; + void setLimit(int limit); + + bool previousPagesAvailable() const; + bool nextPagesAvailable() const; + + Status status() const; + void setStatus(Status status, const QString &errorString = QString()); + + Q_INVOKABLE void update(); + + Q_INVOKABLE void cancel(); + Q_INVOKABLE void reset(); + + Q_INVOKABLE QString errorString() const; + + Q_INVOKABLE void previousPage(); + Q_INVOKABLE void nextPage(); + + virtual void clearData(bool suppressSignal = false); + + // From QQmlParserStatus + virtual void classBegin(); + virtual void componentComplete(); + +Q_SIGNALS: + void pluginChanged(); + void searchAreaChanged(); + void limitChanged(); + void previousPagesAvailableChanged(); + void nextPagesAvailableChanged(); + void statusChanged(); + +protected: + virtual void initializePlugin(QDeclarativeGeoServiceProvider *plugin); + +protected Q_SLOTS: + virtual void queryFinished() = 0; + +private Q_SLOTS: + void pluginNameChanged(); + +protected: + virtual QPlaceReply *sendQuery(QPlaceManager *manager, const QPlaceSearchRequest &request) = 0; + void setPreviousPageRequest(const QPlaceSearchRequest &previous); + void setNextPageRequest(const QPlaceSearchRequest &next); + + QPlaceSearchRequest m_request; + QDeclarativeGeoServiceProvider *m_plugin; + QPlaceReply *m_reply; + +private: + bool m_complete; + Status m_status; + QString m_errorString; + QPlaceSearchRequest m_previousPageRequest; + QPlaceSearchRequest m_nextPageRequest; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESEARCHMODELBASE_H diff --git a/src/location/declarativeplaces/qdeclarativesearchresultmodel.cpp b/src/location/declarativeplaces/qdeclarativesearchresultmodel.cpp new file mode 100644 index 00000000..31d152db --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchresultmodel.cpp @@ -0,0 +1,917 @@ +/**************************************************************************** +** +** 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> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype PlaceSearchModel + \instantiates QDeclarativeSearchResultModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since Qt Location 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 +*/ + +/*! + \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()))); + 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; + if (reply->error() != QPlaceReply::NoError) { + m_resultsBuffer.clear(); + updateLayout(); + setStatus(Error, reply->errorString()); + reply->deleteLater(); + return; + } + + if (reply->type() == QPlaceReply::SearchReply) { + QPlaceSearchReply *searchReply = qobject_cast<QPlaceSearchReply *>(reply); + Q_ASSERT(searchReply); + + m_resultsBuffer = searchReply->results(); + setPreviousPageRequest(searchReply->previousPageRequest()); + setNextPageRequest(searchReply->nextPageRequest()); + + reply->deleteLater(); + + 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) { + reply->deleteLater(); + 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); + m_reply = favoritesManager->matchingPlaces(request); + connect(m_reply, SIGNAL(finished()), this, SLOT(queryFinished())); + } + } else if (reply->type() == QPlaceReply::MatchReply) { + QPlaceMatchReply *matchReply = qobject_cast<QPlaceMatchReply *>(reply); + Q_ASSERT(matchReply); + updateLayout(matchReply->places()); + setStatus(Ready); + reply->deleteLater(); + } else { + setStatus(Error, QStringLiteral("Unknown reply type")); + reply->deleteLater(); + } +} + +/*! + \qmlmethod void 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) +{ + int oldRowCount = rowCount(); + + beginResetModel(); + clearData(true); + m_results = m_resultsBuffer; + m_resultsBuffer.clear(); + + for (int i = 0; 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); + } + + 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); + endRemoveRows(); + + emit rowCountChanged(); +} + +/*! + \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 diff --git a/src/location/declarativeplaces/qdeclarativesearchresultmodel_p.h b/src/location/declarativeplaces/qdeclarativesearchresultmodel_p.h new file mode 100644 index 00000000..77526fd0 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchresultmodel_p.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESEARCHRESULTMODEL_P_H +#define QDECLARATIVESEARCHRESULTMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativesearchmodelbase_p.h> +#include <QtLocation/private/qdeclarativecategory_p.h> +#include <QtLocation/private/qdeclarativeplace_p.h> +#include <QtLocation/private/qdeclarativeplaceicon_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoServiceProvider; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSearchResultModel : public QDeclarativeSearchModelBase +{ + Q_OBJECT + + Q_PROPERTY(QString searchTerm READ searchTerm WRITE setSearchTerm NOTIFY searchTermChanged) + Q_PROPERTY(QQmlListProperty<QDeclarativeCategory> categories READ categories NOTIFY categoriesChanged) + Q_PROPERTY(QString recommendationId READ recommendationId WRITE setRecommendationId NOTIFY recommendationIdChanged) + Q_PROPERTY(RelevanceHint relevanceHint READ relevanceHint WRITE setRelevanceHint NOTIFY relevanceHintChanged) + Q_PROPERTY(QDeclarativePlace::Visibility visibilityScope READ visibilityScope WRITE setVisibilityScope NOTIFY visibilityScopeChanged) + + Q_PROPERTY(int count READ rowCount NOTIFY rowCountChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider *favoritesPlugin READ favoritesPlugin WRITE setFavoritesPlugin NOTIFY favoritesPluginChanged) + Q_PROPERTY(QVariantMap favoritesMatchParameters READ favoritesMatchParameters WRITE setFavoritesMatchParameters NOTIFY favoritesMatchParametersChanged) + + Q_ENUMS(SearchResultType RelevanceHint) + +public: + enum SearchResultType { + UnknownSearchResult = QPlaceSearchResult::UnknownSearchResult, + PlaceResult = QPlaceSearchResult::PlaceResult, + ProposedSearchResult = QPlaceSearchResult::ProposedSearchResult + }; + + enum RelevanceHint { + UnspecifiedHint = QPlaceSearchRequest::UnspecifiedHint, + DistanceHint = QPlaceSearchRequest::DistanceHint, + LexicalPlaceNameHint = QPlaceSearchRequest::LexicalPlaceNameHint + }; + + explicit QDeclarativeSearchResultModel(QObject *parent = 0); + ~QDeclarativeSearchResultModel(); + + QString searchTerm() const; + void setSearchTerm(const QString &searchTerm); + + QQmlListProperty<QDeclarativeCategory> categories(); + static void categories_append(QQmlListProperty<QDeclarativeCategory> *list, + QDeclarativeCategory *category); + static int categories_count(QQmlListProperty<QDeclarativeCategory> *list); + static QDeclarativeCategory *category_at(QQmlListProperty<QDeclarativeCategory> *list, int index); + static void categories_clear(QQmlListProperty<QDeclarativeCategory> *list); + + QString recommendationId() const; + void setRecommendationId(const QString &recommendationId); + + QDeclarativeSearchResultModel::RelevanceHint relevanceHint() const; + void setRelevanceHint(QDeclarativeSearchResultModel::RelevanceHint hint); + + QDeclarativePlace::Visibility visibilityScope() const; + void setVisibilityScope(QDeclarativePlace::Visibility visibilityScope); + + QDeclarativeGeoServiceProvider *favoritesPlugin() const; + void setFavoritesPlugin(QDeclarativeGeoServiceProvider *plugin); + + QVariantMap favoritesMatchParameters() const; + void setFavoritesMatchParameters(const QVariantMap ¶meters); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + + virtual void clearData(bool suppressSignal = false); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + Q_INVOKABLE QVariant data(int index, const QString &roleName) const; + QHash<int, QByteArray> roleNames() const; + + Q_INVOKABLE void updateWith(int proposedSearchIndex); + + void updateSearchRequest(); + +Q_SIGNALS: + void searchTermChanged(); + void categoriesChanged(); + void recommendationIdChanged(); + void relevanceHintChanged(); + void visibilityScopeChanged(); + + void rowCountChanged(); + void favoritesPluginChanged(); + void favoritesMatchParametersChanged(); + void dataChanged(); + +protected: + QPlaceReply *sendQuery(QPlaceManager *manager, const QPlaceSearchRequest &request); + virtual void initializePlugin(QDeclarativeGeoServiceProvider *plugin); + +protected Q_SLOTS: + virtual void queryFinished(); + +private Q_SLOTS: + void updateLayout(const QList<QPlace> &favoritePlaces = QList<QPlace>()); + + void placeUpdated(const QString &placeId); + void placeRemoved(const QString &placeId); + +private: + enum Roles { + SearchResultTypeRole = Qt::UserRole, + TitleRole, + IconRole, + DistanceRole, + PlaceRole, + SponsoredRole + }; + + int getRow(const QString &placeId) const; + QList<QDeclarativeCategory *> m_categories; + QLocation::VisibilityScope m_visibilityScope; + + QList<QPlaceSearchResult> m_results; + QList<QPlaceSearchResult> m_resultsBuffer; + QList<QDeclarativePlace *> m_places; + QList<QDeclarativePlaceIcon *> m_icons; + + QDeclarativeGeoServiceProvider *m_favoritesPlugin; + QVariantMap m_matchParameters; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESEARCHRESULTMODEL_P_H diff --git a/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel.cpp b/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel.cpp new file mode 100644 index 00000000..95cd3277 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** 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 "qdeclarativesearchsuggestionmodel_p.h" +#include "qdeclarativegeoserviceprovider_p.h" + +#include <QtQml/QQmlInfo> +#include <QtLocation/QGeoServiceProvider> + +#include <qplacemanager.h> +#include <qplacesearchrequest.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype PlaceSearchSuggestionModel + \instantiates QDeclarativeSearchSuggestionModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since Qt Location 5.5 + + \brief Provides access to search term suggestions. + + The PlaceSearchSuggestionModel can be used to provide search term suggestions as the user enters their + search term. The properties of this model should match that of the \l PlaceSearchModel, which + will be used to perform the actual search query, to ensure that the search suggestion results are + relevant. + + There are two ways of accessing the data provided by this model, either through the + \l suggestions property or through views and delegates. The latter is the preferred + method. + + The \l offset and \l limit properties can be used to access paged suggestions. When the + \l offset and \l limit properties are set the suggestions between \l offset and + (\l offset + \l limit - 1) will be returned. Support for paging may vary + from plugin to plugin. + + The model returns data for the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li suggestion + \li string + \li Suggested search term. + \endtable + + The following example shows how to use the PlaceSearchSuggestionModel to get suggested search terms + from a partial search term. The \l searchArea is set to match what would be used to perform the + actual place search with \l PlaceSearchModel. + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml SearchSuggestionModel + + \sa PlaceSearchModel, {QPlaceManager} +*/ + +/*! + \qmlproperty Plugin PlaceSearchSuggestionModel::plugin + + This property holds the provider \l Plugin which will be used to perform the search. +*/ + +/*! + \qmlproperty geoshape PlaceSearchSuggestionModel::searchArea + + This property holds the search area. Search suggestion results returned by the model will be + relevant to the given 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. +*/ + +/*! + \qmlproperty int PlaceSearchSuggestionModel::offset + + This property holds the index of the first item in the model. + + \sa limit +*/ + +/*! + \qmlproperty int PlaceSearchSuggestionModel::limit + + This property holds the limit of the number of items that will be returned. + + \sa offset +*/ + +/*! + \qmlproperty enum PlaceSearchSuggestionModel::status + + This property holds the status of the model. It can be one of: + + \table + \row + \li PlaceSearchSuggestionModel.Null + \li No search query has been executed. The model is empty. + \row + \li PlaceSearchSuggestionModel.Ready + \li The search query has completed, and the results are available. + \row + \li PlaceSearchSuggestionModel.Loading + \li A search query is currently being executed. + \row + \li PlaceSearchSuggestionModel.Error + \li An error occurred when executing the previous search query. + \endtable +*/ + +/*! + \qmlmethod void PlaceSearchSuggestionModel::update() + + Updates the model based on the provided query parameters. The model will be populated with a + list of search suggestions for the partial \l searchTerm and \l searchArea. If the \l plugin + supports it, other parameters such as \l limit and \l offset may be specified. \c update() + submits the set of parameters to the \l plugin to process. + + + While the model is updating the \l status of the model is set to + \c PlaceSearchSuggestionModel.Loading. If the model is successfully updated, the \l status is + set to \c PlaceSearchSuggestionModel.Ready, while if it unsuccessfully completes, the \l status + is set to \c PlaceSearchSuggestionModel.Error and the model cleared. + + This example shows use of the model + \code + PlaceSeachSuggestionModel { + id: model + plugin: backendPlugin + searchArea: QtPositioning.circle(QtPositioning.coordinate(10, 10)) + ... + } + + MouseArea { + ... + onClicked: { + model.searchTerm = "piz" + model.searchArea.center.latitude = -27.5; + model.searchArea.cetner.longitude = 153; + model.update(); + } + } + \endcode + + A more detailed example can be found in the in + \l {Places (QML)#Presenting-Search-Suggestions}{Places (QML)} example. + + \sa cancel(), status +*/ + +/*! + \qmlmethod void PlaceSearchSuggestionModel::cancel() + + Cancels an ongoing search suggestion operation immediately and sets the model + status to PlaceSearchSuggestionModel.Ready. The model retains any search + suggestions it had before the operation was started. + + If an operation is not ongoing, invoking cancel() has no effect. + + \sa update(), status +*/ + +/*! + \qmlmethod void PlaceSearchSuggestionModel::reset() + + Resets the model. All search suggestions are cleared, any outstanding requests are aborted and + possible errors are cleared. Model status will be set to PlaceSearchSuggestionModel.Null. +*/ + + +/*! + \qmlmethod string QtLocation::PlaceSearchSuggestionModel::errorString() const + + This read-only property holds the textual presentation of the latest search suggestion 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. +*/ + +QDeclarativeSearchSuggestionModel::QDeclarativeSearchSuggestionModel(QObject *parent) +: QDeclarativeSearchModelBase(parent) +{ +} + +QDeclarativeSearchSuggestionModel::~QDeclarativeSearchSuggestionModel() +{ +} + +/*! + \qmlproperty string PlaceSearchSuggestionModel::searchTerm + + This property holds the partial search term used in query. +*/ +QString QDeclarativeSearchSuggestionModel::searchTerm() const +{ + return m_request.searchTerm(); +} + +void QDeclarativeSearchSuggestionModel::setSearchTerm(const QString &searchTerm) +{ + if (m_request.searchTerm() == searchTerm) + return; + + m_request.setSearchTerm(searchTerm); + emit searchTermChanged(); +} + +/*! + \qmlproperty stringlist PlaceSearchSuggestionModel::suggestions + + This property holds the list of predicted search terms that the model currently has. +*/ +QStringList QDeclarativeSearchSuggestionModel::suggestions() const +{ + return m_suggestions; +} + +/*! + \internal +*/ +void QDeclarativeSearchSuggestionModel::clearData(bool suppressSignal) +{ + QDeclarativeSearchModelBase::clearData(suppressSignal); + + if (!m_suggestions.isEmpty()) { + m_suggestions.clear(); + + if (!suppressSignal) + emit suggestionsChanged(); + } +} + +/*! + \internal +*/ +int QDeclarativeSearchSuggestionModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_suggestions.count(); +} + +/*! + \internal +*/ +QVariant QDeclarativeSearchSuggestionModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + switch (role) { + case Qt::DisplayRole: + case SearchSuggestionRole: + return m_suggestions.at(index.row()); + } + + return QVariant(); +} + +QHash<int, QByteArray> QDeclarativeSearchSuggestionModel::roleNames() const +{ + QHash<int, QByteArray> roleNames = QDeclarativeSearchModelBase::roleNames(); + roleNames.insert(SearchSuggestionRole, "suggestion"); + return roleNames; +} + +/*! + \internal +*/ +void QDeclarativeSearchSuggestionModel::queryFinished() +{ + if (!m_reply) + return; + + QPlaceReply *reply = m_reply; + m_reply = 0; + + int initialCount = m_suggestions.count(); + beginResetModel(); + + clearData(true); + + QPlaceSearchSuggestionReply *suggestionReply = qobject_cast<QPlaceSearchSuggestionReply *>(reply); + m_suggestions = suggestionReply->suggestions(); + + if (initialCount != m_suggestions.count()) + emit suggestionsChanged(); + + endResetModel(); + + if (suggestionReply->error() != QPlaceReply::NoError) + setStatus(Error, suggestionReply->errorString()); + else + setStatus(Ready); + + + reply->deleteLater(); +} + +/*! + \internal +*/ +QPlaceReply *QDeclarativeSearchSuggestionModel::sendQuery(QPlaceManager *manager, + const QPlaceSearchRequest &request) +{ + return manager->searchSuggestions(request); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel_p.h b/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel_p.h new file mode 100644 index 00000000..5d75ee4b --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESEARCHSUGGESTIONMODEL_P_H +#define QDECLARATIVESEARCHSUGGESTIONMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativesearchmodelbase_p.h> + +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoServiceProvider; +class QGeoServiceProvider; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSearchSuggestionModel : public QDeclarativeSearchModelBase +{ + Q_OBJECT + + Q_PROPERTY(QString searchTerm READ searchTerm WRITE setSearchTerm NOTIFY searchTermChanged) + Q_PROPERTY(QStringList suggestions READ suggestions NOTIFY suggestionsChanged) + +public: + explicit QDeclarativeSearchSuggestionModel(QObject *parent = 0); + ~QDeclarativeSearchSuggestionModel(); + + QString searchTerm() const; + void setSearchTerm(const QString &searchTerm); + + QStringList suggestions() const; + + void clearData(bool suppressSignal = false); + + // From QAbstractListModel + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QHash<int, QByteArray> roleNames() const; + + enum Roles { + SearchSuggestionRole = Qt::UserRole + }; + +protected Q_SLOTS: + virtual void queryFinished(); + +Q_SIGNALS: + void searchTermChanged(); + void suggestionsChanged(); + +protected: + QPlaceReply *sendQuery(QPlaceManager *manager, const QPlaceSearchRequest &request); + +private: + QStringList m_suggestions; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativeplaces/qdeclarativesupplier.cpp b/src/location/declarativeplaces/qdeclarativesupplier.cpp new file mode 100644 index 00000000..92e9a8fd --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesupplier.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** 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 "qdeclarativesupplier_p.h" + +#include <QtCore/QUrl> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Supplier + \instantiates QDeclarativeSupplier + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since Qt Location 5.5 + + \brief Holds data regarding the supplier of a place, a place's image, review, or editorial. + + Each instance represents a set of data about a supplier, which can include + supplier's name, url and icon. The supplier is typically a business or organization. + + Note: The Places API only supports suppliers as 'retrieve-only' objects. Submitting + suppliers to a provider is not a supported use case. + + \sa ImageModel, ReviewModel, EditorialModel + + \section1 Example + + The following example shows how to create and display a supplier in QML: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml Supplier +*/ + +QDeclarativeSupplier::QDeclarativeSupplier(QObject *parent) + : QObject(parent), m_icon(0) +{ +} + +QDeclarativeSupplier::QDeclarativeSupplier(const QPlaceSupplier &src, + QDeclarativeGeoServiceProvider *plugin, + QObject *parent) + : QObject(parent), + m_src(src), + m_icon(0) +{ + setSupplier(src, plugin); +} + +QDeclarativeSupplier::~QDeclarativeSupplier() +{ +} + +/*! + \internal +*/ +void QDeclarativeSupplier::componentComplete() +{ + // delayed instantiation of QObject based properties. + if (!m_icon) + m_icon = new QDeclarativePlaceIcon(this); +} + +/*! + \qmlproperty QPlaceSupplier Supplier::supplier + + For details on how to use this property to interface between C++ and QML see + "\l {Supplier - QPlaceSupplier} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativeSupplier::setSupplier(const QPlaceSupplier &src, QDeclarativeGeoServiceProvider *plugin) +{ + QPlaceSupplier previous = m_src; + m_src = src; + + if (previous.name() != m_src.name()) + emit nameChanged(); + + if (previous.supplierId() != m_src.supplierId()) + emit supplierIdChanged(); + + if (previous.url() != m_src.url()) + emit urlChanged(); + + if (m_icon && m_icon->parent() == this) { + m_icon->setPlugin(plugin); + m_icon->setIcon(m_src.icon()); + } else if (!m_icon || m_icon->parent() != this) { + m_icon = new QDeclarativePlaceIcon(m_src.icon(), plugin, this); + emit iconChanged(); + } +} + +QPlaceSupplier QDeclarativeSupplier::supplier() +{ + m_src.setIcon(m_icon ? m_icon->icon() : QPlaceIcon()); + return m_src; +} + +/*! + \qmlproperty string Supplier::supplierId + + This property holds the identifier of the supplier. The identifier is unique + to the Plugin backend which provided the supplier and is generally + not suitable for displaying to the user. +*/ + +void QDeclarativeSupplier::setSupplierId(const QString &supplierId) +{ + if (m_src.supplierId() != supplierId) { + m_src.setSupplierId(supplierId); + emit supplierIdChanged(); + } +} + +QString QDeclarativeSupplier::supplierId() const +{ + return m_src.supplierId(); +} + +/*! + \qmlproperty string Supplier::name + + This property holds the name of the supplier which can be displayed + to the user. + + The name can potentially be localized. The language is dependent on the + entity that sets it, typically this is the \l Plugin. The \l {Plugin::locales} + property defines what language is used. +*/ + +void QDeclarativeSupplier::setName(const QString &name) +{ + if (m_src.name() != name) { + m_src.setName(name); + emit nameChanged(); + } +} + +QString QDeclarativeSupplier::name() const +{ + return m_src.name(); +} + +/*! + \qmlproperty url Supplier::url + + This property holds the URL of the supplier's website. +*/ + +void QDeclarativeSupplier::setUrl(const QUrl &url) +{ + if (m_src.url() != url) { + m_src.setUrl(url); + emit urlChanged(); + } +} + +QUrl QDeclarativeSupplier::url() const +{ + return m_src.url(); +} + +/*! + \qmlproperty PlaceIcon Supplier::icon + + This property holds the icon of the supplier. +*/ +QDeclarativePlaceIcon *QDeclarativeSupplier::icon() const +{ + return m_icon; +} + +void QDeclarativeSupplier::setIcon(QDeclarativePlaceIcon *icon) +{ + if (m_icon == icon) + return; + + if (m_icon && m_icon->parent() == this) + delete m_icon; + + m_icon = icon; + emit iconChanged(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativesupplier_p.h b/src/location/declarativeplaces/qdeclarativesupplier_p.h new file mode 100644 index 00000000..b344d674 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesupplier_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESUPPLIER_P_H +#define QDECLARATIVESUPPLIER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QObject> +#include <QtCore/QUrl> +#include <QtQml/qqml.h> +#include <QtQml/QQmlParserStatus> +#include <QtLocation/qplacesupplier.h> + +#include <QtLocation/private/qdeclarativeplaceicon_p.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSupplier : public QObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_PROPERTY(QPlaceSupplier supplier READ supplier WRITE setSupplier) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString supplierId READ supplierId WRITE setSupplierId NOTIFY supplierIdChanged) + Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QDeclarativePlaceIcon *icon READ icon WRITE setIcon NOTIFY iconChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativeSupplier(QObject *parent = 0); + explicit QDeclarativeSupplier(const QPlaceSupplier &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent = 0); + ~QDeclarativeSupplier(); + + // From QQmlParserStatus + void classBegin() { } + void componentComplete(); + + QPlaceSupplier supplier(); + void setSupplier(const QPlaceSupplier &src, QDeclarativeGeoServiceProvider *plugin = 0); + + QString name() const; + void setName(const QString &data); + QString supplierId() const; + void setSupplierId(const QString &data); + QUrl url() const; + void setUrl(const QUrl &data); + + QDeclarativePlaceIcon *icon() const; + void setIcon(QDeclarativePlaceIcon *icon); + +Q_SIGNALS: + void nameChanged(); + void supplierIdChanged(); + void urlChanged(); + void iconChanged(); + +private: + QPlaceSupplier m_src; + QDeclarativePlaceIcon *m_icon; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSupplier) + +#endif // QDECLARATIVESUPPLIER_P_H diff --git a/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel.cpp b/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel.cpp new file mode 100644 index 00000000..d832ff40 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel.cpp @@ -0,0 +1,689 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Aaron McCarthy <mccarthy.aaron@gmail.com> +** 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 "qdeclarativesupportedcategoriesmodel_p.h" +#include "qdeclarativeplaceicon_p.h" +#include "qgeoserviceprovider.h" +#include "error_messages.h" + +#include <QCoreApplication> +#include <QtQml/QQmlInfo> +#include <QtLocation/QPlaceManager> +#include <QtLocation/QPlaceIcon> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype CategoryModel + \instantiates QDeclarativeSupportedCategoriesModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since Qt Location 5.5 + + \brief The CategoryModel type provides a model of the categories supported by a \l Plugin. + + The CategoryModel type provides a model of the categories that are available from the + current \l Plugin. The model supports both a flat list of categories and a hierarchical tree + representing category groupings. This can be controlled by the \l hierarchical property. + + The model supports the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li category + \li \l Category + \li Category object for the current item. + \row + \li parentCategory + \li \l Category + \li Parent category object for the current item. + If there is no parent, null is returned. + \endtable + + The following example displays a flat list of all available categories: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml CategoryView + + To access the hierarchical category model it is necessary to use a \l DelegateModel to access + the child items. +*/ + +/*! + \qmlproperty Plugin CategoryModel::plugin + + This property holds the provider \l Plugin used by this model. +*/ + +/*! + \qmlproperty bool CategoryModel::hierarchical + + This property holds whether the model provides a hierarchical tree of categories or a flat + list. The default is true. +*/ + +/*! + \qmlmethod string QtLocation::CategoryModel::data(ModelIndex index, int role) + \internal + + This method retrieves the model's data per \a index and \a role. +*/ + +/*! + \qmlmethod string QtLocation::CategoryModel::errorString() const + + This read-only property holds the textual presentation of the latest category model 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. +*/ + +/*! + \internal + \enum QDeclarativeSupportedCategoriesModel::Roles +*/ + +QDeclarativeSupportedCategoriesModel::QDeclarativeSupportedCategoriesModel(QObject *parent) +: QAbstractItemModel(parent), m_response(0), m_plugin(0), m_hierarchical(true), + m_complete(false), m_status(Null) +{ +} + +QDeclarativeSupportedCategoriesModel::~QDeclarativeSupportedCategoriesModel() +{ + qDeleteAll(m_categoriesTree); +} + +/*! + \internal +*/ +// From QQmlParserStatus +void QDeclarativeSupportedCategoriesModel::componentComplete() +{ + m_complete = true; +} + +/*! + \internal +*/ +int QDeclarativeSupportedCategoriesModel::rowCount(const QModelIndex &parent) const +{ + if (m_categoriesTree.keys().isEmpty()) + return 0; + + PlaceCategoryNode *node = static_cast<PlaceCategoryNode *>(parent.internalPointer()); + if (!node) + node = m_categoriesTree.value(QString()); + else if (m_categoriesTree.keys(node).isEmpty()) + return 0; + + return node->childIds.count(); +} + +/*! + \internal +*/ +int QDeclarativeSupportedCategoriesModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return 1; +} + +/*! + \internal +*/ +QModelIndex QDeclarativeSupportedCategoriesModel::index(int row, int column, const QModelIndex &parent) const +{ + if (column != 0 || row < 0) + return QModelIndex(); + + PlaceCategoryNode *node = static_cast<PlaceCategoryNode *>(parent.internalPointer()); + + if (!node) + node = m_categoriesTree.value(QString()); + else if (m_categoriesTree.keys(node).isEmpty()) //return root index if parent is non-existent + return QModelIndex(); + + if (row > node->childIds.count()) + return QModelIndex(); + + QString id = node->childIds.at(row); + Q_ASSERT(m_categoriesTree.contains(id)); + + return createIndex(row, 0, m_categoriesTree.value(id)); +} + +/*! + \internal +*/ +QModelIndex QDeclarativeSupportedCategoriesModel::parent(const QModelIndex &child) const +{ + PlaceCategoryNode *childNode = static_cast<PlaceCategoryNode *>(child.internalPointer()); + if (m_categoriesTree.keys(childNode).isEmpty()) + return QModelIndex(); + + return index(childNode->parentId); +} + +/*! + \internal +*/ +QVariant QDeclarativeSupportedCategoriesModel::data(const QModelIndex &index, int role) const +{ + PlaceCategoryNode *node = static_cast<PlaceCategoryNode *>(index.internalPointer()); + if (!node) + node = m_categoriesTree.value(QString()); + else if (m_categoriesTree.keys(node).isEmpty()) + return QVariant(); + + QDeclarativeCategory *category = node->declCategory.data(); + + switch (role) { + case Qt::DisplayRole: + return category->name(); + case CategoryRole: + return QVariant::fromValue(category); + case ParentCategoryRole: { + if (!m_categoriesTree.keys().contains(node->parentId)) + return QVariant(); + else + return QVariant::fromValue(m_categoriesTree.value(node->parentId)->declCategory.data()); + } + default: + return QVariant(); + } +} + +QHash<int, QByteArray> QDeclarativeSupportedCategoriesModel::roleNames() const +{ + QHash<int, QByteArray> roles = QAbstractItemModel::roleNames(); + roles.insert(CategoryRole, "category"); + roles.insert(ParentCategoryRole, "parentCategory"); + return roles; +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + //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(categoryAdded(QPlaceCategory,QString)), + this, SLOT(addedCategory(QPlaceCategory,QString))); + disconnect(placeManager, SIGNAL(categoryUpdated(QPlaceCategory,QString)), + this, SLOT(updatedCategory(QPlaceCategory,QString))); + disconnect(placeManager, SIGNAL(categoryRemoved(QString,QString)), + this, SLOT(removedCategory(QString,QString))); + disconnect(placeManager, SIGNAL(dataChanged()), + this, SIGNAL(dataChanged())); + } + } + } + + m_plugin = plugin; + + // handle plugin name changes -> update categories + if (m_plugin) { + connect(m_plugin, SIGNAL(nameChanged(QString)), this, SLOT(connectNotificationSignals())); + connect(m_plugin, SIGNAL(nameChanged(QString)), this, SLOT(update())); + } + + connectNotificationSignals(); + + if (m_complete) + emit pluginChanged(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider *QDeclarativeSupportedCategoriesModel::plugin() const +{ + return m_plugin; +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::setHierarchical(bool hierarchical) +{ + if (m_hierarchical == hierarchical) + return; + + m_hierarchical = hierarchical; + emit hierarchicalChanged(); + + updateLayout(); +} + +/*! + \internal +*/ +bool QDeclarativeSupportedCategoriesModel::hierarchical() const +{ + return m_hierarchical; +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::replyFinished() +{ + if (!m_response) + return; + + m_response->deleteLater(); + + if (m_response->error() == QPlaceReply::NoError) { + m_errorString.clear(); + + m_response = 0; + + updateLayout(); + setStatus(QDeclarativeSupportedCategoriesModel::Ready); + } else { + const QString errorString = m_response->errorString(); + + m_response = 0; + + setStatus(Error, errorString); + } +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::addedCategory(const QPlaceCategory &category, + const QString &parentId) +{ + if (m_response) + return; + + if (!m_categoriesTree.contains(parentId)) + return; + + if (category.categoryId().isEmpty()) + return; + + PlaceCategoryNode *parentNode = m_categoriesTree.value(parentId); + if (!parentNode) + return; + + int rowToBeAdded = rowToAddChild(parentNode, category); + QModelIndex parentIndex = index(parentId); + beginInsertRows(parentIndex, rowToBeAdded, rowToBeAdded); + PlaceCategoryNode *categoryNode = new PlaceCategoryNode; + categoryNode->parentId = parentId; + categoryNode->declCategory = QSharedPointer<QDeclarativeCategory>(new QDeclarativeCategory(category, m_plugin, this)); + + m_categoriesTree.insert(category.categoryId(), categoryNode); + parentNode->childIds.insert(rowToBeAdded,category.categoryId()); + endInsertRows(); + + //this is a workaround to deal with the fact that the hasModelChildren field of DelegateModel + //does not get updated when a child is added to a model + beginResetModel(); + endResetModel(); +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::updatedCategory(const QPlaceCategory &category, + const QString &parentId) +{ + if (m_response) + return; + + QString categoryId = category.categoryId(); + + if (!m_categoriesTree.contains(parentId)) + return; + + if (category.categoryId().isEmpty() || !m_categoriesTree.contains(categoryId)) + return; + + PlaceCategoryNode *newParentNode = m_categoriesTree.value(parentId); + if (!newParentNode) + return; + + PlaceCategoryNode *categoryNode = m_categoriesTree.value(categoryId); + if (!categoryNode) + return; + + categoryNode->declCategory->setCategory(category); + + if (categoryNode->parentId == parentId) { //reparenting to same parent + QModelIndex parentIndex = index(parentId); + int rowToBeAdded = rowToAddChild(newParentNode, category); + int oldRow = newParentNode->childIds.indexOf(categoryId); + + //check if we are changing the position of the category + if (qAbs(rowToBeAdded - newParentNode->childIds.indexOf(categoryId)) > 1) { + //if the position has changed we are moving rows + beginMoveRows(parentIndex, oldRow, oldRow, + parentIndex, rowToBeAdded); + + newParentNode->childIds.removeAll(categoryId); + newParentNode->childIds.insert(rowToBeAdded, categoryId); + endMoveRows(); + } else {// if the position has not changed we modifying an existing row + QModelIndex categoryIndex = index(categoryId); + emit dataChanged(categoryIndex, categoryIndex); + } + } else { //reparenting to different parents + QPlaceCategory oldCategory = categoryNode->declCategory->category(); + PlaceCategoryNode *oldParentNode = m_categoriesTree.value(categoryNode->parentId); + if (!oldParentNode) + return; + QModelIndex oldParentIndex = index(categoryNode->parentId); + QModelIndex newParentIndex = index(parentId); + + int rowToBeAdded = rowToAddChild(newParentNode, category); + beginMoveRows(oldParentIndex, oldParentNode->childIds.indexOf(categoryId), + oldParentNode->childIds.indexOf(categoryId), newParentIndex, rowToBeAdded); + oldParentNode->childIds.removeAll(oldCategory.categoryId()); + newParentNode->childIds.insert(rowToBeAdded, categoryId); + categoryNode->parentId = parentId; + endMoveRows(); + + //this is a workaround to deal with the fact that the hasModelChildren field of DelegateModel + //does not get updated when an index is updated to contain children + beginResetModel(); + endResetModel(); + } +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::removedCategory(const QString &categoryId, const QString &parentId) +{ + if (m_response) + return; + + if (!m_categoriesTree.contains(categoryId) || !m_categoriesTree.contains(parentId)) + return; + + QModelIndex parentIndex = index(parentId); + QModelIndex categoryIndex = index(categoryId); + + beginRemoveRows(parentIndex, categoryIndex.row(), categoryIndex.row()); + PlaceCategoryNode *parentNode = m_categoriesTree.value(parentId); + parentNode->childIds.removeAll(categoryId); + delete m_categoriesTree.take(categoryId); + endRemoveRows(); +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::connectNotificationSignals() +{ + if (!m_plugin) + return; + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider || serviceProvider->error() != QGeoServiceProvider::NoError) + return; + + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) + return; + + // listen for any category notifications so that we can reupdate the categories + // model. + connect(placeManager, SIGNAL(categoryAdded(QPlaceCategory,QString)), + this, SLOT(addedCategory(QPlaceCategory,QString))); + connect(placeManager, SIGNAL(categoryUpdated(QPlaceCategory,QString)), + this, SLOT(updatedCategory(QPlaceCategory,QString))); + connect(placeManager, SIGNAL(categoryRemoved(QString,QString)), + this, SLOT(removedCategory(QString,QString))); + connect(placeManager, SIGNAL(dataChanged()), + this, SIGNAL(dataChanged())); +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::update() +{ + if (m_response) + return; + + setStatus(Loading); + + if (!m_plugin) { + updateLayout(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROPERTY_NOT_SET)); + return; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider || serviceProvider->error() != QGeoServiceProvider::NoError) { + updateLayout(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROVIDER_ERROR) + .arg(m_plugin->name())); + return; + } + + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) { + updateLayout(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return; + } + + m_response = placeManager->initializeCategories(); + if (m_response) { + connect(m_response, SIGNAL(finished()), this, SLOT(replyFinished())); + } else { + updateLayout(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, + CATEGORIES_NOT_INITIALIZED)); + } +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::updateLayout() +{ + beginResetModel(); + qDeleteAll(m_categoriesTree); + m_categoriesTree.clear(); + + if (m_plugin) { + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (serviceProvider && serviceProvider->error() == QGeoServiceProvider::NoError) { + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (placeManager) { + PlaceCategoryNode *node = new PlaceCategoryNode; + node->childIds = populateCategories(placeManager, QPlaceCategory()); + m_categoriesTree.insert(QString(), node); + node->declCategory = QSharedPointer<QDeclarativeCategory> + (new QDeclarativeCategory(QPlaceCategory(), m_plugin, this)); + } + } + } + + endResetModel(); +} + +QString QDeclarativeSupportedCategoriesModel::errorString() const +{ + return m_errorString; +} + +/*! + \qmlproperty enumeration CategoryModel::status + + This property holds the status of the model. It can be one of: + + \table + \row + \li CategoryModel.Null + \li No category fetch query has been executed. The model is empty. + \row + \li CategoryModel.Ready + \li No error occurred during the last operation, further operations may be performed on + the model. + \row + \li CategoryModel.Loading + \li The model is being updated, no other operations may be performed until complete. + \row + \li CategoryModel.Error + \li An error occurred during the last operation, further operations can still be + performed on the model. + \endtable +*/ +void QDeclarativeSupportedCategoriesModel::setStatus(Status status, const QString &errorString) +{ + Status originalStatus = m_status; + m_status = status; + m_errorString = errorString; + + if (originalStatus != m_status) + emit statusChanged(); +} + +QDeclarativeSupportedCategoriesModel::Status QDeclarativeSupportedCategoriesModel::status() const +{ + return m_status; +} + +/*! + \internal +*/ +QStringList QDeclarativeSupportedCategoriesModel::populateCategories(QPlaceManager *manager, const QPlaceCategory &parent) +{ + Q_ASSERT(manager); + + QStringList childIds; + PlaceCategoryNode *node; + + QMap<QString, QPlaceCategory> sortedCategories; + foreach ( const QPlaceCategory &category, manager->childCategories(parent.categoryId())) + sortedCategories.insert(category.name(), category); + + QMapIterator<QString, QPlaceCategory> iter(sortedCategories); + while (iter.hasNext()) { + iter.next(); + node = new PlaceCategoryNode; + node->parentId = parent.categoryId(); + node->declCategory = QSharedPointer<QDeclarativeCategory>(new QDeclarativeCategory(iter.value(), m_plugin ,this)); + + if (m_hierarchical) + node->childIds = populateCategories(manager, iter.value()); + + m_categoriesTree.insert(node->declCategory->categoryId(), node); + childIds.append(iter.value().categoryId()); + + if (!m_hierarchical) { + childIds.append(populateCategories(manager,node->declCategory->category())); + } + } + return childIds; +} + +/*! + \internal +*/ +QModelIndex QDeclarativeSupportedCategoriesModel::index(const QString &categoryId) const +{ + if (categoryId.isEmpty()) + return QModelIndex(); + + if (!m_categoriesTree.contains(categoryId)) + return QModelIndex(); + + PlaceCategoryNode *categoryNode = m_categoriesTree.value(categoryId); + if (!categoryNode) + return QModelIndex(); + + QString parentCategoryId = categoryNode->parentId; + + PlaceCategoryNode *parentNode = m_categoriesTree.value(parentCategoryId); + + return createIndex(parentNode->childIds.indexOf(categoryId), 0, categoryNode); +} + +/*! + \internal +*/ +int QDeclarativeSupportedCategoriesModel::rowToAddChild(PlaceCategoryNode *node, const QPlaceCategory &category) +{ + Q_ASSERT(node); + for (int i = 0; i < node->childIds.count(); ++i) { + if (category.name() < m_categoriesTree.value(node->childIds.at(i))->declCategory->name()) + return i; + } + return node->childIds.count(); +} + +/*! + \qmlsignal CategoryModel::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 categories + change without any action on their part. + + The corresponding handler is \c onDataChanged. +*/ + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h b/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h new file mode 100644 index 00000000..9f17ab4d --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESUPPORTEDCATEGORIESMODEL_H +#define QDECLARATIVESUPPORTEDCATEGORIESMODEL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativegeoserviceprovider_p.h> + +#include <QObject> +#include <QtCore/QStringList> +#include <QtCore/QSharedPointer> +#include <QAbstractListModel> +#include <QQmlListProperty> +#include <QtQml/QQmlParserStatus> + +#include <QtLocation/QPlaceCategory> + +#include <QtLocation/private/qdeclarativecategory_p.h> + +QT_BEGIN_NAMESPACE + +class QGeoServiceProvider; +class QPlaceManager; +class QPlaceReply; + +class PlaceCategoryNode +{ +public: + QString parentId; + QStringList childIds; + QSharedPointer<QDeclarativeCategory> declCategory; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSupportedCategoriesModel : public QAbstractItemModel, public QQmlParserStatus +{ + Q_OBJECT + + Q_ENUMS(Status) + + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(bool hierarchical READ hierarchical WRITE setHierarchical NOTIFY hierarchicalChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + + Q_INTERFACES(QQmlParserStatus) + Q_ENUMS(Roles) //The Roles enum is for internal usage only. + +public: + explicit QDeclarativeSupportedCategoriesModel(QObject *parent = 0); + virtual ~QDeclarativeSupportedCategoriesModel(); + + // From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + // From QAbstractItemModel + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + + Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const; + QHash<int, QByteArray> roleNames() const; + + enum Roles { + CategoryRole = Qt::UserRole, + ParentCategoryRole + }; //for internal usage only + + enum Status {Null, Ready, Loading, Error}; + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + void setHierarchical(bool hierarchical); + bool hierarchical() const; + + Q_INVOKABLE QString errorString() const; + + Status status() const; + void setStatus(Status status, const QString &errorString = QString()); + + using QAbstractItemModel::dataChanged; +Q_SIGNALS: + void pluginChanged(); + void hierarchicalChanged(); + void statusChanged(); + void dataChanged(); + +public Q_SLOTS: + void update(); + +private Q_SLOTS: + void replyFinished(); + void addedCategory(const QPlaceCategory &category, const QString &parentId); + void updatedCategory(const QPlaceCategory &category, const QString &parentId); + void removedCategory(const QString &categoryId, const QString &parentId); + void connectNotificationSignals(); + +private: + QStringList populateCategories(QPlaceManager *, const QPlaceCategory &parent); + QModelIndex index(const QString &categoryId) const; + int rowToAddChild(PlaceCategoryNode *, const QPlaceCategory &category); + void updateLayout(); + + QPlaceReply *m_response; + + QDeclarativeGeoServiceProvider *m_plugin; + bool m_hierarchical; + bool m_complete; + Status m_status; + QString m_errorString; + + QHash<QString, PlaceCategoryNode *> m_categoriesTree; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSupportedCategoriesModel) + +#endif // QDECLARATIVESUPPORTEDCATEGORIESMODEL_H diff --git a/src/location/location.pro b/src/location/location.pro index 3669188c..b43d626a 100644 --- a/src/location/location.pro +++ b/src/location/location.pro @@ -1,15 +1,19 @@ TARGET = QtLocation QT = core-private positioning-private - -#INCLUDEPATH += ../3rdparty/poly2tri -INCLUDEPATH += ../3rdparty/clipper -INCLUDEPATH += ../3rdparty/clip2tri - android { # adding qtconcurrent dependency here for the osm plugin QT += concurrent } +CONFIG += simd optimize_full + +INCLUDEPATH += ../3rdparty/poly2tri +INCLUDEPATH += ../3rdparty/clipper +INCLUDEPATH += ../3rdparty/clip2tri +INCLUDEPATH += ../positioning +INCLUDEPATH += ../imports/positioning +INCLUDEPATH *= $$PWD + MODULE_PLUGIN_TYPES = \ geoservices @@ -18,6 +22,7 @@ OTHER_FILES += doc/src/*.qdoc # show .qdoc files in Qt Creator PUBLIC_HEADERS += \ qlocation.h \ + qlocationglobal_p.h \ qlocationglobal.h PRIVATE_HEADERS += \ @@ -28,6 +33,8 @@ SOURCES += \ include(maps/maps.pri) include(places/places.pri) +include(declarativemaps/declarativemaps.pri) +include(declarativeplaces/declarativeplaces.pri) HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS |