diff options
Diffstat (limited to 'src/location/labs/qmapobjectview.cpp')
-rw-r--r-- | src/location/labs/qmapobjectview.cpp | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/src/location/labs/qmapobjectview.cpp b/src/location/labs/qmapobjectview.cpp new file mode 100644 index 00000000..310775a1 --- /dev/null +++ b/src/location/labs/qmapobjectview.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** 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 "qmapobjectview_p.h" +#include "qmapobjectview_p_p.h" +#include <QtQml/private/qqmldelegatemodel_p.h> +#include <QtLocation/private/qgeomap_p.h> + +QT_BEGIN_NAMESPACE + +/* + + QGeoMapLayerPrivate + +*/ + +static const QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::Asynchronous; + +QMapObjectViewPrivate::QMapObjectViewPrivate(QGeoMapObject *q) + : QGeoMapObjectPrivate(q) +{ +} + +QMapObjectViewPrivate::~QMapObjectViewPrivate() +{ + +} + +QGeoMapObject::Type QMapObjectViewPrivate::type() const +{ + return QGeoMapObject::ViewType; +} + + +/* + + QGeoMapLayerPrivateDefault + +*/ + + +QMapObjectViewPrivateDefault::QMapObjectViewPrivateDefault(const QMapObjectViewPrivate &other) : QMapObjectViewPrivate(other.q) +{ +} + +QMapObjectViewPrivateDefault::~QMapObjectViewPrivateDefault() +{ + +} + +QMapObjectViewPrivateDefault::QMapObjectViewPrivateDefault(QGeoMapObject *q) : QMapObjectViewPrivate(q) +{ + +} + +QGeoMapObjectPrivate *QMapObjectViewPrivateDefault::clone() +{ + return new QMapObjectViewPrivateDefault(*this); +} + +/* + + QMapObjectView + +*/ + + +QMapObjectView::QMapObjectView(QObject *parent) + : QGeoMapObject(QExplicitlySharedDataPointer<QGeoMapObjectPrivate>(new QMapObjectViewPrivateDefault(this)), parent) +{ + +} + +QMapObjectView::~QMapObjectView() +{ + flushDelegateModel(); + flushUserAddedMapObjects(); +} + +QList<QGeoMapObject *> QMapObjectView::geoMapObjectChildren() const +{ + auto kids = QGeoMapObject::geoMapObjectChildren(); + auto size = m_instantiatedMapObjects.count(); + for (int i = 0; i < size; ++i) { + auto obj = qobject_cast<QGeoMapObject*>(m_instantiatedMapObjects[i]); + if (obj) + kids << obj; + } + for (int i = 0; i < m_userAddedMapObjects.size(); ++i) { + auto obj = m_userAddedMapObjects.at(i); + if (obj) + kids << obj; + } + return kids; +} + +void QMapObjectView::setMap(QGeoMap *map) +{ + QMapObjectViewPrivate *d = static_cast<QMapObjectViewPrivate *>(d_ptr.data()); + if (d->m_map == map) + return; + + QGeoMapObject::setMap(map); // This is where the specialized pimpl gets created and injected + + for (int i = 0; i < m_userAddedMapObjects.size(); ++i) { + auto obj = m_userAddedMapObjects.at(i); + if (obj && obj->map() != map) + obj->setMap(map); + } + + if (!map) { + // Map was set, now it has ben re-set to NULL + flushDelegateModel(); + flushUserAddedMapObjects(); + d_ptr = new QMapObjectViewPrivateDefault(*d); + } else if (d->m_componentCompleted) { + // Map was null, now it's set AND delegateModel is already complete. + // some delegates may have been incubated but not added to the map. + for (int i = 0; i < m_pendingMapObjects.size(); ++i) { + auto obj = m_pendingMapObjects.at(i); + if (obj && obj->map() != map) + obj->setMap(map); + } + m_pendingMapObjects.clear(); + } +} + +void QMapObjectView::classBegin() +{ + QQmlContext *ctx = qmlContext(this); + m_delegateModel = new QQmlDelegateModel(ctx, this); + m_delegateModel->classBegin(); + + QQmlInstanceModel *model = m_delegateModel; + connect(model, &QQmlInstanceModel::modelUpdated, this, &QMapObjectView::modelUpdated); + connect(model, &QQmlInstanceModel::createdItem, this, &QMapObjectView::createdItem); + connect(model, &QQmlInstanceModel::destroyingItem, this, &QMapObjectView::destroyingItem); + connect(model, &QQmlInstanceModel::initItem, this, &QMapObjectView::initItem); +} + +void QMapObjectView::componentComplete() +{ + QGeoMapObject::componentComplete(); + if (m_delegate) + m_delegateModel->setDelegate(m_delegate); + if (m_model.isValid()) + m_delegateModel->setModel(m_model); + m_delegateModel->componentComplete(); +} + +QVariant QMapObjectView::model() const +{ + return m_model; +} + +QQmlComponent *QMapObjectView::delegate() const +{ + return m_delegate; +} + +void QMapObjectView::setModel(QVariant model) +{ + if (m_model == model) + return; + m_model = model; + + if (d_ptr->m_componentCompleted) + m_delegateModel->setModel(model); + + emit modelChanged(model); +} + +void QMapObjectView::setDelegate(QQmlComponent *delegate) +{ + if (m_delegate == delegate) + return; + m_delegate = delegate; + + if (d_ptr->m_componentCompleted) + m_delegateModel->setDelegate(delegate); + + emit delegateChanged(delegate); +} + +/*! + \qmlmethod void Qt.labs.location::MapObjectView::addMapObject(MapObject object) + + Adds the given \a object to the MapObjectView (for example MapIconObject, MapRouteObject), and, + indirectly, to the underlying map. If the object already is on the MapObjectView, it will not be added again. + + \sa removeMapObject +*/ +void QMapObjectView::addMapObject(QGeoMapObject *object) +{ + if (m_userAddedMapObjects.indexOf(object) < 0) + m_userAddedMapObjects.append(object); + if (map() && object->map() != map()) + object->setMap(map()); +} + +/*! + \qmlmethod void Qt.labs.location::MapObjectView::removeMapObject(MapObject object) + + Removes the given \a object from the MapObjectView (for example MapIconObject, MapRouteObject), and, + indirectly, from the underlying map. + + \sa addMapObject +*/ +void QMapObjectView::removeMapObject(QGeoMapObject *object) +{ + int idx = m_userAddedMapObjects.indexOf(object); + if ( idx >= 0) { + object->setMap(nullptr); + m_userAddedMapObjects.remove(idx); + } +} + +void QMapObjectView::destroyingItem(QObject * /*object*/) +{ + +} + +void QMapObjectView::initItem(int /*index*/, QObject * /*object*/) +{ + +} + +void QMapObjectView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + // move changes are expressed as one remove + one insert, with the same moveId. + // For simplicity, they will be treated as remove + insert. + // Changes will be also ignored, as they represent only data changes, not layout changes + if (reset) { // Assuming this means "remove everything already instantiated" + flushDelegateModel(); + } else { + // Remove map objects from the back to the front to retain the mapping to what is received from the changesets + const QVector<QQmlChangeSet::Change> &removes = changeSet.removes(); + std::map<int, int> mapRemoves; + for (int i = 0; i < removes.size(); i++) + mapRemoves.insert(std::pair<int, int>(removes.at(i).start(), i)); + + for (auto rit = mapRemoves.rbegin(); rit != mapRemoves.rend(); ++rit) { + const QQmlChangeSet::Change &c = removes.at(rit->second); + for (int idx = c.end() - 1; idx >= c.start(); --idx) + removeMapObjectFromMap(idx); + } + } + + for (const QQmlChangeSet::Change &c: changeSet.inserts()) { + for (int idx = c.start(); idx < c.end(); idx++) { + m_instantiatedMapObjects.insert(idx, nullptr); + QGeoMapObject *mo = qobject_cast<QGeoMapObject *>(m_delegateModel->object(idx, incubationMode)); + if (mo) // if not, a createdItem signal will be emitted. + addMapObjectToMap(mo, idx); + } + } +} + +void QMapObjectView::addMapObjectToMap(QGeoMapObject *object, int index) +{ + if (!object) + return; + + m_instantiatedMapObjects[index] = object; + if (map()) + object->setMap(map()); + else + m_pendingMapObjects << object; + + // ToDo: + // Figure out the proper way to replace "mo->setVisible(visible());". Options: + // - simply leave it to the user to set up a property binding + // - set up a property binding automatically + // - add a viewVisibility member to QGeoMapObject that gets combined at all times, + // and a connection for it. +} + +void QMapObjectView::removeMapObjectFromMap(int index) +{ + if (index >= 0 && index < m_instantiatedMapObjects.size()) { + QGeoMapObject *mo = m_instantiatedMapObjects.takeAt(index); + if (!mo) + return; + mo->setMap(nullptr); + m_delegateModel->release(mo); + } +} + +// See QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incubationMode) doc +// for explanation on when createdItem is emitted. +void QMapObjectView::createdItem(int index, QObject * /*object*/) +{ + if (m_instantiatedMapObjects.at(index)) + return; // The first call to object() apparently returned a valid item. Don't call it again. + + // If here, according to the documentation above, object() should be called again for index, + // or else, it will be destroyed exiting this scope + QGeoMapObject *mo = nullptr; + mo = qobject_cast<QGeoMapObject *>(m_delegateModel->object(index, incubationMode)); + if (mo) + addMapObjectToMap(mo, index); +} + + +void QMapObjectView::flushDelegateModel() +{ + // Backward as removeItemFromMap modifies m_instantiatedItems + for (int i = m_instantiatedMapObjects.size() -1; i >= 0 ; i--) + removeMapObjectFromMap(i); +} + +void QMapObjectView::flushUserAddedMapObjects() +{ + for (int i = 0; i < m_userAddedMapObjects.size(); ++i) { + auto obj = m_userAddedMapObjects.at(i); + if (obj) + obj->setMap(nullptr); // obj parent might not be this. If so, it would not be destroyed by destroying this view. + } +} + +QT_END_NAMESPACE + |