1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtLocation module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36
37 #include "qmapobjectview_p.h"
38 #include "qmapobjectview_p_p.h"
39 #include <private/qqmldelegatemodel_p.h>
40 #include <QtLocation/private/qgeomap_p.h>
41
42 QT_BEGIN_NAMESPACE
43
44 /*!
45 \qmltype MapObjectView
46 \instantiates QMapObjectView
47 \inqmlmodule Qt.labs.location
48 \ingroup qml-QtLocation5-maps
49 \inherits QGeoMapObject
50
51 \brief The MapObjectView is used to populate Map with map objects from a model.
52
53 The MapObjectView is used to populate Map with map objects, either from a model or via
54 \l addMapObject or \l removeMapObject.
55
56 The MapObjectView type only makes sense when contained in a Map, meaning that it will not work when added inside
57 other QML elements.
58 This can also be intended as an object layer on top of a Map.
59 */
60
61 /*
62
63 QMapObjectViewPrivate
64
65 */
66
67 static const QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::Asynchronous;
68
QMapObjectViewPrivate(QGeoMapObject * q)69 QMapObjectViewPrivate::QMapObjectViewPrivate(QGeoMapObject *q)
70 : QGeoMapObjectPrivate(q)
71 {
72 }
73
~QMapObjectViewPrivate()74 QMapObjectViewPrivate::~QMapObjectViewPrivate()
75 {
76
77 }
78
type() const79 QGeoMapObject::Type QMapObjectViewPrivate::type() const
80 {
81 return QGeoMapObject::ViewType;
82 }
83
84
85 /*
86
87 QMapObjectViewPrivateDefault
88
89 */
90
91
QMapObjectViewPrivateDefault(const QMapObjectViewPrivate & other)92 QMapObjectViewPrivateDefault::QMapObjectViewPrivateDefault(const QMapObjectViewPrivate &other)
93 : QMapObjectViewPrivate(other.q), m_model(other.model()), m_delegate(other.delegate())
94 {
95 }
96
~QMapObjectViewPrivateDefault()97 QMapObjectViewPrivateDefault::~QMapObjectViewPrivateDefault()
98 {
99
100 }
101
model() const102 QVariant QMapObjectViewPrivateDefault::model() const
103 {
104 return m_model;
105 }
106
setModel(const QVariant & model)107 void QMapObjectViewPrivateDefault::setModel(const QVariant &model)
108 {
109 m_model = model;
110 }
111
delegate() const112 QQmlComponent *QMapObjectViewPrivateDefault::delegate() const
113 {
114 return m_delegate;
115 }
116
setDelegate(QQmlComponent * delegate)117 void QMapObjectViewPrivateDefault::setDelegate(QQmlComponent *delegate)
118 {
119 m_delegate = delegate;
120 }
121
QMapObjectViewPrivateDefault(QGeoMapObject * q)122 QMapObjectViewPrivateDefault::QMapObjectViewPrivateDefault(QGeoMapObject *q) : QMapObjectViewPrivate(q)
123 {
124
125 }
126
clone()127 QGeoMapObjectPrivate *QMapObjectViewPrivateDefault::clone()
128 {
129 return new QMapObjectViewPrivateDefault(*this);
130 }
131
equals(const QGeoMapObjectPrivate & other) const132 bool QMapObjectViewPrivateDefault::equals(const QGeoMapObjectPrivate &other) const
133 {
134 if (other.type() != type())
135 return false;
136
137 const QMapObjectViewPrivate &o = static_cast<const QMapObjectViewPrivate &>(other);
138 return (QGeoMapObjectPrivate::equals(o)
139 && model() == o.model()
140 && delegate() == o.delegate());
141 }
142
geoShape() const143 QGeoShape QMapObjectViewPrivateDefault::geoShape() const
144 {
145 const QMapObjectView *qq = static_cast<const QMapObjectView *>(q);
146 QGeoRectangle rect;
147 const QList<QGeoMapObject *> kids = qq->geoMapObjectChildren();
148 for (const auto &kid: kids) {
149 if (!rect.isValid())
150 rect = kid->geoShape().boundingGeoRectangle();
151 else
152 rect = rect.united(kid->geoShape().boundingGeoRectangle());
153 }
154 return rect;
155 }
156
setGeoShape(const QGeoShape &)157 void QMapObjectViewPrivateDefault::setGeoShape(const QGeoShape &/*shape*/)
158 {
159 // MOV doesn't support setting the geoshape.
160 }
161
162 /*
163
164 QMapObjectView
165
166 */
167
168
QMapObjectView(QObject * parent)169 QMapObjectView::QMapObjectView(QObject *parent)
170 : QGeoMapObject(QExplicitlySharedDataPointer<QGeoMapObjectPrivate>(new QMapObjectViewPrivateDefault(this)), parent)
171 {
172
173 }
174
~QMapObjectView()175 QMapObjectView::~QMapObjectView()
176 {
177 flushDelegateModel();
178 flushUserAddedMapObjects();
179 }
180
geoMapObjectChildren() const181 QList<QGeoMapObject *> QMapObjectView::geoMapObjectChildren() const
182 {
183 auto kids = QGeoMapObject::geoMapObjectChildren();
184 auto size = m_instantiatedMapObjects.count();
185 for (int i = 0; i < size; ++i) {
186 auto obj = qobject_cast<QGeoMapObject*>(m_instantiatedMapObjects[i]);
187 if (obj)
188 kids << obj;
189 }
190 for (int i = 0; i < m_userAddedMapObjects.size(); ++i) {
191 auto obj = m_userAddedMapObjects.at(i);
192 if (obj)
193 kids << obj;
194 }
195 return kids;
196 }
197
classBegin()198 void QMapObjectView::classBegin()
199 {
200 QQmlContext *ctx = qmlContext(this);
201 m_delegateModel = new QQmlDelegateModel(ctx, this);
202 m_delegateModel->classBegin();
203
204 QQmlInstanceModel *model = m_delegateModel;
205 connect(model, &QQmlInstanceModel::modelUpdated, this, &QMapObjectView::modelUpdated);
206 connect(model, &QQmlInstanceModel::createdItem, this, &QMapObjectView::createdItem);
207 // connect(model, &QQmlInstanceModel::destroyingItem, this, &QMapObjectView::destroyingItem);
208 // connect(model, &QQmlInstanceModel::initItem, this, &QMapObjectView::initItem);
209 }
210
componentComplete()211 void QMapObjectView::componentComplete()
212 {
213 QGeoMapObject::componentComplete();
214 QMapObjectViewPrivate *d = static_cast<QMapObjectViewPrivate *>(d_ptr.data());
215 if (d->delegate())
216 m_delegateModel->setDelegate(d->delegate());
217 if (d->model().isValid())
218 m_delegateModel->setModel(d->model());
219 m_delegateModel->componentComplete();
220 }
221
222 /*!
223 \qmlproperty Variant Qt.labs.location::MapObjectView::model
224
225 This property holds the model that provides data used for creating the map items defined by the
226 delegate. Only QAbstractItemModel based models are supported.
227 */
model() const228 QVariant QMapObjectView::model() const
229 {
230 const QMapObjectViewPrivate *d = static_cast<const QMapObjectViewPrivate *>(d_ptr.data());
231 return d->model();
232 }
233
234 /*!
235 \qmlproperty Component Qt.labs.location::MapObjectView::delegate
236
237 This property holds the delegate which defines how each item in the
238 model should be displayed. The Component must contain exactly one
239 QGeoMapObject -derived object as the root object.
240 */
delegate() const241 QQmlComponent *QMapObjectView::delegate() const
242 {
243 const QMapObjectViewPrivate *d = static_cast<const QMapObjectViewPrivate *>(d_ptr.data());
244 return d->delegate();
245 }
246
setModel(QVariant model)247 void QMapObjectView::setModel(QVariant model)
248 {
249 QMapObjectViewPrivate *d = static_cast<QMapObjectViewPrivate *>(d_ptr.data());
250 if (d->model() == model)
251 return;
252 d->setModel(model);
253
254 if (d_ptr->m_componentCompleted)
255 m_delegateModel->setModel(model);
256
257 emit modelChanged(model);
258 }
259
setDelegate(QQmlComponent * delegate)260 void QMapObjectView::setDelegate(QQmlComponent *delegate)
261 {
262 QMapObjectViewPrivate *d = static_cast<QMapObjectViewPrivate *>(d_ptr.data());
263 if (d->delegate() == delegate)
264 return;
265 d->setDelegate(delegate);
266
267 if (d_ptr->m_componentCompleted)
268 m_delegateModel->setDelegate(delegate);
269
270 emit delegateChanged(delegate);
271 }
272
273 /*!
274 \qmlmethod void Qt.labs.location::MapObjectView::addMapObject(MapObject object)
275
276 Adds the given \a object to the MapObjectView (for example MapIconObject, MapRouteObject), and,
277 indirectly, to the underlying map. If the object already is on the MapObjectView, it will not be added again.
278
279 \sa removeMapObject
280 */
addMapObject(QGeoMapObject * object)281 void QMapObjectView::addMapObject(QGeoMapObject *object)
282 {
283 if (m_userAddedMapObjects.indexOf(object) < 0)
284 m_userAddedMapObjects.append(object);
285 if (map() && object->map() != map())
286 object->setMap(map());
287 }
288
289 /*!
290 \qmlmethod void Qt.labs.location::MapObjectView::removeMapObject(MapObject object)
291
292 Removes the given \a object from the MapObjectView (for example MapIconObject, MapRouteObject), and,
293 indirectly, from the underlying map.
294
295 \sa addMapObject
296 */
removeMapObject(QGeoMapObject * object)297 void QMapObjectView::removeMapObject(QGeoMapObject *object)
298 {
299 int idx = m_userAddedMapObjects.indexOf(object);
300 if ( idx >= 0) {
301 object->setMap(nullptr);
302 m_userAddedMapObjects.remove(idx);
303 }
304 }
305
destroyingItem(QObject *)306 void QMapObjectView::destroyingItem(QObject * /*object*/)
307 {
308
309 }
310
initItem(int,QObject *)311 void QMapObjectView::initItem(int /*index*/, QObject * /*object*/)
312 {
313
314 }
315
modelUpdated(const QQmlChangeSet & changeSet,bool reset)316 void QMapObjectView::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
317 {
318 // move changes are expressed as one remove + one insert, with the same moveId.
319 // For simplicity, they will be treated as remove + insert.
320 // Changes will be also ignored, as they represent only data changes, not layout changes
321 if (reset) { // Assuming this means "remove everything already instantiated"
322 flushDelegateModel();
323 } else {
324 // Remove map objects from the back to the front to retain the mapping to what is received from the changesets
325 const QVector<QQmlChangeSet::Change> &removes = changeSet.removes();
326 std::map<int, int> mapRemoves;
327 for (int i = 0; i < removes.size(); i++)
328 mapRemoves.insert(std::pair<int, int>(removes.at(i).start(), i));
329
330 for (auto rit = mapRemoves.rbegin(); rit != mapRemoves.rend(); ++rit) {
331 const QQmlChangeSet::Change &c = removes.at(rit->second);
332 for (int idx = c.end() - 1; idx >= c.start(); --idx)
333 removeMapObjectFromMap(idx);
334 }
335 }
336
337 QBoolBlocker createBlocker(m_creatingObject, true);
338 for (const QQmlChangeSet::Change &c: changeSet.inserts()) {
339 for (int idx = c.start(); idx < c.end(); idx++) {
340 m_instantiatedMapObjects.insert(idx, nullptr);
341 QGeoMapObject *mo = qobject_cast<QGeoMapObject *>(m_delegateModel->object(idx, incubationMode));
342 if (mo) {// if not, a createdItem signal will be emitted later, else it has been emitted already while createBlocker is in effect.
343 mo->setParent(this);
344 addMapObjectToMap(mo, idx);
345 }
346 }
347 }
348 }
349
addMapObjectToMap(QGeoMapObject * object,int index)350 void QMapObjectView::addMapObjectToMap(QGeoMapObject *object, int index)
351 {
352 if (!object)
353 return;
354
355 m_instantiatedMapObjects[index] = object;
356 if (map())
357 object->setMap(map());
358 else
359 m_pendingMapObjects << object;
360
361 // ToDo:
362 // Figure out the proper way to replace "mo->setVisible(visible());". Options:
363 // - simply leave it to the user to set up a property binding
364 // - set up a property binding automatically
365 // - add a viewVisibility member to QGeoMapObject that gets combined at all times,
366 // and a connection for it.
367 }
368
removeMapObjectFromMap(int index)369 void QMapObjectView::removeMapObjectFromMap(int index)
370 {
371 if (index >= 0 && index < m_instantiatedMapObjects.size()) {
372 QGeoMapObject *mo = m_instantiatedMapObjects.takeAt(index);
373 if (!mo)
374 return;
375
376 mo->setMap(nullptr);
377 QQmlInstanceModel::ReleaseFlags releaseStatus = m_delegateModel->release(mo);
378 #ifdef QT_DEBUG
379 if (releaseStatus == QQmlInstanceModel::Referenced)
380 qWarning() << "object "<<mo<<" still referenced";
381 #else
382 Q_UNUSED(releaseStatus)
383 #endif
384 }
385 }
386
387 // See QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incubationMode) doc
388 // for explanation on when createdItem is emitted.
createdItem(int index,QObject *)389 void QMapObjectView::createdItem(int index, QObject * /*object*/)
390 {
391 if (m_creatingObject) {
392 // see QDeclarativeGeoMapItemView::createdItem
393 return;
394 }
395
396 // If here, according to the documentation above, object() should be called again for index,
397 // or else, it will be destroyed exiting this scope
398 QGeoMapObject *mo = nullptr;
399 mo = qobject_cast<QGeoMapObject *>(m_delegateModel->object(index, incubationMode));
400 if (mo) {
401 mo->setParent(this);
402 addMapObjectToMap(mo, index);
403 } else {
404 qWarning() << "QQmlDelegateModel::object called in createdItem for " << index << " produced a null object";
405 }
406 }
407
408
flushDelegateModel()409 void QMapObjectView::flushDelegateModel()
410 {
411 // Backward as removeItemFromMap modifies m_instantiatedItems
412 for (int i = m_instantiatedMapObjects.size() -1; i >= 0 ; i--)
413 removeMapObjectFromMap(i);
414 }
415
flushUserAddedMapObjects()416 void QMapObjectView::flushUserAddedMapObjects()
417 {
418 for (int i = 0; i < m_userAddedMapObjects.size(); ++i) {
419 auto obj = m_userAddedMapObjects.at(i);
420 if (obj)
421 obj->setMap(nullptr); // obj parent might not be this. If so, it would not be destroyed by destroying this view.
422 }
423 }
424
setMap(QGeoMap * map)425 void QMapObjectView::setMap(QGeoMap *map)
426 {
427 QMapObjectViewPrivate *d = static_cast<QMapObjectViewPrivate *>(d_ptr.data());
428 if (d->m_map == map)
429 return;
430
431 QGeoMapObject::setMap(map); // This is where the specialized pimpl gets created and injected
432
433 for (int i = 0; i < m_userAddedMapObjects.size(); ++i) {
434 auto obj = m_userAddedMapObjects.at(i);
435 if (obj && obj->map() != map)
436 obj->setMap(map);
437 }
438
439 if (!map) {
440 // Map was set, now it has ben re-set to NULL
441 flushDelegateModel();
442 flushUserAddedMapObjects();
443 bool oldVisible = d_ptr->m_visible;
444 bool oldCmponentCompleted = d_ptr->m_componentCompleted;
445 d_ptr = new QMapObjectViewPrivateDefault(*d);
446 d_ptr->m_componentCompleted = oldCmponentCompleted;
447 d_ptr->setVisible(oldVisible);
448 } else if (d->m_componentCompleted) {
449 // Map was null, now it's set AND delegateModel is already complete.
450 // some delegates may have been incubated but not added to the map.
451 for (int i = 0; i < m_pendingMapObjects.size(); ++i) {
452 auto obj = m_pendingMapObjects.at(i);
453 if (obj && obj->map() != map)
454 obj->setMap(map);
455 }
456 m_pendingMapObjects.clear();
457 }
458 }
459
460 QT_END_NAMESPACE
461
462