| 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 |  |