File indexing completed on 2025-01-05 03:58:35
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-12-01 0007 * Description : Widget for displaying HTML in the backends - QtWebEngine version 0008 * 0009 * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2009-2011 by Michael G. Hansen <mike at mghansen dot de> 0011 * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "htmlwidget_qwebengine.h" 0018 0019 // Qt includes 0020 0021 #include <QEventLoop> 0022 #include <QResizeEvent> 0023 #include <QCoreApplication> 0024 #include <QWebEngineSettings> 0025 #include <QtWebEngineWidgetsVersion> 0026 0027 // Local includes 0028 0029 #include "digikam_debug.h" 0030 0031 namespace Digikam 0032 { 0033 0034 HTMLWidgetPage::HTMLWidgetPage(HTMLWidget* const parent) 0035 : QWebEnginePage(parent) 0036 { 0037 m_timer = new QTimer(this); 0038 m_timer->setInterval(100); 0039 m_timer->setSingleShot(true); 0040 0041 connect(m_timer, SIGNAL(timeout()), 0042 this, SLOT(slotSendHTMLEvents()), 0043 Qt::QueuedConnection); 0044 } 0045 0046 HTMLWidgetPage::~HTMLWidgetPage() 0047 { 0048 } 0049 0050 void HTMLWidgetPage::javaScriptConsoleMessage(JavaScriptConsoleMessageLevel /*level*/, 0051 const QString& message, 0052 int /*lineNumber*/, 0053 const QString& /*sourceID*/) 0054 { 0055 if (!message.startsWith(QLatin1String("(event)"))) 0056 { 0057 m_message = message; 0058 m_timer->start(); 0059 0060 return; 0061 } 0062 0063 qCDebug(DIGIKAM_GEOIFACE_LOG) << message; 0064 0065 const QString eventString = message.mid(7); 0066 0067 if (eventString.isEmpty()) 0068 { 0069 return; 0070 } 0071 0072 m_events << eventString; 0073 m_timer->start(); 0074 } 0075 0076 void HTMLWidgetPage::slotSendHTMLEvents() 0077 { 0078 if (!m_message.isEmpty()) 0079 { 0080 Q_EMIT signalMessageEvent(m_message); 0081 0082 m_message.clear(); 0083 } 0084 0085 if (!m_events.isEmpty()) 0086 { 0087 Q_EMIT signalHTMLEvents(m_events); 0088 0089 m_events.clear(); 0090 } 0091 } 0092 0093 // --------------------------------------------------------------------------------------------- 0094 0095 class Q_DECL_HIDDEN HTMLWidget::Private 0096 { 0097 public: 0098 0099 explicit Private() 0100 : parent (nullptr), 0101 child (nullptr), 0102 hpage (nullptr), 0103 isReady (false), 0104 selectionStatus (false), 0105 firstSelectionPoint (), 0106 intermediateSelectionPoint (), 0107 firstSelectionScreenPoint (), 0108 intermediateSelectionScreenPoint() 0109 { 0110 } 0111 0112 QWidget* parent; 0113 QWidget* child; 0114 HTMLWidgetPage* hpage; 0115 0116 bool isReady; 0117 bool selectionStatus; 0118 0119 GeoCoordinates firstSelectionPoint; 0120 GeoCoordinates intermediateSelectionPoint; 0121 0122 QPoint firstSelectionScreenPoint; 0123 QPoint intermediateSelectionScreenPoint; 0124 }; 0125 0126 HTMLWidget::HTMLWidget(QWidget* const parent) 0127 : QWebEngineView(parent), 0128 d (new Private()), 0129 s (nullptr) 0130 { 0131 d->parent = parent; 0132 setAcceptDrops(false); 0133 setFocusPolicy(Qt::WheelFocus); 0134 0135 d->hpage = new HTMLWidgetPage(this); 0136 setPage(d->hpage); 0137 0138 settings()->setAttribute(QWebEngineSettings::WebGLEnabled, false); 0139 settings()->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, false); 0140 settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); 0141 0142 d->parent->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 0143 0144 connect(this, SIGNAL(loadProgress(int)), 0145 this, SLOT(progress(int))); 0146 0147 connect(this, SIGNAL(loadFinished(bool)), 0148 this, SLOT(slotHTMLCompleted(bool))); 0149 0150 connect(d->hpage, SIGNAL(signalHTMLEvents(QStringList)), 0151 this, SIGNAL(signalHTMLEvents(QStringList))); 0152 0153 connect(d->hpage, SIGNAL(signalMessageEvent(QString)), 0154 this, SIGNAL(signalMessageEvent(QString))); 0155 0156 if (d->parent) 0157 { 0158 d->parent->installEventFilter(this); 0159 } 0160 0161 installEventFilter(this); 0162 } 0163 0164 HTMLWidget::~HTMLWidget() 0165 { 0166 delete d; 0167 } 0168 0169 void HTMLWidget::progress(int progress) 0170 { 0171 qCDebug(DIGIKAM_GEOIFACE_LOG) << "Maps Loading Progress: " << progress << "%"; 0172 } 0173 0174 void HTMLWidget::slotHTMLCompleted(bool ok) 0175 { 0176 qCDebug(DIGIKAM_GEOIFACE_LOG) << "Map Loading Completed: " << ok; 0177 d->isReady = ok; 0178 0179 Q_EMIT signalJavaScriptReady(); 0180 } 0181 0182 /** 0183 * @brief Wrapper around executeScript to catch more errors 0184 */ 0185 QVariant HTMLWidget::runScript(const QString& scriptCode, bool async) 0186 { 0187 GEOIFACE_ASSERT(d->isReady); 0188 0189 if (!d->isReady) 0190 { 0191 return QVariant(); 0192 } 0193 0194 //qCDebug(DIGIKAM_GEOIFACE_LOG) << scriptCode; 0195 0196 if (async) 0197 { 0198 page()->runJavaScript(scriptCode); 0199 } 0200 else 0201 { 0202 QVariant ret; 0203 QEventLoop loop; 0204 0205 // Lambda c++11 function capturing value returned by java script code which is not synchro with QWebEngineView. 0206 // See https://wiki.qt.io/Porting_from_QtWebKit_to_QtWebEngine. 0207 0208 page()->runJavaScript(scriptCode, 0209 [&ret, &loop](const QVariant& result) 0210 { 0211 ret.setValue(result); 0212 loop.quit(); 0213 } 0214 ); 0215 0216 loop.exec(); 0217 0218 return ret; 0219 } 0220 0221 return QVariant(); 0222 } 0223 0224 /** 0225 * @brief Execute a script which returns coordinates and parse these 0226 */ 0227 bool HTMLWidget::runScript2Coordinates(const QString& scriptCode, GeoCoordinates* const coordinates) 0228 { 0229 const QVariant scriptResult = runScript(scriptCode, false); 0230 0231 return GeoIfaceHelperParseLatLonString(scriptResult.toString(), coordinates); 0232 } 0233 0234 bool HTMLWidget::eventFilter(QObject* object, QEvent* event) 0235 { 0236 if (object == this) 0237 { 0238 if (event->type() == QEvent::ChildAdded) 0239 { 0240 d->child = findChild<QWidget*>(); 0241 0242 if (d->child) 0243 { 0244 d->child->installEventFilter(this); 0245 } 0246 } 0247 0248 return QWebEngineView::eventFilter(object, event); 0249 } 0250 else if (d->parent && (object == d->parent)) 0251 { 0252 if (event->type() == QEvent::Resize) 0253 { 0254 QResizeEvent* const resizeEvent = dynamic_cast<QResizeEvent*>(event); 0255 0256 if (resizeEvent) 0257 { 0258 resize(resizeEvent->size()); 0259 } 0260 } 0261 } 0262 else if (d->child && (object == d->child)) 0263 { 0264 if (event->type() == QEvent::MouseButtonRelease) 0265 { 0266 QMouseEvent* const e = dynamic_cast<QMouseEvent*>(event); 0267 0268 if (e && (s->currentMouseMode == MouseModeRegionSelection)) 0269 { 0270 if (!d->firstSelectionPoint.hasCoordinates()) 0271 { 0272 runScript2Coordinates(QString::fromLatin1("kgeomapPixelToLatLng(%1, %2);") 0273 0274 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0275 0276 .arg(e->position().x()) 0277 .arg(e->position().y()), 0278 0279 #else 0280 0281 .arg(e->x()) 0282 .arg(e->y()), 0283 0284 #endif 0285 0286 &d->firstSelectionPoint); 0287 0288 d->firstSelectionScreenPoint = QPoint( 0289 0290 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0291 0292 e->position().toPoint() 0293 0294 #else 0295 0296 e->x(), e->y() 0297 0298 #endif 0299 0300 ); 0301 } 0302 else 0303 { 0304 runScript2Coordinates(QString::fromLatin1("kgeomapPixelToLatLng(%1, %2);") 0305 0306 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0307 0308 .arg(e->position().toPoint().x()) 0309 .arg(e->position().toPoint().y()), 0310 0311 #else 0312 0313 .arg(e->x()) 0314 .arg(e->y()), 0315 0316 #endif 0317 0318 &d->intermediateSelectionPoint); 0319 0320 d->intermediateSelectionScreenPoint = QPoint( 0321 0322 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0323 0324 e->position().toPoint() 0325 0326 #else 0327 0328 e->x(), e->y() 0329 0330 #endif 0331 0332 ); 0333 0334 qreal lonWest = 0.0, latNorth = 0.0, lonEast = 0.0, latSouth = 0.0; 0335 0336 if (d->firstSelectionScreenPoint.x() < d->intermediateSelectionScreenPoint.x()) 0337 { 0338 lonWest = d->firstSelectionPoint.lon(); 0339 lonEast = d->intermediateSelectionPoint.lon(); 0340 } 0341 else 0342 { 0343 lonEast = d->firstSelectionPoint.lon(); 0344 lonWest = d->intermediateSelectionPoint.lon(); 0345 } 0346 0347 if (d->firstSelectionScreenPoint.y() < d->intermediateSelectionScreenPoint.y()) 0348 { 0349 latNorth = d->firstSelectionPoint.lat(); 0350 latSouth = d->intermediateSelectionPoint.lat(); 0351 } 0352 else 0353 { 0354 latNorth = d->intermediateSelectionPoint.lat(); 0355 latSouth = d->firstSelectionPoint.lat(); 0356 } 0357 0358 runScript(QLatin1String("kgeomapRemoveTemporarySelectionRectangle();")); 0359 runScript(QString::fromLatin1("kgeomapSetSelectionRectangle(%1, %2, %3, %4);") 0360 .arg(lonWest) 0361 .arg(latNorth) 0362 .arg(lonEast) 0363 .arg(latSouth)); 0364 0365 const GeoCoordinates::Pair selectionCoordinates( 0366 GeoCoordinates(latNorth, lonWest), 0367 GeoCoordinates(latSouth, lonEast)); 0368 0369 d->firstSelectionPoint.clear(); 0370 d->intermediateSelectionPoint.clear(); 0371 0372 Q_EMIT selectionHasBeenMade(selectionCoordinates); 0373 } 0374 } 0375 } 0376 else if (event->type() == QEvent::MouseMove) 0377 { 0378 QMouseEvent* const e = dynamic_cast<QMouseEvent*>(event); 0379 0380 if (e && 0381 (s->currentMouseMode == MouseModeRegionSelection) && 0382 d->firstSelectionPoint.hasCoordinates()) 0383 { 0384 runScript2Coordinates(QString::fromLatin1("kgeomapPixelToLatLng(%1, %2);") 0385 0386 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0387 0388 .arg(e->position().toPoint().x()) 0389 .arg(e->position().toPoint().y()), 0390 0391 #else 0392 0393 .arg(e->x()) 0394 .arg(e->y()), 0395 0396 #endif 0397 0398 &d->intermediateSelectionPoint); 0399 0400 d->intermediateSelectionScreenPoint = QPoint( 0401 0402 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0403 0404 e->position().toPoint() 0405 0406 #else 0407 0408 e->x(), e->y() 0409 0410 #endif 0411 0412 ); 0413 0414 qCDebug(DIGIKAM_GEOIFACE_LOG) << d->firstSelectionScreenPoint << QLatin1Char(' ') 0415 << d->intermediateSelectionScreenPoint; 0416 0417 qreal lonWest = 0.0, latNorth = 0.0, lonEast = 0.0, latSouth = 0.0; 0418 0419 if (d->firstSelectionScreenPoint.x() < d->intermediateSelectionScreenPoint.x()) 0420 { 0421 lonWest = d->firstSelectionPoint.lon(); 0422 lonEast = d->intermediateSelectionPoint.lon(); 0423 } 0424 else 0425 { 0426 lonEast = d->firstSelectionPoint.lon(); 0427 lonWest = d->intermediateSelectionPoint.lon(); 0428 } 0429 0430 if (d->firstSelectionScreenPoint.y() < d->intermediateSelectionScreenPoint.y()) 0431 { 0432 latNorth = d->firstSelectionPoint.lat(); 0433 latSouth = d->intermediateSelectionPoint.lat(); 0434 } 0435 else 0436 { 0437 latNorth = d->intermediateSelectionPoint.lat(); 0438 latSouth = d->firstSelectionPoint.lat(); 0439 } 0440 0441 runScript(QString::fromLatin1("kgeomapSetTemporarySelectionRectangle(%1, %2, %3, %4);") 0442 .arg(lonWest) 0443 .arg(latNorth) 0444 .arg(lonEast) 0445 .arg(latSouth)); 0446 } 0447 } 0448 } 0449 0450 return false; 0451 } 0452 0453 void HTMLWidget::setSelectionRectangle(const GeoCoordinates::Pair& searchCoordinates) 0454 { 0455 if (!searchCoordinates.first.hasCoordinates()) 0456 { 0457 runScript(QLatin1String("kgeomapRemoveSelectionRectangle();")); 0458 0459 return; 0460 } 0461 0462 qreal West = searchCoordinates.first.lon(); 0463 qreal North = searchCoordinates.first.lat(); 0464 qreal East = searchCoordinates.second.lon(); 0465 qreal South = searchCoordinates.second.lat(); 0466 0467 runScript(QString::fromLatin1("kgeomapSetSelectionRectangle(%1, %2, %3, %4);") 0468 .arg(West).arg(North).arg(East).arg(South)); 0469 } 0470 0471 void HTMLWidget::removeSelectionRectangle() 0472 { 0473 runScript(QLatin1String("kgeomapRemoveSelectionRectangle();")); 0474 } 0475 0476 void HTMLWidget::mouseModeChanged(const GeoMouseModes mouseMode) 0477 { 0478 const bool inSelectionMode = (mouseMode == MouseModeRegionSelection); 0479 0480 if (inSelectionMode) 0481 { 0482 d->firstSelectionPoint.clear(); 0483 d->intermediateSelectionPoint.clear(); 0484 runScript(QString::fromLatin1("kgeomapSelectionModeStatus(%1);").arg(inSelectionMode)); 0485 } 0486 else 0487 { 0488 runScript(QString::fromLatin1("kgeomapSelectionModeStatus(%1);").arg(inSelectionMode)); 0489 } 0490 } 0491 0492 void HTMLWidget::centerOn(const qreal west, const qreal north, 0493 const qreal east, const qreal south, 0494 const bool useSaneZoomLevel) 0495 { 0496 /* 0497 qCDebug(DIGIKAM_GEOIFACE_LOG) << "West:" << west 0498 << " North:" << north 0499 << " East:" << east 0500 << " South:" << south; 0501 */ 0502 runScript(QString::fromLatin1("kgeomapSetMapBoundaries(%1, %2, %3, %4, %5);") 0503 .arg(west) 0504 .arg(north) 0505 .arg(east) 0506 .arg(south) 0507 .arg(useSaneZoomLevel ? 1 : 0)); 0508 } 0509 0510 void HTMLWidget::setSharedGeoIfaceObject(GeoIfaceSharedData* const sharedData) 0511 { 0512 s = sharedData; 0513 } 0514 0515 } // namespace Digikam 0516 0517 #include "moc_htmlwidget_qwebengine.cpp"