1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Aaron McCarthy <mccarthy.aaron@gmail.com> |
4 | ** Copyright (C) 2015 The Qt Company Ltd. |
5 | ** Contact: http://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the QtLocation module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL3$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see http://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at http://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or later as published by the Free |
29 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
30 | ** the packaging of this file. Please review the following information to |
31 | ** ensure the GNU General Public License version 2.0 requirements will be |
32 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
33 | ** |
34 | ** $QT_END_LICENSE$ |
35 | ** |
36 | ****************************************************************************/ |
37 | |
38 | #include "qdeclarativegeomapcopyrightsnotice_p.h" |
39 | |
40 | #include <QtGui/QTextDocument> |
41 | #include <QtGui/QAbstractTextDocumentLayout> |
42 | #include <QtGui/QPainter> |
43 | #include <QtQuick/private/qquickanchors_p.h> |
44 | #include <QtQuick/private/qquickanchors_p_p.h> |
45 | #include <QtLocation/private/qdeclarativegeomap_p.h> |
46 | #include <QtQuick/private/qquickpainteditem_p.h> |
47 | |
48 | QT_BEGIN_NAMESPACE |
49 | |
50 | class QDeclarativeGeoMapCopyrightNoticePrivate: public QQuickPaintedItemPrivate |
51 | { |
52 | Q_DECLARE_PUBLIC(QDeclarativeGeoMapCopyrightNotice) |
53 | public: |
54 | virtual void setVisible(bool visible); |
55 | }; |
56 | |
57 | /*! |
58 | \qmltype MapCopyrightNotice |
59 | \instantiates QDeclarativeGeoMapCopyrightNotice |
60 | \inqmlmodule QtLocation |
61 | \ingroup qml-QtLocation5-maps |
62 | \since QtLocation 5.9 |
63 | |
64 | \brief The MapCopyrightNotice item displays the current valid |
65 | copyright notice for a Map element. |
66 | |
67 | This object can be used to place an additional copyright notices |
68 | programmatically. |
69 | |
70 | Note that declaring a MapCopyrightNotice inside a QtLocation::Map element |
71 | is not possible, like for any other QQuickItem. |
72 | |
73 | The release of this API with Qt 5.9 is a Technology Preview. |
74 | */ |
75 | |
76 | /*! |
77 | \qmlproperty Map QtLocation::MapCopyrightNotice::mapSource |
78 | |
79 | This property holds the current map source providing the copyright data shown |
80 | in this notice. |
81 | In order to let the MapCopyrightNotice display a copyright, this property must |
82 | be set, as it is the only data source for this element. |
83 | */ |
84 | |
85 | /*! |
86 | \qmlproperty string QtLocation::MapCopyrightNotice::styleSheet |
87 | |
88 | This property holds the current css2.1 style sheet used to style the copyright notice, if in HTML form. |
89 | |
90 | Example: |
91 | \code |
92 | MapCopyrightNotice { |
93 | mapSource: myMap |
94 | styleSheet: "body { color : green; font-family: \"Lucida\"; font-size: 8px} a{ font-size: 8px; color:#A62900}" |
95 | } |
96 | \endcode |
97 | */ |
98 | |
99 | QDeclarativeGeoMapCopyrightNotice::QDeclarativeGeoMapCopyrightNotice(QQuickItem *parent) |
100 | : QQuickPaintedItem(parent), m_copyrightsHtml(0), m_copyrightsVisible(true), m_mapSource(0), |
101 | m_userDefinedStyleSheet(false) |
102 | { |
103 | // If this item is constructed inside the map, automatically anchor it where it always used to be. |
104 | if (qobject_cast<QDeclarativeGeoMap *>(object: parent)) |
105 | anchorToBottomLeft(); |
106 | } |
107 | |
108 | QDeclarativeGeoMapCopyrightNotice::~QDeclarativeGeoMapCopyrightNotice() |
109 | { |
110 | setMapSource(nullptr); |
111 | } |
112 | |
113 | void QDeclarativeGeoMapCopyrightNotice::anchorToBottomLeft() |
114 | { |
115 | if (!parent()) |
116 | return; |
117 | QQuickAnchors *anchors = property(name: "anchors" ).value<QQuickAnchors *>(); |
118 | if (anchors) { |
119 | anchors->setLeft(QQuickAnchorLine(qobject_cast<QQuickItem *>(object: parent()), QQuickAnchors::LeftAnchor)); |
120 | anchors->setBottom(QQuickAnchorLine(qobject_cast<QQuickItem *>(object: parent()), QQuickAnchors::BottomAnchor)); |
121 | } |
122 | } |
123 | |
124 | void QDeclarativeGeoMapCopyrightNotice::setMapSource(QDeclarativeGeoMap *map) |
125 | { |
126 | if (m_mapSource == map) |
127 | return; |
128 | |
129 | if (m_mapSource) { |
130 | // disconnect this object from current map source |
131 | m_mapSource->detachCopyrightNotice(currentVisibility: copyrightsVisible()); |
132 | m_mapSource->disconnect(receiver: this); |
133 | m_mapSource->m_map->disconnect(receiver: this); |
134 | if (m_copyrightsHtml) |
135 | m_copyrightsHtml->clear(); |
136 | m_copyrightsImage = QImage(); |
137 | m_mapSource = nullptr; |
138 | } |
139 | |
140 | if (map) { |
141 | m_mapSource = map; |
142 | m_mapSource->attachCopyrightNotice(initialVisibility: copyrightsVisible()); |
143 | connect(sender: this, signal: &QDeclarativeGeoMapCopyrightNotice::copyrightsVisibleChanged, |
144 | receiver: mapSource(), slot: &QDeclarativeGeoMap::onAttachedCopyrightNoticeVisibilityChanged); |
145 | |
146 | // First update the copyright. Only Image will do here, no need to store HTML right away. |
147 | if (m_mapSource->m_copyrights && !m_mapSource->m_copyrights->m_copyrightsImage.isNull()) |
148 | m_copyrightsImage = m_mapSource->m_copyrights->m_copyrightsImage; |
149 | |
150 | connect(sender: mapSource(), SIGNAL(copyrightsChanged(QImage)), |
151 | receiver: this, SLOT(copyrightsChanged(QImage))); |
152 | connect(sender: mapSource(), SIGNAL(copyrightsChanged(QString)), |
153 | receiver: this, SLOT(copyrightsChanged(QString))); |
154 | |
155 | if (m_mapSource->m_map) |
156 | connectMap(); |
157 | else |
158 | connect(sender: mapSource(), signal: &QDeclarativeGeoMap::mapReadyChanged, receiver: this, slot: &QDeclarativeGeoMapCopyrightNotice::connectMap); |
159 | } |
160 | } |
161 | |
162 | void QDeclarativeGeoMapCopyrightNotice::connectMap() |
163 | { |
164 | connect(sender: m_mapSource->m_map, SIGNAL(copyrightsStyleSheetChanged(QString)), |
165 | receiver: this, SLOT(onCopyrightsStyleSheetChanged(QString))); |
166 | connect(sender: this, SIGNAL(linkActivated(QString)), |
167 | receiver: mapSource(), SIGNAL(copyrightLinkActivated(QString))); |
168 | |
169 | onCopyrightsStyleSheetChanged(styleSheet: m_mapSource->m_map->copyrightsStyleSheet()); |
170 | |
171 | update(); |
172 | emit mapSourceChanged(); |
173 | } |
174 | |
175 | QDeclarativeGeoMap *QDeclarativeGeoMapCopyrightNotice::mapSource() |
176 | { |
177 | return m_mapSource.data(); |
178 | } |
179 | |
180 | QString QDeclarativeGeoMapCopyrightNotice::styleSheet() const |
181 | { |
182 | return m_styleSheet; |
183 | } |
184 | |
185 | void QDeclarativeGeoMapCopyrightNotice::setStyleSheet(const QString &styleSheet) |
186 | { |
187 | m_userDefinedStyleSheet = true; |
188 | |
189 | if (styleSheet == m_styleSheet) |
190 | return; |
191 | |
192 | m_styleSheet = styleSheet; |
193 | if (!m_html.isEmpty() && m_copyrightsHtml) { |
194 | delete m_copyrightsHtml; |
195 | createCopyright(); |
196 | #if QT_CONFIG(texthtmlparser) |
197 | m_copyrightsHtml->setHtml(m_html); |
198 | #else |
199 | m_copyrightsHtml->setPlainText(m_html); |
200 | #endif |
201 | } |
202 | rasterizeHtmlAndUpdate(); |
203 | emit styleSheetChanged(styleSheet: m_styleSheet); |
204 | } |
205 | |
206 | /*! |
207 | \internal |
208 | */ |
209 | void QDeclarativeGeoMapCopyrightNotice::paint(QPainter *painter) |
210 | { |
211 | painter->drawImage(x: 0, y: 0, image: m_copyrightsImage); |
212 | } |
213 | |
214 | void QDeclarativeGeoMapCopyrightNotice::mousePressEvent(QMouseEvent *event) |
215 | { |
216 | if (m_copyrightsHtml) { |
217 | m_activeAnchor = m_copyrightsHtml->documentLayout()->anchorAt(pos: event->pos()); |
218 | if (!m_activeAnchor.isEmpty()) |
219 | return; |
220 | } |
221 | |
222 | QQuickPaintedItem::mousePressEvent(event); |
223 | } |
224 | |
225 | void QDeclarativeGeoMapCopyrightNotice::mouseReleaseEvent(QMouseEvent *event) |
226 | { |
227 | if (m_copyrightsHtml) { |
228 | QString anchor = m_copyrightsHtml->documentLayout()->anchorAt(pos: event->pos()); |
229 | if (anchor == m_activeAnchor && !anchor.isEmpty()) { |
230 | emit linkActivated(link: anchor); |
231 | m_activeAnchor.clear(); |
232 | } |
233 | } |
234 | } |
235 | |
236 | void QDeclarativeGeoMapCopyrightNotice::rasterizeHtmlAndUpdate() |
237 | { |
238 | if (!m_copyrightsHtml || m_copyrightsHtml->isEmpty()) |
239 | return; |
240 | |
241 | m_copyrightsImage = QImage(m_copyrightsHtml->size().toSize(), |
242 | QImage::Format_ARGB32_Premultiplied); |
243 | |
244 | m_copyrightsImage.fill(pixel: qPremultiply(x: QColor(Qt::transparent).rgba())); |
245 | QPainter painter(&m_copyrightsImage); |
246 | QAbstractTextDocumentLayout::PaintContext ctx; |
247 | ctx.palette.setColor(acr: QPalette::Text, QStringLiteral("black" )); |
248 | m_copyrightsHtml->documentLayout()->draw(painter: &painter, context: ctx); |
249 | |
250 | setImplicitSize(m_copyrightsImage.width(), m_copyrightsImage.height()); |
251 | setContentsSize(m_copyrightsImage.size()); |
252 | |
253 | setKeepMouseGrab(true); |
254 | setAcceptedMouseButtons(Qt::LeftButton); |
255 | |
256 | update(); |
257 | } |
258 | |
259 | void QDeclarativeGeoMapCopyrightNotice::createCopyright() |
260 | { |
261 | m_copyrightsHtml = new QTextDocument(this); |
262 | #if QT_CONFIG(cssparser) |
263 | if (!m_styleSheet.isEmpty()) |
264 | m_copyrightsHtml->setDefaultStyleSheet(m_styleSheet); |
265 | #endif |
266 | |
267 | // The default 4 makes the copyright too wide and tall. |
268 | m_copyrightsHtml->setDocumentMargin(0); |
269 | } |
270 | |
271 | void QDeclarativeGeoMapCopyrightNoticePrivate::setVisible(bool visible) |
272 | { |
273 | Q_Q(QDeclarativeGeoMapCopyrightNotice); |
274 | q->m_copyrightsVisible = visible; |
275 | QQuickItemPrivate::setVisible(visible); |
276 | } |
277 | |
278 | /*! |
279 | \internal |
280 | */ |
281 | void QDeclarativeGeoMapCopyrightNotice::setCopyrightsVisible(bool visible) |
282 | { |
283 | Q_D(QDeclarativeGeoMapCopyrightNotice); |
284 | if (visible == m_copyrightsVisible) |
285 | return; |
286 | |
287 | m_copyrightsVisible = visible; |
288 | d->QQuickItemPrivate::setVisible(!m_copyrightsImage.isNull() && visible); |
289 | emit copyrightsVisibleChanged(); |
290 | } |
291 | |
292 | bool QDeclarativeGeoMapCopyrightNotice::copyrightsVisible() const |
293 | { |
294 | return m_copyrightsVisible; |
295 | } |
296 | |
297 | /*! |
298 | \internal |
299 | */ |
300 | void QDeclarativeGeoMapCopyrightNotice::setCopyrightsZ(qreal copyrightsZ) |
301 | { |
302 | setZ(copyrightsZ); |
303 | update(); |
304 | } |
305 | |
306 | /*! |
307 | \internal |
308 | */ |
309 | void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QImage ©rightsImage) |
310 | { |
311 | Q_D(QDeclarativeGeoMapCopyrightNotice); |
312 | delete m_copyrightsHtml; |
313 | m_copyrightsHtml = 0; |
314 | |
315 | m_copyrightsImage = copyrightsImage; |
316 | |
317 | setImplicitSize(m_copyrightsImage.width(), m_copyrightsImage.height()); |
318 | |
319 | setKeepMouseGrab(false); |
320 | setAcceptedMouseButtons(Qt::NoButton); |
321 | d->QQuickItemPrivate::setVisible(m_copyrightsVisible && !m_copyrightsImage.isNull()); |
322 | |
323 | update(); |
324 | } |
325 | |
326 | void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QString ©rightsHtml) |
327 | { |
328 | Q_D(QDeclarativeGeoMapCopyrightNotice); |
329 | if (copyrightsHtml.isEmpty()) { |
330 | d->QQuickItemPrivate::setVisible(false); |
331 | return; |
332 | } else { |
333 | d->QQuickItemPrivate::setVisible(m_copyrightsVisible); |
334 | } |
335 | |
336 | // Divfy, so we can style the background. The extra <span> is a |
337 | // workaround to QTBUG-58838 and should be removed when it gets fixed. |
338 | #if QT_CONFIG(texthtmlparser) |
339 | m_html = QStringLiteral("<div id='copyright-root'><span>" ) + copyrightsHtml + QStringLiteral("</span></div>" ); |
340 | #else |
341 | m_html = copyrightsHtml; |
342 | #endif |
343 | |
344 | if (!m_copyrightsHtml) |
345 | createCopyright(); |
346 | |
347 | #if QT_CONFIG(texthtmlparser) |
348 | m_copyrightsHtml->setHtml(m_html); |
349 | #else |
350 | m_copyrightsHtml->setPlainText(m_html); |
351 | #endif |
352 | rasterizeHtmlAndUpdate(); |
353 | } |
354 | |
355 | void QDeclarativeGeoMapCopyrightNotice::onCopyrightsStyleSheetChanged(const QString &styleSheet) |
356 | { |
357 | if (m_userDefinedStyleSheet || styleSheet == m_styleSheet) |
358 | return; |
359 | |
360 | m_styleSheet = styleSheet; |
361 | if (!m_html.isEmpty() && m_copyrightsHtml) { |
362 | delete m_copyrightsHtml; |
363 | createCopyright(); |
364 | #if QT_CONFIG(texthtmlparser) |
365 | m_copyrightsHtml->setHtml(m_html); |
366 | #else |
367 | m_copyrightsHtml->setPlainText(m_html); |
368 | #endif |
369 | } |
370 | rasterizeHtmlAndUpdate(); |
371 | emit styleSheetChanged(styleSheet: m_styleSheet); |
372 | } |
373 | |
374 | QT_END_NAMESPACE |
375 | |