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 "qdeclarativegeomapitembase_p.h" |
38 | #include "qgeocameradata_p.h" |
39 | #include <QtLocation/private/qgeomap_p.h> |
40 | #include <QtQml/QQmlInfo> |
41 | #include <QtQuick/QSGOpacityNode> |
42 | #include <QtQuick/private/qquickmousearea_p.h> |
43 | #include <QtQuick/private/qquickitem_p.h> |
44 | |
45 | QT_BEGIN_NAMESPACE |
46 | |
47 | QGeoMapViewportChangeEvent::QGeoMapViewportChangeEvent() |
48 | : zoomLevelChanged(false), |
49 | centerChanged(false), |
50 | mapSizeChanged(false), |
51 | tiltChanged(false), |
52 | bearingChanged(false), |
53 | rollChanged(false) |
54 | { |
55 | } |
56 | |
57 | QGeoMapViewportChangeEvent::QGeoMapViewportChangeEvent(const QGeoMapViewportChangeEvent &other) |
58 | { |
59 | this->operator=(other); |
60 | } |
61 | |
62 | QGeoMapViewportChangeEvent &QGeoMapViewportChangeEvent::operator=(const QGeoMapViewportChangeEvent &other) |
63 | { |
64 | if (this == &other) |
65 | return (*this); |
66 | |
67 | cameraData = other.cameraData; |
68 | mapSize = other.mapSize; |
69 | zoomLevelChanged = other.zoomLevelChanged; |
70 | centerChanged = other.centerChanged; |
71 | mapSizeChanged = other.mapSizeChanged; |
72 | tiltChanged = other.tiltChanged; |
73 | bearingChanged = other.bearingChanged; |
74 | rollChanged = other.rollChanged; |
75 | |
76 | return (*this); |
77 | } |
78 | |
79 | QDeclarativeGeoMapItemBase::QDeclarativeGeoMapItemBase(QQuickItem *parent) |
80 | : QQuickItem(parent), map_(0), quickMap_(0), parentGroup_(0) |
81 | { |
82 | setFiltersChildMouseEvents(true); |
83 | connect(sender: this, SIGNAL(childrenChanged()), |
84 | receiver: this, SLOT(afterChildrenChanged())); |
85 | // Changing opacity on a mapItemGroup should affect also the opacity on the children. |
86 | // This must be notified to plugins, if they are to render the item. |
87 | connect(sender: this, signal: &QQuickItem::opacityChanged, receiver: this, slot: &QDeclarativeGeoMapItemBase::mapItemOpacityChanged); |
88 | } |
89 | |
90 | QDeclarativeGeoMapItemBase::~QDeclarativeGeoMapItemBase() |
91 | { |
92 | disconnect(receiver: this, SLOT(afterChildrenChanged())); |
93 | if (quickMap_) |
94 | quickMap_->removeMapItem(item: this); |
95 | } |
96 | |
97 | /*! |
98 | \internal |
99 | */ |
100 | void QDeclarativeGeoMapItemBase::afterChildrenChanged() |
101 | { |
102 | QList<QQuickItem *> kids = childItems(); |
103 | if (kids.size() > 0) { |
104 | bool printedWarning = false; |
105 | foreach (QQuickItem *i, kids) { |
106 | if (i->flags() & QQuickItem::ItemHasContents |
107 | && !qobject_cast<QQuickMouseArea *>(object: i)) { |
108 | if (!printedWarning) { |
109 | qmlWarning(me: this) << "Geographic map items do not support child items" ; |
110 | printedWarning = true; |
111 | } |
112 | |
113 | qmlWarning(me: i) << "deleting this child" ; |
114 | i->deleteLater(); |
115 | } |
116 | } |
117 | } |
118 | } |
119 | |
120 | /*! |
121 | \internal |
122 | */ |
123 | void QDeclarativeGeoMapItemBase::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) |
124 | { |
125 | if (quickMap == quickMap_) |
126 | return; |
127 | if (quickMap && quickMap_) |
128 | return; // don't allow association to more than one map |
129 | |
130 | quickMap_ = quickMap; |
131 | map_ = map; |
132 | |
133 | if (map_ && quickMap_) { |
134 | // For performance reasons we're not connecting map_'s and quickMap_'s signals to this. |
135 | // Rather, the handling of cameraDataChanged, visibleAreaChanged, heightChanged and widthChanged is done explicitly in QDeclarativeGeoMap by directly calling methods on the items. |
136 | // See QTBUG-76950 |
137 | lastSize_ = QSizeF(quickMap_->width(), quickMap_->height()); |
138 | lastCameraData_ = map_->cameraData(); |
139 | } |
140 | } |
141 | |
142 | /*! |
143 | \internal |
144 | */ |
145 | void QDeclarativeGeoMapItemBase::baseCameraDataChanged(const QGeoCameraData &cameraData) |
146 | { |
147 | QGeoMapViewportChangeEvent evt; |
148 | evt.cameraData = cameraData; |
149 | evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height()); |
150 | |
151 | if (evt.mapSize != lastSize_) |
152 | evt.mapSizeChanged = true; |
153 | |
154 | if (cameraData.bearing() != lastCameraData_.bearing()) |
155 | evt.bearingChanged = true; |
156 | if (cameraData.center() != lastCameraData_.center()) |
157 | evt.centerChanged = true; |
158 | if (cameraData.roll() != lastCameraData_.roll()) |
159 | evt.rollChanged = true; |
160 | if (cameraData.tilt() != lastCameraData_.tilt()) |
161 | evt.tiltChanged = true; |
162 | if (cameraData.zoomLevel() != lastCameraData_.zoomLevel()) |
163 | evt.zoomLevelChanged = true; |
164 | |
165 | lastSize_ = evt.mapSize; |
166 | lastCameraData_ = cameraData; |
167 | |
168 | afterViewportChanged(event: evt); |
169 | } |
170 | |
171 | void QDeclarativeGeoMapItemBase::visibleAreaChanged() |
172 | { |
173 | QGeoMapViewportChangeEvent evt; |
174 | evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height()); |
175 | afterViewportChanged(event: evt); |
176 | } |
177 | |
178 | /*! |
179 | \internal |
180 | */ |
181 | void QDeclarativeGeoMapItemBase::setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset) |
182 | { |
183 | if (!map_ || !quickMap_) |
184 | return; |
185 | |
186 | QDoubleVector2D pos; |
187 | if (map()->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) { |
188 | const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); |
189 | QDoubleVector2D wrappedProjection = p.geoToWrappedMapProjection(coordinate); |
190 | if (!p.isProjectable(wrappedProjection)) |
191 | return; |
192 | pos = p.wrappedMapProjectionToItemPosition(wrappedProjection); |
193 | } else { |
194 | pos = map()->geoProjection().coordinateToItemPosition(coordinate, clipToViewport: false); |
195 | if (qIsNaN(d: pos.x())) |
196 | return; |
197 | } |
198 | |
199 | QPointF topLeft = pos.toPointF() - offset; |
200 | |
201 | setPosition(topLeft); |
202 | } |
203 | |
204 | bool QDeclarativeGeoMapItemBase::autoFadeIn() const |
205 | { |
206 | return m_autoFadeIn; |
207 | } |
208 | |
209 | static const double opacityRampMin = 1.5; |
210 | static const double opacityRampMax = 2.5; |
211 | |
212 | void QDeclarativeGeoMapItemBase::setAutoFadeIn(bool fadeIn) |
213 | { |
214 | if (fadeIn == m_autoFadeIn) |
215 | return; |
216 | m_autoFadeIn = fadeIn; |
217 | if (quickMap_ && quickMap_->zoomLevel() < opacityRampMax) |
218 | polishAndUpdate(); |
219 | } |
220 | |
221 | int QDeclarativeGeoMapItemBase::lodThreshold() const |
222 | { |
223 | return m_lodThreshold; |
224 | } |
225 | |
226 | void QDeclarativeGeoMapItemBase::setLodThreshold(int lt) |
227 | { |
228 | if (lt == m_lodThreshold) |
229 | return; |
230 | m_lodThreshold = lt; |
231 | update(); |
232 | } |
233 | |
234 | /*! |
235 | \internal |
236 | |
237 | This returns the zoom level to be used when requesting the LOD. |
238 | Essentially it clamps to m_lodThreshold, and if above, it selects |
239 | a ZL higher than the maximum LODable level. |
240 | */ |
241 | unsigned int QDeclarativeGeoMapItemBase::zoomForLOD(int zoom) const |
242 | { |
243 | if (zoom >= m_lodThreshold) |
244 | return 30; // some arbitrarily large zoom |
245 | return uint(zoom); |
246 | } |
247 | |
248 | /*! |
249 | \internal |
250 | */ |
251 | float QDeclarativeGeoMapItemBase::zoomLevelOpacity() const |
252 | { |
253 | if (!m_autoFadeIn) // Consider skipping the opacity node instead. |
254 | return 1.0; |
255 | else if (quickMap_->zoomLevel() > opacityRampMax) |
256 | return 1.0; |
257 | else if (quickMap_->zoomLevel() > opacityRampMin) |
258 | return quickMap_->zoomLevel() - opacityRampMin; |
259 | else |
260 | return 0.0; |
261 | } |
262 | |
263 | bool QDeclarativeGeoMapItemBase::childMouseEventFilter(QQuickItem *item, QEvent *event) |
264 | { |
265 | Q_UNUSED(item); |
266 | if (event->type() == QEvent::MouseButtonPress && !contains(point: static_cast<QMouseEvent*>(event)->pos())) { |
267 | // In case of items that are not rectangles, this filter is used to test if the event has landed |
268 | // inside the actual item shape. |
269 | // If so, the method returns true, meaning that it prevents the event delivery to child "*item" (for example, |
270 | // a mouse area that is on top of this map item). |
271 | // However, this method sets "accepted" to false, so that the event can still be passed further up, |
272 | // specifically to the parent Map, that is a sort of flickable. |
273 | // Otherwise, if the event is not contained within the map item, the method returns false, meaning the event |
274 | // is delivered to the child *item (like the mouse area associated). |
275 | event->setAccepted(false); |
276 | return true; |
277 | } |
278 | return false; |
279 | } |
280 | |
281 | /*! |
282 | \internal |
283 | */ |
284 | QSGNode *QDeclarativeGeoMapItemBase::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *pd) |
285 | { |
286 | if (!map_ || !quickMap_ || map_->supportedMapItemTypes() & itemType()) { |
287 | if (oldNode) |
288 | delete oldNode; |
289 | oldNode = 0; |
290 | return 0; |
291 | } |
292 | |
293 | QSGOpacityNode *opn = static_cast<QSGOpacityNode *>(oldNode); |
294 | if (!opn) |
295 | opn = new QSGOpacityNode(); |
296 | |
297 | opn->setOpacity(zoomLevelOpacity()); |
298 | |
299 | QSGNode *oldN = opn->childCount() ? opn->firstChild() : 0; |
300 | opn->removeAllChildNodes(); |
301 | if (opn->opacity() > 0.0) { |
302 | QSGNode *n = this->updateMapItemPaintNode(oldN, pd); |
303 | if (n) |
304 | opn->appendChildNode(node: n); |
305 | } else { |
306 | delete oldN; |
307 | } |
308 | |
309 | return opn; |
310 | } |
311 | |
312 | /*! |
313 | \internal |
314 | */ |
315 | QSGNode *QDeclarativeGeoMapItemBase::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *) |
316 | { |
317 | delete oldNode; |
318 | return 0; |
319 | } |
320 | |
321 | QGeoMap::ItemType QDeclarativeGeoMapItemBase::itemType() const |
322 | { |
323 | return m_itemType; |
324 | } |
325 | |
326 | /*! |
327 | \internal |
328 | |
329 | The actual combined opacity of the item. Needed by custom renderer to look like |
330 | the scene-graph one. |
331 | */ |
332 | qreal QDeclarativeGeoMapItemBase::mapItemOpacity() const |
333 | { |
334 | if (parentGroup_) |
335 | return parentGroup_->mapItemOpacity() * opacity(); |
336 | return opacity(); |
337 | } |
338 | |
339 | void QDeclarativeGeoMapItemBase::setParentGroup(QDeclarativeGeoMapItemGroup &parentGroup) |
340 | { |
341 | parentGroup_ = &parentGroup; |
342 | if (parentGroup_) { |
343 | connect(sender: parentGroup_, signal: &QDeclarativeGeoMapItemGroup::mapItemOpacityChanged, |
344 | receiver: this, slot: &QDeclarativeGeoMapItemBase::mapItemOpacityChanged); |
345 | } |
346 | } |
347 | |
348 | bool QDeclarativeGeoMapItemBase::isPolishScheduled() const |
349 | { |
350 | return QQuickItemPrivate::get(item: this)->polishScheduled; |
351 | } |
352 | |
353 | void QDeclarativeGeoMapItemBase::setMaterialDirty() {} |
354 | |
355 | void QDeclarativeGeoMapItemBase::polishAndUpdate() |
356 | { |
357 | polish(); |
358 | update(); |
359 | } |
360 | |
361 | QT_END_NAMESPACE |
362 | |