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 
QDeclarativeGeoMapCopyrightNotice(QQuickItem * parent)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 *>(parent))
105         anchorToBottomLeft();
106 }
107 
~QDeclarativeGeoMapCopyrightNotice()108 QDeclarativeGeoMapCopyrightNotice::~QDeclarativeGeoMapCopyrightNotice()
109 {
110     setMapSource(nullptr);
111 }
112 
anchorToBottomLeft()113 void QDeclarativeGeoMapCopyrightNotice::anchorToBottomLeft()
114 {
115     if (!parent())
116         return;
117     QQuickAnchors *anchors = property("anchors").value<QQuickAnchors *>();
118     if (anchors) {
119         anchors->setLeft(QQuickAnchorLine(qobject_cast<QQuickItem *>(parent()), QQuickAnchors::LeftAnchor));
120         anchors->setBottom(QQuickAnchorLine(qobject_cast<QQuickItem *>(parent()), QQuickAnchors::BottomAnchor));
121     }
122 }
123 
setMapSource(QDeclarativeGeoMap * map)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(copyrightsVisible());
132         m_mapSource->disconnect(this);
133         m_mapSource->m_map->disconnect(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(copyrightsVisible());
143         connect(this, &QDeclarativeGeoMapCopyrightNotice::copyrightsVisibleChanged,
144                 mapSource(), &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(mapSource(), SIGNAL(copyrightsChanged(QImage)),
151                 this, SLOT(copyrightsChanged(QImage)));
152         connect(mapSource(), SIGNAL(copyrightsChanged(QString)),
153                 this, SLOT(copyrightsChanged(QString)));
154 
155         if (m_mapSource->m_map)
156             connectMap();
157         else
158             connect(mapSource(), &QDeclarativeGeoMap::mapReadyChanged, this, &QDeclarativeGeoMapCopyrightNotice::connectMap);
159     }
160 }
161 
connectMap()162 void QDeclarativeGeoMapCopyrightNotice::connectMap()
163 {
164     connect(m_mapSource->m_map, SIGNAL(copyrightsStyleSheetChanged(QString)),
165             this, SLOT(onCopyrightsStyleSheetChanged(QString)));
166     connect(this, SIGNAL(linkActivated(QString)),
167             mapSource(), SIGNAL(copyrightLinkActivated(QString)));
168 
169     onCopyrightsStyleSheetChanged(m_mapSource->m_map->copyrightsStyleSheet());
170 
171     update();
172     emit mapSourceChanged();
173 }
174 
mapSource()175 QDeclarativeGeoMap *QDeclarativeGeoMapCopyrightNotice::mapSource()
176 {
177     return m_mapSource.data();
178 }
179 
styleSheet() const180 QString QDeclarativeGeoMapCopyrightNotice::styleSheet() const
181 {
182     return m_styleSheet;
183 }
184 
setStyleSheet(const QString & styleSheet)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(m_styleSheet);
204 }
205 
206 /*!
207     \internal
208 */
paint(QPainter * painter)209 void QDeclarativeGeoMapCopyrightNotice::paint(QPainter *painter)
210 {
211     painter->drawImage(0, 0, m_copyrightsImage);
212 }
213 
mousePressEvent(QMouseEvent * event)214 void QDeclarativeGeoMapCopyrightNotice::mousePressEvent(QMouseEvent *event)
215 {
216     if (m_copyrightsHtml) {
217         m_activeAnchor = m_copyrightsHtml->documentLayout()->anchorAt(event->pos());
218         if (!m_activeAnchor.isEmpty())
219             return;
220     }
221 
222     QQuickPaintedItem::mousePressEvent(event);
223 }
224 
mouseReleaseEvent(QMouseEvent * event)225 void QDeclarativeGeoMapCopyrightNotice::mouseReleaseEvent(QMouseEvent *event)
226 {
227     if (m_copyrightsHtml) {
228         QString anchor = m_copyrightsHtml->documentLayout()->anchorAt(event->pos());
229         if (anchor == m_activeAnchor && !anchor.isEmpty()) {
230             emit linkActivated(anchor);
231             m_activeAnchor.clear();
232         }
233     }
234 }
235 
rasterizeHtmlAndUpdate()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(qPremultiply(QColor(Qt::transparent).rgba()));
245     QPainter painter(&m_copyrightsImage);
246     QAbstractTextDocumentLayout::PaintContext ctx;
247     ctx.palette.setColor(QPalette::Text, QStringLiteral("black"));
248     m_copyrightsHtml->documentLayout()->draw(&painter, 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 
createCopyright()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 
setVisible(bool visible)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 */
setCopyrightsVisible(bool visible)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 
copyrightsVisible() const292 bool QDeclarativeGeoMapCopyrightNotice::copyrightsVisible() const
293 {
294     return m_copyrightsVisible;
295 }
296 
297 /*!
298     \internal
299 */
setCopyrightsZ(qreal copyrightsZ)300 void QDeclarativeGeoMapCopyrightNotice::setCopyrightsZ(qreal copyrightsZ)
301 {
302     setZ(copyrightsZ);
303     update();
304 }
305 
306 /*!
307     \internal
308 */
copyrightsChanged(const QImage & copyrightsImage)309 void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QImage &copyrightsImage)
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 
copyrightsChanged(const QString & copyrightsHtml)326 void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QString &copyrightsHtml)
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 
onCopyrightsStyleSheetChanged(const QString & styleSheet)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(m_styleSheet);
372 }
373 
374 QT_END_NAMESPACE
375