File indexing completed on 2025-03-09 03:57:15
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-12-08 0007 * Description : Marble-backend for geolocation interface 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: 2014 by Justus Schwartz <justus at gmx dot li> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "backendmarble.h" 0018 0019 // Qt includes 0020 0021 #include <QMenu> 0022 #include <QMouseEvent> 0023 #include <QPointer> 0024 #include <QAction> 0025 #include <QActionGroup> 0026 #include <QStandardItemModel> 0027 0028 // KDE includes 0029 0030 #include <kconfiggroup.h> 0031 #include <klocalizedstring.h> 0032 0033 // Marble includes 0034 0035 #include "GeoDataLinearRing.h" 0036 #include "GeoDataLatLonAltBox.h" 0037 #include "GeoPainter.h" 0038 #include "MarbleMap.h" 0039 #include "MarbleDirs.h" 0040 #include "MarbleWidget.h" 0041 #include "ViewportParams.h" 0042 #include "AbstractFloatItem.h" 0043 #include "MarbleWidgetPopupMenu.h" 0044 #include "MapThemeManager.h" 0045 0046 // Local includes 0047 0048 #include "digikam_debug.h" 0049 #include "digikam_config.h" 0050 #include "backendmarblelayer.h" 0051 #include "abstractmarkertiler.h" 0052 #include "mapwidget.h" 0053 #include "geomodelhelper.h" 0054 0055 namespace Digikam 0056 { 0057 0058 class Q_DECL_HIDDEN BMInternalWidgetInfo 0059 { 0060 public: 0061 0062 BMInternalWidgetInfo() = default; 0063 0064 BackendMarbleLayer* bmLayer = nullptr; 0065 }; 0066 0067 } // namespace Digikam 0068 0069 Q_DECLARE_METATYPE(Digikam::BMInternalWidgetInfo) 0070 0071 namespace Digikam 0072 { 0073 0074 class Q_DECL_HIDDEN BackendMarble::Private 0075 { 0076 public: 0077 0078 explicit Private() 0079 : marbleWidget (nullptr), 0080 actionGroupMapTheme (nullptr), 0081 actionGroupProjection (nullptr), 0082 actionGroupFloatItems (nullptr), 0083 actionShowCompass (nullptr), 0084 actionShowScaleBar (nullptr), 0085 actionShowNavigation (nullptr), 0086 actionShowOverviewMap (nullptr), 0087 cacheMapTheme (QLatin1String("earth/openstreetmap/openstreetmap.dgml")), 0088 cacheProjection (QLatin1String("spherical")), 0089 cacheShowCompass (false), 0090 cacheShowScaleBar (false), 0091 cacheShowNavigation (true), 0092 cacheShowOverviewMap (false), 0093 cacheZoom (900), 0094 havePotentiallyMouseMovingObject(false), 0095 haveMouseMovingObject (false), 0096 mouseMoveClusterIndex (-1), 0097 mouseMoveMarkerIndex (), 0098 mouseMoveObjectCoordinates (), 0099 mouseMoveCenterOffset (0, 0), 0100 dragDropMarkerCount (0), 0101 dragDropMarkerPos (), 0102 clustersDirtyCacheProjection (), 0103 clustersDirtyCacheLat (), 0104 clustersDirtyCacheLon (), 0105 displayedRectangle (), 0106 firstSelectionScreenPoint (), 0107 firstSelectionPoint (), 0108 activeState (false), 0109 widgetIsDocked (false), 0110 blockingZoomWhileChangingTheme (false), 0111 trackCache (), 0112 bmLayer (nullptr), 0113 marbleMapThemeManager (nullptr) 0114 { 0115 } 0116 0117 QPointer<Marble::MarbleWidget> marbleWidget; 0118 0119 QActionGroup* actionGroupMapTheme; 0120 QActionGroup* actionGroupProjection; 0121 QActionGroup* actionGroupFloatItems; 0122 QAction* actionShowCompass; 0123 QAction* actionShowScaleBar; 0124 QAction* actionShowNavigation; 0125 QAction* actionShowOverviewMap; 0126 0127 QString cacheMapTheme; 0128 QString cacheProjection; 0129 bool cacheShowCompass; 0130 bool cacheShowScaleBar; 0131 bool cacheShowNavigation; 0132 bool cacheShowOverviewMap; 0133 int cacheZoom; 0134 bool havePotentiallyMouseMovingObject; 0135 bool haveMouseMovingObject; 0136 int mouseMoveClusterIndex; 0137 QPersistentModelIndex mouseMoveMarkerIndex; 0138 GeoCoordinates mouseMoveObjectCoordinates; 0139 QPoint mouseMoveCenterOffset; 0140 int dragDropMarkerCount; 0141 QPoint dragDropMarkerPos; 0142 int clustersDirtyCacheProjection; 0143 qreal clustersDirtyCacheLat; 0144 qreal clustersDirtyCacheLon; 0145 QStringList mainMarbleThemes; 0146 0147 GeoCoordinates::Pair displayedRectangle; 0148 QPoint firstSelectionScreenPoint; 0149 QPoint intermediateSelectionScreenPoint; 0150 GeoCoordinates firstSelectionPoint; 0151 GeoCoordinates intermediateSelectionPoint; 0152 bool activeState; 0153 bool widgetIsDocked; 0154 bool blockingZoomWhileChangingTheme; 0155 0156 QHash<quint64, Marble::GeoDataLineString> trackCache; 0157 0158 BackendMarbleLayer* bmLayer; 0159 Marble::MapThemeManager* marbleMapThemeManager; 0160 }; 0161 0162 BackendMarble::BackendMarble(const QExplicitlySharedDataPointer<GeoIfaceSharedData>& sharedData, 0163 QObject* const parent) 0164 : MapBackend(sharedData, parent), 0165 d (new Private()) 0166 { 0167 d->marbleMapThemeManager = new Marble::MapThemeManager(this); 0168 this->createActions(); 0169 } 0170 0171 BackendMarble::~BackendMarble() 0172 { 0173 /// @todo Should we leave our widget in this list and not destroy it? 0174 0175 GeoIfaceGlobalObject* const go = GeoIfaceGlobalObject::instance(); 0176 go->removeMyInternalWidgetFromPool(this); 0177 0178 if (d->marbleWidget) 0179 { 0180 d->marbleWidget->removeLayer(d->bmLayer); 0181 0182 delete d->marbleWidget; 0183 } 0184 0185 delete d; 0186 } 0187 0188 QString BackendMarble::backendName() const 0189 { 0190 return QLatin1String("marble"); 0191 } 0192 0193 QString BackendMarble::backendHumanName() const 0194 { 0195 return i18n("Marble Virtual Globe"); 0196 } 0197 0198 QWidget* BackendMarble::mapWidget() 0199 { 0200 if (!d->marbleWidget) 0201 { 0202 GeoIfaceGlobalObject* const go = GeoIfaceGlobalObject::instance(); 0203 0204 GeoIfaceInternalWidgetInfo info; 0205 0206 if (go->getInternalWidgetFromPool(this, &info)) 0207 { 0208 d->marbleWidget = qobject_cast<Marble::MarbleWidget*>(info.widget); 0209 const BMInternalWidgetInfo intInfo = info.backendData.value<BMInternalWidgetInfo>(); 0210 d->bmLayer = intInfo.bmLayer; 0211 0212 if (d->bmLayer) 0213 { 0214 d->bmLayer->setBackend(this); 0215 } 0216 else 0217 { 0218 qCWarning(DIGIKAM_GEOIFACE_LOG) << "Marble widget instance is null!"; 0219 } 0220 } 0221 else 0222 { 0223 d->marbleWidget = new Marble::MarbleWidget(); 0224 d->bmLayer = new BackendMarbleLayer(this); 0225 0226 d->marbleWidget->addLayer(d->bmLayer); 0227 } 0228 0229 d->marbleWidget->installEventFilter(this); 0230 0231 connect(d->marbleWidget, SIGNAL(zoomChanged(int)), 0232 this, SLOT(slotMarbleZoomChanged())); 0233 0234 // set a backend first 0235 /// @todo Do this only if we are set active! 0236 0237 applyCacheToWidget(); 0238 0239 Q_EMIT signalBackendReadyChanged(backendName()); 0240 } 0241 0242 return d->marbleWidget; 0243 } 0244 0245 void BackendMarble::releaseWidget(GeoIfaceInternalWidgetInfo* const info) 0246 { 0247 info->widget->removeEventFilter(this); 0248 0249 BMInternalWidgetInfo intInfo = info->backendData.value<BMInternalWidgetInfo>(); 0250 0251 if (intInfo.bmLayer) 0252 { 0253 intInfo.bmLayer->setBackend(nullptr); 0254 } 0255 0256 disconnect(d->marbleWidget, SIGNAL(zoomChanged(int)), 0257 this, SLOT(slotMarbleZoomChanged())); 0258 0259 info->currentOwner = nullptr; 0260 info->state = GeoIfaceInternalWidgetInfo::InternalWidgetReleased; 0261 d->marbleWidget = nullptr; 0262 d->bmLayer = nullptr; 0263 0264 Q_EMIT signalBackendReadyChanged(backendName()); 0265 } 0266 0267 GeoCoordinates BackendMarble::getCenter() const 0268 { 0269 if (!d->marbleWidget) 0270 { 0271 return GeoCoordinates(); 0272 } 0273 0274 return GeoCoordinates(d->marbleWidget->centerLatitude(), d->marbleWidget->centerLongitude()); 0275 } 0276 0277 void BackendMarble::setCenter(const GeoCoordinates& coordinate) 0278 { 0279 if (!d->marbleWidget) 0280 { 0281 return; 0282 } 0283 0284 d->marbleWidget->setCenterLatitude(coordinate.lat()); 0285 d->marbleWidget->setCenterLongitude(coordinate.lon()); 0286 } 0287 0288 bool BackendMarble::isReady() const 0289 { 0290 return (d->marbleWidget != nullptr); 0291 } 0292 0293 void BackendMarble::zoomIn() 0294 { 0295 if (!d->marbleWidget) 0296 { 0297 return; 0298 } 0299 0300 d->marbleWidget->zoomIn(); 0301 d->marbleWidget->repaint(); 0302 } 0303 0304 void BackendMarble::zoomOut() 0305 { 0306 if (!d->marbleWidget) 0307 { 0308 return; 0309 } 0310 0311 d->marbleWidget->zoomOut(); 0312 d->marbleWidget->repaint(); 0313 } 0314 0315 void BackendMarble::createActions() 0316 { 0317 // map theme: 0318 0319 d->actionGroupMapTheme = new QActionGroup(this); 0320 d->actionGroupMapTheme->setExclusive(true); 0321 0322 connect(d->actionGroupMapTheme, &QActionGroup::triggered, 0323 this, &BackendMarble::slotMapThemeActionTriggered); 0324 0325 QList<QPair<QString, QString>> mainThemes; 0326 mainThemes.append( { i18n("Atlas map"), QLatin1String("earth/srtm/srtm.dgml") } ); 0327 mainThemes.append( { i18n("Satellite map"), QLatin1String("earth/bluemarble/bluemarble.dgml") } ); 0328 mainThemes.append( { i18n("OpenStreetMap"), QLatin1String("earth/openstreetmap/openstreetmap.dgml") } ); 0329 mainThemes.append( { i18n("OpenStreetMap (Vector)"), QLatin1String("earth/vectorosm/vectorosm.dgml") } ); 0330 0331 for (auto& theme : mainThemes) 0332 { 0333 QAction* const mapAction = new QAction(d->actionGroupMapTheme); 0334 mapAction->setCheckable(true); 0335 mapAction->setText(theme.first); 0336 mapAction->setData(theme.second); 0337 d->mainMarbleThemes.append(theme.second); 0338 } 0339 0340 QStringList blackListedMarbleThemes 0341 { 0342 QLatin1String("earth/behaim1492/behaim1492.dgml"), 0343 QLatin1String("earth/citylights/citylights.dgml"), 0344 QLatin1String("earth/plain/plain.dgml"), 0345 QLatin1String("earth/political/political.dgml"), 0346 QLatin1String("earth/precip-dec/precip-dec.dgml"), 0347 QLatin1String("earth/precip-july/precip-july.dgml"), 0348 QLatin1String("earth/schagen1689/schagen1689.dgml"), 0349 QLatin1String("earth/openstreetmap/openstreetmap.dgml"), 0350 QLatin1String("earth/temp-dec/temp-dec.dgml"), 0351 QLatin1String("earth/temp-july/temp-july.dgml") 0352 }; 0353 0354 // add all available marble earth themes 0355 0356 auto* themeModel = d->marbleMapThemeManager->mapThemeModel(); 0357 0358 for (int i = 0 ; i < themeModel->rowCount() ; ++i) 0359 { 0360 auto* item = themeModel->item(i); 0361 auto themeId = item->data(Qt::UserRole + 1).toString(); 0362 auto themeName = item->data(Qt::DisplayRole).toString(); 0363 0364 if (d->mainMarbleThemes.contains(themeId) || 0365 blackListedMarbleThemes.contains(themeId) || 0366 !themeId.startsWith(QLatin1String("earth/"))) 0367 { 0368 continue; 0369 } 0370 0371 QAction* const mapAction = new QAction(d->actionGroupMapTheme); 0372 mapAction->setCheckable(true); 0373 mapAction->setText(themeName); 0374 mapAction->setData(themeId); 0375 } 0376 0377 // projection 0378 0379 d->actionGroupProjection = new QActionGroup(this); 0380 d->actionGroupProjection->setExclusive(true); 0381 0382 connect(d->actionGroupProjection, &QActionGroup::triggered, 0383 this, &BackendMarble::slotProjectionActionTriggered); 0384 0385 QAction* const actionSpherical = new QAction(d->actionGroupProjection); 0386 actionSpherical->setCheckable(true); 0387 actionSpherical->setText(i18nc("Spherical projection", "Spherical")); 0388 actionSpherical->setData(QLatin1String("spherical")); 0389 0390 QAction* const actionMercator = new QAction(d->actionGroupProjection); 0391 actionMercator->setCheckable(true); 0392 actionMercator->setText(i18n("Mercator")); 0393 actionMercator->setData(QLatin1String("mercator")); 0394 0395 QAction* const actionEquirectangular = new QAction(d->actionGroupProjection); 0396 actionEquirectangular->setCheckable(true); 0397 actionEquirectangular->setText(i18n("Equirectangular")); 0398 actionEquirectangular->setData(QLatin1String("equirectangular")); 0399 0400 // float items 0401 0402 d->actionGroupFloatItems = new QActionGroup(this); 0403 d->actionGroupFloatItems->setExclusive(false); 0404 0405 connect(d->actionGroupFloatItems, &QActionGroup::triggered, 0406 this, &BackendMarble::slotFloatSettingsTriggered); 0407 0408 d->actionShowCompass = new QAction(i18n("Show compass"), d->actionGroupFloatItems); 0409 d->actionShowCompass->setData(QLatin1String("showcompass")); 0410 d->actionShowCompass->setCheckable(true); 0411 d->actionGroupFloatItems->addAction(d->actionShowCompass); 0412 0413 d->actionShowScaleBar = new QAction(i18n("Show scale bar"), d->actionGroupFloatItems); 0414 d->actionShowScaleBar->setData(QLatin1String("showscalebar")); 0415 d->actionShowScaleBar->setCheckable(true); 0416 d->actionGroupFloatItems->addAction(d->actionShowScaleBar); 0417 0418 d->actionShowNavigation = new QAction(i18n("Show navigation"), d->actionGroupFloatItems); 0419 d->actionShowNavigation->setData(QLatin1String("shownavigation")); 0420 d->actionShowNavigation->setCheckable(true); 0421 d->actionGroupFloatItems->addAction(d->actionShowNavigation); 0422 0423 d->actionShowOverviewMap = new QAction(i18n("Show overview map"), d->actionGroupFloatItems); 0424 d->actionShowOverviewMap->setData(QLatin1String("showoverviewmap")); 0425 d->actionShowOverviewMap->setCheckable(true); 0426 d->actionGroupFloatItems->addAction(d->actionShowOverviewMap); 0427 } 0428 0429 void BackendMarble::addActionsToConfigurationMenu(QMenu* const configurationMenu) 0430 { 0431 GEOIFACE_ASSERT(configurationMenu != nullptr); 0432 0433 configurationMenu->addSeparator(); 0434 0435 const QList<QAction*> mapThemeActions = d->actionGroupMapTheme->actions(); 0436 QMenu* const extraMarbleMenu = new QMenu(i18n("Other Marble Themes"), configurationMenu); 0437 0438 for (auto* action : mapThemeActions) 0439 { 0440 if (d->mainMarbleThemes.contains(action->data().toString())) 0441 { 0442 configurationMenu->addAction(action); 0443 } 0444 else 0445 { 0446 extraMarbleMenu->addAction(action); 0447 } 0448 } 0449 0450 if (extraMarbleMenu->isEmpty()) 0451 { 0452 delete extraMarbleMenu; 0453 } 0454 else 0455 { 0456 configurationMenu->addMenu(extraMarbleMenu); 0457 } 0458 0459 configurationMenu->addSeparator(); 0460 0461 // TODO: we need a parent for this guy! 0462 0463 QMenu* const projectionSubMenu = new QMenu(i18n("Projection"), configurationMenu); 0464 configurationMenu->addMenu(projectionSubMenu); 0465 const QList<QAction*> projectionActions = d->actionGroupProjection->actions(); 0466 0467 for (int i = 0 ; i < projectionActions.count() ; ++i) 0468 { 0469 projectionSubMenu->addAction(projectionActions.at(i)); 0470 } 0471 0472 QMenu* const floatItemsSubMenu = new QMenu(i18n("Float items"), configurationMenu); 0473 configurationMenu->addMenu(floatItemsSubMenu); 0474 const QList<QAction*> floatActions = d->actionGroupFloatItems->actions(); 0475 0476 for (int i = 0 ; i < floatActions.count() ; ++i) 0477 { 0478 floatItemsSubMenu->addAction(floatActions.at(i)); 0479 } 0480 0481 configurationMenu->addSeparator(); 0482 0483 addCommonOptions(configurationMenu); 0484 0485 updateActionAvailability(); 0486 } 0487 0488 void BackendMarble::slotMapThemeActionTriggered(QAction* action) 0489 { 0490 setMapTheme(action->data().toString()); 0491 } 0492 0493 QString BackendMarble::getMapTheme() const 0494 { 0495 // TODO: read the theme from the marblewidget! 0496 0497 return d->cacheMapTheme; 0498 } 0499 0500 void BackendMarble::setMapTheme(const QString& newMapTheme) 0501 { 0502 // convert old ids to themeIds 0503 0504 if (newMapTheme == QLatin1String("atlas")) 0505 { 0506 d->cacheMapTheme = QLatin1String("earth/srtm/srtm.dgml"); 0507 } 0508 else if (newMapTheme == QLatin1String("satellite")) 0509 { 0510 d->cacheMapTheme = QLatin1String("earth/bluemarble/bluemarble.dgml"); 0511 } 0512 else if (newMapTheme == QLatin1String("openstreetmap")) 0513 { 0514 d->cacheMapTheme = QLatin1String("earth/openstreetmap/openstreetmap.dgml"); 0515 } 0516 else if (!d->marbleMapThemeManager->mapThemeIds().contains(newMapTheme)) 0517 { 0518 // fall back to atlas 0519 d->cacheMapTheme = QLatin1String("earth/srtm/srtm.dgml"); 0520 } 0521 else 0522 { 0523 d->cacheMapTheme = newMapTheme; 0524 } 0525 0526 if (!d->marbleWidget) 0527 { 0528 return; 0529 } 0530 0531 // Changing the map theme changes the zoom - we want to try to keep the zoom constant 0532 0533 d->blockingZoomWhileChangingTheme = true; 0534 0535 // Remember the zoom from the cache. The zoom of the widget may not have been set yet! 0536 0537 const int oldMarbleZoom = d->cacheZoom; 0538 0539 d->marbleWidget->setMapThemeId(d->cacheMapTheme); 0540 0541 // the float items are reset when the theme is changed: 0542 0543 setShowCompass(d->cacheShowCompass); 0544 setShowScaleBar(d->cacheShowScaleBar); 0545 setShowNavigation(d->cacheShowNavigation); 0546 setShowOverviewMap(d->cacheShowOverviewMap); 0547 0548 // make sure the zoom level is within the allowed range 0549 0550 int targetZoomLevel = oldMarbleZoom; 0551 0552 if (oldMarbleZoom > d->marbleWidget->maximumZoom()) 0553 { 0554 targetZoomLevel = d->marbleWidget->maximumZoom(); 0555 } 0556 else if (oldMarbleZoom < d->marbleWidget->minimumZoom()) 0557 { 0558 targetZoomLevel = d->marbleWidget->minimumZoom(); 0559 } 0560 0561 if (targetZoomLevel != oldMarbleZoom) 0562 { 0563 // our zoom level had to be adjusted, therefore unblock 0564 // the signal now to allow the change to propagate 0565 0566 d->blockingZoomWhileChangingTheme = false; 0567 } 0568 0569 d->marbleWidget->zoomView(targetZoomLevel); 0570 d->blockingZoomWhileChangingTheme = false; 0571 0572 updateActionAvailability(); 0573 } 0574 0575 void BackendMarble::saveSettingsToGroup(KConfigGroup* const group) 0576 { 0577 GEOIFACE_ASSERT(group != nullptr); 0578 0579 if (!group) 0580 { 0581 return; 0582 } 0583 0584 group->writeEntry("Marble Map Theme", d->cacheMapTheme); 0585 group->writeEntry("Marble Projection", d->cacheProjection); 0586 group->writeEntry("Marble Show Compass", d->cacheShowCompass); 0587 group->writeEntry("Marble Show Scale Bar", d->cacheShowScaleBar); 0588 group->writeEntry("Marble Show Navigation", d->cacheShowNavigation); 0589 group->writeEntry("Marble Show Overview Map", d->cacheShowOverviewMap); 0590 } 0591 0592 void BackendMarble::readSettingsFromGroup(const KConfigGroup* const group) 0593 { 0594 GEOIFACE_ASSERT(group != nullptr); 0595 0596 if (!group) 0597 { 0598 return; 0599 } 0600 0601 setMapTheme(group->readEntry("Marble Map Theme", d->cacheMapTheme)); 0602 setProjection(group->readEntry("Marble Projection", d->cacheProjection)); 0603 setShowCompass(group->readEntry("Marble Show Compass", d->cacheShowCompass)); 0604 setShowScaleBar(group->readEntry("Marble Show Scale Bar", d->cacheShowScaleBar)); 0605 setShowNavigation(group->readEntry("Marble Show Navigation", d->cacheShowNavigation)); 0606 setShowOverviewMap(group->readEntry("Marble Show Overview Map", d->cacheShowOverviewMap)); 0607 } 0608 0609 void BackendMarble::updateMarkers() 0610 { 0611 // just redraw, that's it: 0612 0613 reload(); 0614 } 0615 0616 void BackendMarble::reload() 0617 { 0618 if (!d->marbleWidget) 0619 { 0620 return; 0621 } 0622 0623 d->marbleWidget->update(); 0624 } 0625 0626 bool BackendMarble::screenCoordinates(const GeoCoordinates& coordinates, QPoint* const point) 0627 { 0628 if (!d->marbleWidget) 0629 { 0630 return false; 0631 } 0632 0633 if (!coordinates.hasCoordinates()) 0634 { 0635 return false; 0636 } 0637 0638 qreal x, y; 0639 const bool isVisible = d->marbleWidget->screenCoordinates(coordinates.lon(), coordinates.lat(), x, y); 0640 0641 if (!isVisible) 0642 { 0643 return false; 0644 } 0645 0646 if (point) 0647 { 0648 *point = QPoint(x, y); 0649 } 0650 0651 return true; 0652 } 0653 0654 bool BackendMarble::geoCoordinates(const QPoint& point, GeoCoordinates* const coordinates) const 0655 { 0656 if (!d->marbleWidget) 0657 { 0658 return false; 0659 } 0660 0661 // apparently, MarbleWidget::GeoCoordinates can return true even if the object is not on the screen 0662 // check that the point is in the visible range: 0663 0664 if (!d->marbleWidget->rect().contains(point)) 0665 { 0666 return false; 0667 } 0668 0669 qreal lat, lon; 0670 const bool isVisible = d->marbleWidget->geoCoordinates(point.x(), point.y(), 0671 lon, lat, 0672 Marble::GeoDataCoordinates::Degree); 0673 0674 if (!isVisible) 0675 { 0676 return false; 0677 } 0678 0679 if (coordinates) 0680 { 0681 *coordinates = GeoCoordinates(lat, lon); 0682 } 0683 0684 return true; 0685 } 0686 0687 /** 0688 * @brief Replacement for Marble::GeoPainter::drawPixmap which takes a pixel offset 0689 * 0690 * @param painter Marble::GeoPainter on which to draw the pixmap 0691 * @param pixmap Pixmap to be drawn 0692 * @param coordinates GeoCoordinates where the image is to be drawn 0693 * @param offsetPoint Point in the @p pixmap which should be at @p coordinates 0694 */ 0695 void BackendMarble::GeoPainter_drawPixmapAtCoordinates(Marble::GeoPainter* const painter, 0696 const QPixmap& pixmap, 0697 const GeoCoordinates& coordinates, 0698 const QPoint& offsetPoint) 0699 { 0700 // base point starts at the top left of the pixmap 0701 0702 // try to convert the coordinates to pixels 0703 0704 QPoint pointOnScreen; 0705 0706 if (!screenCoordinates(coordinates, &pointOnScreen)) 0707 { 0708 return; 0709 } 0710 0711 // Marble::GeoPainter::drawPixmap(coordinates, pixmap) draws the pixmap centered on coordinates 0712 // therefore we calculate the pixel position of the center of the image if its offsetPoint 0713 // is to be at pointOnScreen: 0714 0715 const QSize pixmapSize = pixmap.size(); 0716 const QPoint pixmapHalfSize = QPoint(pixmapSize.width() / 2, pixmapSize.height() / 2); 0717 const QPoint drawPoint = pointOnScreen + pixmapHalfSize - offsetPoint; 0718 0719 // now re-calculate the coordinates of the new pixel coordinates: 0720 0721 GeoCoordinates drawGeoCoordinates; 0722 0723 if (!geoCoordinates(drawPoint, &drawGeoCoordinates)) 0724 { 0725 return; 0726 } 0727 0728 // convert to Marble datatype and draw: 0729 0730 const Marble::GeoDataCoordinates mcoord = drawGeoCoordinates.toMarbleCoordinates(); 0731 painter->drawPixmap(mcoord, pixmap); 0732 } 0733 0734 void BackendMarble::marbleCustomPaint(Marble::GeoPainter* painter) 0735 { 0736 if (!d->activeState) 0737 { 0738 return; 0739 } 0740 0741 // check whether the parameters of the map changed and we may have to update the clusters: 0742 0743 if ((d->clustersDirtyCacheLat != d->marbleWidget->centerLatitude()) || 0744 (d->clustersDirtyCacheLon != d->marbleWidget->centerLongitude()) || 0745 (d->clustersDirtyCacheProjection != d->marbleWidget->projection())) 0746 { 0747 /* 0748 qCDebug(DIGIKAM_GEOIFACE_LOG) << d->marbleWidget->centerLatitude() 0749 << d->marbleWidget->centerLongitude() 0750 << d->marbleWidget->projection(); 0751 */ 0752 d->clustersDirtyCacheLat = d->marbleWidget->centerLatitude(); 0753 d->clustersDirtyCacheLon = d->marbleWidget->centerLongitude(); 0754 d->clustersDirtyCacheProjection = d->marbleWidget->projection(); 0755 s->worldMapWidget->markClustersAsDirty(); 0756 } 0757 0758 painter->save(); 0759 0760 if (s->trackManager) 0761 { 0762 if (s->trackManager->getVisibility()) 0763 { 0764 TrackManager::Track::List const& tracks = s->trackManager->getTrackList(); 0765 0766 for (int trackIdx = 0 ; trackIdx < tracks.count() ; ++trackIdx) 0767 { 0768 TrackManager::Track const& track = tracks.at(trackIdx); 0769 0770 if (track.points.count() < 2) 0771 { 0772 continue; 0773 } 0774 0775 Marble::GeoDataLineString lineString; 0776 0777 if (d->trackCache.contains(track.id)) 0778 { 0779 lineString = d->trackCache.value(track.id); 0780 } 0781 else 0782 { 0783 for (int coordIdx = 0 ; coordIdx < track.points.count() ; ++coordIdx) 0784 { 0785 GeoCoordinates const& coordinates = track.points.at(coordIdx).coordinates; 0786 const Marble::GeoDataCoordinates marbleCoordinates = coordinates.toMarbleCoordinates(); 0787 lineString << marbleCoordinates; 0788 } 0789 0790 d->trackCache.insert(track.id, lineString); 0791 } 0792 0793 /// @TODO looks a bit too thick IMHO when you zoom out. 0794 /// Maybe adjust to zoom level? 0795 0796 QColor trackColor = track.color; 0797 trackColor.setAlpha(180); 0798 painter->setPen(QPen(QBrush(trackColor),5)); 0799 painter->drawPolyline(lineString); 0800 } 0801 } 0802 } 0803 0804 for (int i = 0 ; i < s->ungroupedModels.count() ; ++i) 0805 { 0806 GeoModelHelper* const modelHelper = s->ungroupedModels.at(i); 0807 0808 if (!modelHelper->modelFlags().testFlag(GeoModelHelper::FlagVisible)) 0809 { 0810 continue; 0811 } 0812 0813 QAbstractItemModel* const model = modelHelper->model(); 0814 0815 // render all visible markers: 0816 0817 for (int row = 0 ; row < model->rowCount() ; ++row) 0818 { 0819 const QModelIndex currentIndex = model->index(row, 0); 0820 GeoCoordinates markerCoordinates; 0821 0822 if (!modelHelper->itemCoordinates(currentIndex, &markerCoordinates)) 0823 { 0824 continue; 0825 } 0826 0827 // is the marker being moved right now? 0828 0829 if (currentIndex == d->mouseMoveMarkerIndex) 0830 { 0831 markerCoordinates = d->mouseMoveObjectCoordinates; 0832 } 0833 0834 QPoint markerPoint; 0835 0836 if (!screenCoordinates(markerCoordinates, &markerPoint)) 0837 { 0838 /// @todo This check does not work properly in all cases! 0839 // the marker is not visible 0840 0841 continue; 0842 } 0843 0844 QPoint markerOffsetPoint; 0845 QPixmap markerPixmap; 0846 const bool haveMarkerPixmap = modelHelper->itemIcon(currentIndex, 0847 &markerOffsetPoint, nullptr, 0848 &markerPixmap, nullptr); 0849 0850 if (!haveMarkerPixmap || markerPixmap.isNull()) 0851 { 0852 markerPixmap = GeoIfaceGlobalObject::instance()->getStandardMarkerPixmap(); 0853 markerOffsetPoint = QPoint(markerPixmap.width() / 2, markerPixmap.height() - 1); 0854 } 0855 0856 GeoPainter_drawPixmapAtCoordinates(painter, markerPixmap, markerCoordinates, markerOffsetPoint); 0857 } 0858 } 0859 0860 int markersInMovingCluster = 0; 0861 0862 if (s->markerModel) 0863 { 0864 // now for the clusters: 0865 0866 s->worldMapWidget->updateClusters(); 0867 0868 for (int i = 0 ; i < s->clusterList.size() ; ++i) 0869 { 0870 const GeoIfaceCluster& cluster = s->clusterList.at(i); 0871 GeoCoordinates clusterCoordinates = cluster.coordinates; 0872 int markerCountOverride = cluster.markerCount; 0873 GeoGroupState selectionStateOverride = cluster.groupState; 0874 0875 if (d->haveMouseMovingObject && (d->mouseMoveClusterIndex >= 0)) 0876 { 0877 bool movingSelectedMarkers = s->clusterList.at(d->mouseMoveClusterIndex).groupState != SelectedNone; 0878 0879 if (movingSelectedMarkers) 0880 { 0881 markersInMovingCluster += cluster.markerSelectedCount; 0882 markerCountOverride -= cluster.markerSelectedCount; 0883 selectionStateOverride = SelectedNone; 0884 } 0885 else if (d->mouseMoveClusterIndex == i) 0886 { 0887 markerCountOverride = 0; 0888 } 0889 0890 if (markerCountOverride == 0) 0891 { 0892 continue; 0893 } 0894 } 0895 0896 QPoint clusterPoint; 0897 0898 if (!screenCoordinates(clusterCoordinates, &clusterPoint)) 0899 { 0900 /// @todo This check does not work properly in all cases! 0901 // cluster is not visible 0902 0903 continue; 0904 } 0905 0906 QPoint clusterOffsetPoint; 0907 const QPixmap clusterPixmap = s->worldMapWidget->getDecoratedPixmapForCluster(i, 0908 &selectionStateOverride, 0909 &markerCountOverride, 0910 &clusterOffsetPoint); 0911 0912 GeoPainter_drawPixmapAtCoordinates(painter, 0913 clusterPixmap, 0914 clusterCoordinates, 0915 clusterOffsetPoint); 0916 } 0917 } 0918 0919 // now render the mouse-moving cluster, if there is one: 0920 0921 if (d->haveMouseMovingObject && (d->mouseMoveClusterIndex >= 0)) 0922 { 0923 const GeoIfaceCluster& cluster = s->clusterList.at(d->mouseMoveClusterIndex); 0924 GeoCoordinates clusterCoordinates = d->mouseMoveObjectCoordinates; 0925 int markerCountOverride = (markersInMovingCluster>0)?markersInMovingCluster:cluster.markerCount; 0926 GeoGroupState selectionStateOverride = cluster.groupState; 0927 QPoint clusterPoint; 0928 0929 if (screenCoordinates(clusterCoordinates, &clusterPoint)) 0930 { 0931 // determine the colors: 0932 0933 QColor fillColor; 0934 QColor strokeColor; 0935 Qt::PenStyle strokeStyle; 0936 QColor labelColor; 0937 QString labelText; 0938 s->worldMapWidget->getColorInfos(d->mouseMoveClusterIndex, 0939 &fillColor, &strokeColor, 0940 &strokeStyle, &labelText, 0941 &labelColor, &selectionStateOverride, 0942 &markerCountOverride); 0943 0944 QString pixmapName = fillColor.name().mid(1); 0945 0946 if (cluster.groupState == SelectedAll) 0947 { 0948 pixmapName += QLatin1String("-selected"); 0949 } 0950 0951 if (cluster.groupState == SelectedSome) 0952 { 0953 pixmapName += QLatin1String("-someselected"); 0954 } 0955 0956 const QPixmap& markerPixmap = GeoIfaceGlobalObject::instance()->getMarkerPixmap(pixmapName); 0957 painter->drawPixmap(clusterPoint.x() - markerPixmap.width() / 2, 0958 clusterPoint.y() - markerPixmap.height() - 1, 0959 markerPixmap); 0960 } 0961 } 0962 0963 // now render the drag-and-drop marker, if there is one: 0964 0965 if (d->dragDropMarkerCount>0) 0966 { 0967 // determine the colors: 0968 0969 QColor fillColor; 0970 QColor strokeColor; 0971 Qt::PenStyle strokeStyle; 0972 QColor labelColor; 0973 QString labelText; 0974 s->worldMapWidget->getColorInfos(SelectedAll, d->dragDropMarkerCount, 0975 &fillColor, &strokeColor, 0976 &strokeStyle, &labelText, &labelColor); 0977 0978 QString pixmapName = fillColor.name().mid(1); 0979 pixmapName += QLatin1String("-selected"); 0980 const QPixmap& markerPixmap = GeoIfaceGlobalObject::instance()->getMarkerPixmap(pixmapName); 0981 0982 painter->drawPixmap(d->dragDropMarkerPos.x() - markerPixmap.width() / 2, 0983 d->dragDropMarkerPos.y() - markerPixmap.height() - 1, 0984 markerPixmap); 0985 } 0986 0987 // here we draw the selection rectangle which is being made by the user right now 0988 /// @todo merge drawing of the rectangles into one function 0989 0990 if (d->displayedRectangle.first.hasCoordinates()) 0991 { 0992 drawSearchRectangle(painter, d->displayedRectangle, false); 0993 } 0994 0995 // draw the current or old search rectangle 0996 0997 if (s->selectionRectangle.first.hasCoordinates()) 0998 { 0999 drawSearchRectangle(painter, s->selectionRectangle, 1000 d->intermediateSelectionPoint.hasCoordinates()); 1001 } 1002 1003 painter->restore(); 1004 } 1005 1006 QString BackendMarble::getProjection() const 1007 { 1008 /// @todo Do we actually need to read out the projection from the widget??? 1009 1010 if (d->marbleWidget) 1011 { 1012 const Marble::Projection currentProjection = d->marbleWidget->projection(); 1013 1014 switch (currentProjection) 1015 { 1016 case Marble::Equirectangular: 1017 d->cacheProjection = QLatin1String("equirectangular"); 1018 break; 1019 1020 case Marble::Mercator: 1021 d->cacheProjection = QLatin1String("mercator"); 1022 break; 1023 1024 default: 1025 case Marble::Spherical: 1026 d->cacheProjection = QLatin1String("spherical"); 1027 break; 1028 } 1029 } 1030 1031 return d->cacheProjection; 1032 } 1033 1034 void BackendMarble::setProjection(const QString& newProjection) 1035 { 1036 d->cacheProjection = newProjection; 1037 1038 if (d->marbleWidget) 1039 { 1040 if (newProjection == QLatin1String("equirectangular")) 1041 { 1042 d->marbleWidget->setProjection(Marble::Equirectangular); 1043 } 1044 else if (newProjection == QLatin1String("mercator")) 1045 { 1046 d->marbleWidget->setProjection(Marble::Mercator); 1047 } 1048 else // "spherical" 1049 { 1050 d->marbleWidget->setProjection(Marble::Spherical); 1051 } 1052 } 1053 1054 updateActionAvailability(); 1055 } 1056 1057 void BackendMarble::slotProjectionActionTriggered(QAction* action) 1058 { 1059 setProjection(action->data().toString()); 1060 } 1061 1062 void BackendMarble::setShowCompass(const bool state) 1063 { 1064 d->cacheShowCompass = state; 1065 updateActionAvailability(); 1066 1067 if (d->marbleWidget) 1068 { 1069 Marble::AbstractFloatItem* const item = d->marbleWidget->floatItem(QLatin1String("compass")); 1070 1071 if (item) 1072 { 1073 item->setVisible(state); 1074 } 1075 } 1076 } 1077 1078 void BackendMarble::setShowScaleBar(const bool state) 1079 { 1080 d->cacheShowScaleBar = state; 1081 updateActionAvailability(); 1082 1083 if (d->marbleWidget) 1084 { 1085 Marble::AbstractFloatItem* const item = d->marbleWidget->floatItem(QLatin1String("scalebar")); 1086 1087 if (item) 1088 { 1089 item->setVisible(state); 1090 } 1091 } 1092 } 1093 1094 void BackendMarble::setShowNavigation(const bool state) 1095 { 1096 d->cacheShowNavigation = state; 1097 updateActionAvailability(); 1098 1099 if (d->marbleWidget) 1100 { 1101 Marble::AbstractFloatItem* const item = d->marbleWidget->floatItem(QLatin1String("navigation")); 1102 1103 if (item) 1104 { 1105 item->setVisible(state); 1106 } 1107 } 1108 } 1109 1110 void BackendMarble::setShowOverviewMap(const bool state) 1111 { 1112 d->cacheShowOverviewMap = state; 1113 updateActionAvailability(); 1114 1115 if (d->marbleWidget) 1116 { 1117 Marble::AbstractFloatItem* const item = d->marbleWidget->floatItem(QLatin1String("overviewmap")); 1118 1119 if (item) 1120 { 1121 item->setVisible(state); 1122 } 1123 } 1124 } 1125 1126 void BackendMarble::slotFloatSettingsTriggered(QAction* action) 1127 { 1128 const QString actionIdString = action->data().toString(); 1129 const bool actionState = action->isChecked(); 1130 1131 if (actionIdString == QLatin1String("showcompass")) 1132 { 1133 setShowCompass(actionState); 1134 } 1135 else if (actionIdString == QLatin1String("showscalebar")) 1136 { 1137 setShowScaleBar(actionState); 1138 } 1139 else if (actionIdString == QLatin1String("shownavigation")) 1140 { 1141 setShowNavigation(actionState); 1142 } 1143 else if (actionIdString == QLatin1String("showoverviewmap")) 1144 { 1145 setShowOverviewMap(actionState); 1146 } 1147 } 1148 1149 void BackendMarble::slotClustersNeedUpdating() 1150 { 1151 if (!d->marbleWidget) 1152 { 1153 return; 1154 } 1155 1156 // tell the widget to redraw: 1157 1158 d->marbleWidget->update(); 1159 } 1160 1161 void BackendMarble::updateClusters() 1162 { 1163 // clusters are only needed during redraw 1164 } 1165 1166 QSize BackendMarble::mapSize() const 1167 { 1168 return d->marbleWidget->size(); 1169 } 1170 1171 void BackendMarble::slotMarbleZoomChanged() 1172 { 1173 // ignore the zoom change while changing the map theme 1174 1175 if (d->blockingZoomWhileChangingTheme) 1176 { 1177 return; 1178 } 1179 1180 const QString newZoomString = getZoom(); 1181 s->worldMapWidget->markClustersAsDirty(); 1182 updateActionAvailability(); 1183 1184 Q_EMIT signalZoomChanged(newZoomString); 1185 } 1186 1187 void BackendMarble::setZoom(const QString& newZoom) 1188 { 1189 const QString myZoomString = s->worldMapWidget->convertZoomToBackendZoom(newZoom, QLatin1String("marble")); 1190 1191 GEOIFACE_ASSERT(myZoomString.startsWith(QLatin1String("marble:"))); 1192 1193 const int myZoom = myZoomString.mid(QString::fromLatin1("marble:").length()).toInt(); 1194 d->cacheZoom = myZoom; 1195 d->marbleWidget->setZoom(myZoom); 1196 } 1197 1198 QString BackendMarble::getZoom() const 1199 { 1200 if (d->marbleWidget) 1201 { 1202 d->cacheZoom = d->marbleWidget->zoom(); 1203 } 1204 1205 return QString::fromLatin1("marble:%1").arg(d->cacheZoom); 1206 } 1207 1208 int BackendMarble::getMarkerModelLevel() 1209 { 1210 /* 1211 return AbstractMarkerTiler::TileIndex::MaxLevel-1; 1212 */ 1213 GEOIFACE_ASSERT(isReady()); 1214 1215 if (!isReady()) 1216 { 1217 return 0; 1218 } 1219 1220 const int currentZoom = d->marbleWidget->zoom(); 1221 int tileLevel = 0; 1222 const Marble::Projection currentProjection = d->marbleWidget->projection(); 1223 1224 switch (currentProjection) 1225 { 1226 case Marble::Equirectangular: 1227 1228 if (currentZoom < 1000) { tileLevel = 4; } 1229 else if (currentZoom < 1400) { tileLevel = 5; } 1230 else if (currentZoom < 1900) { tileLevel = 6; } 1231 else if (currentZoom < 2300) { tileLevel = 7; } 1232 else if (currentZoom < 2800) { tileLevel = 8; } 1233 else { tileLevel = 9; } 1234 1235 // note: level 9 is not enough starting at zoom level 3200 1236 break; 1237 1238 case Marble::Mercator: 1239 1240 if (currentZoom < 1000) { tileLevel = 4; } 1241 else if (currentZoom < 1500) { tileLevel = 5; } 1242 else if (currentZoom < 1900) { tileLevel = 6; } 1243 else if (currentZoom < 2300) { tileLevel = 7; } 1244 else if (currentZoom < 2800) { tileLevel = 8; } 1245 else { tileLevel = 9; } 1246 1247 // note: level 9 is not enough starting at zoom level 3200 1248 break; 1249 1250 default: 1251 case Marble::Spherical: 1252 1253 if (currentZoom < 1300) { tileLevel = 5; } 1254 else if (currentZoom < 1800) { tileLevel = 6; } 1255 else if (currentZoom < 2200) { tileLevel = 7; } 1256 else if (currentZoom < 2800) { tileLevel = 8; } 1257 else { tileLevel = 9; } 1258 1259 // note: level 9 is not enough starting at zoom level 3200 1260 break; 1261 } 1262 1263 // TODO: verify that this assertion was too strict 1264 /* 1265 GEOIFACE_ASSERT(tileLevel <= AbstractMarkerTiler::TileIndex::MaxLevel-1); 1266 */ 1267 return tileLevel; 1268 } 1269 1270 GeoCoordinates::PairList BackendMarble::getNormalizedBounds() 1271 { 1272 if (!d->marbleWidget) 1273 { 1274 return GeoCoordinates::PairList(); 1275 } 1276 1277 const Marble::GeoDataLatLonAltBox marbleBounds = d->marbleWidget->viewport()->viewLatLonAltBox(); 1278 /* 1279 qCDebug(DIGIKAM_GEOIFACE_LOG) << marbleBounds.toString(GeoDataCoordinates::Degree); 1280 */ 1281 const GeoCoordinates::Pair boundsPair = GeoCoordinates::makePair( 1282 marbleBounds.south(Marble::GeoDataCoordinates::Degree), 1283 marbleBounds.west(Marble::GeoDataCoordinates::Degree), 1284 marbleBounds.north(Marble::GeoDataCoordinates::Degree), 1285 marbleBounds.east(Marble::GeoDataCoordinates::Degree)); 1286 /* 1287 qCDebug(DIGIKAM_GEOIFACE_LOG) << boundsPair.first<<boundsPair.second; 1288 qCDebug(DIGIKAM_GEOIFACE_LOG) << GeoIfaceHelperNormalizeBounds(boundsPair); 1289 */ 1290 return GeoIfaceHelperNormalizeBounds(boundsPair); 1291 } 1292 1293 bool BackendMarble::eventFilter(QObject* object, QEvent* event) 1294 { 1295 if (object != d->marbleWidget) 1296 { 1297 // event not filtered, because it is not for our object 1298 1299 return QObject::eventFilter(object, event); 1300 } 1301 1302 // we only handle mouse events: 1303 1304 if ((event->type() != QEvent::MouseButtonPress) && 1305 (event->type() != QEvent::MouseMove) && 1306 (event->type() != QEvent::MouseButtonRelease)) 1307 { 1308 return QObject::eventFilter(object, event); 1309 } 1310 1311 // no filtering in pan mode 1312 1313 if (s->currentMouseMode == MouseModePan) 1314 { 1315 return QObject::eventFilter(object, event); 1316 } 1317 1318 QMouseEvent* const mouseEvent = static_cast<QMouseEvent*>(event); 1319 bool doFilterEvent = false; 1320 1321 if (s->currentMouseMode == MouseModeRegionSelection) 1322 { 1323 if ((event->type() == QEvent::MouseButtonPress) && 1324 (mouseEvent->button() == Qt::LeftButton)) 1325 { 1326 // we need to filter this event because otherwise Marble displays 1327 // a left click context menu 1328 1329 doFilterEvent = true; 1330 } 1331 else if (event->type() == QEvent::MouseMove) 1332 { 1333 if (d->firstSelectionPoint.hasCoordinates()) 1334 { 1335 d->intermediateSelectionPoint.clear(); 1336 geoCoordinates(mouseEvent->pos(), &d->intermediateSelectionPoint); 1337 d->intermediateSelectionScreenPoint = mouseEvent->pos(); 1338 1339 qCDebug(DIGIKAM_GEOIFACE_LOG) << d->firstSelectionScreenPoint 1340 << " " 1341 << d->intermediateSelectionScreenPoint; 1342 1343 qreal lonWest, latNorth, lonEast, latSouth; 1344 1345 if (d->firstSelectionScreenPoint.x() < d->intermediateSelectionScreenPoint.x()) 1346 { 1347 lonWest = d->firstSelectionPoint.lon(); 1348 lonEast = d->intermediateSelectionPoint.lon(); 1349 } 1350 else 1351 { 1352 lonWest = d->intermediateSelectionPoint.lon(); 1353 lonEast = d->firstSelectionPoint.lon(); 1354 } 1355 1356 if (d->firstSelectionScreenPoint.y() < d->intermediateSelectionScreenPoint.y()) 1357 { 1358 latNorth = d->firstSelectionPoint.lat(); 1359 latSouth = d->intermediateSelectionPoint.lat(); 1360 } 1361 else 1362 { 1363 latNorth = d->intermediateSelectionPoint.lat(); 1364 latSouth = d->firstSelectionPoint.lat(); 1365 } 1366 1367 const GeoCoordinates::Pair selectionCoordinates( 1368 GeoCoordinates(latNorth, lonWest), 1369 GeoCoordinates(latSouth, lonEast)); 1370 1371 //setSelectionRectangle(selectionCoordinates, SelectionRectangle); 1372 1373 d->displayedRectangle = selectionCoordinates; 1374 d->marbleWidget->update(); 1375 } 1376 1377 doFilterEvent = true; 1378 } 1379 else if ((event->type() == QEvent::MouseButtonRelease) && 1380 (mouseEvent->button() == Qt::LeftButton)) 1381 { 1382 if (!d->firstSelectionPoint.hasCoordinates()) 1383 { 1384 geoCoordinates(mouseEvent->pos(), &d->firstSelectionPoint); 1385 d->firstSelectionScreenPoint = mouseEvent->pos(); 1386 } 1387 else 1388 { 1389 d->intermediateSelectionPoint.clear(); 1390 1391 GeoCoordinates secondSelectionPoint; 1392 geoCoordinates(mouseEvent->pos(), &secondSelectionPoint); 1393 QPoint secondSelectionScreenPoint = mouseEvent->pos(); 1394 1395 qreal lonWest, latNorth, lonEast, latSouth; 1396 1397 if (d->firstSelectionScreenPoint.x() < secondSelectionScreenPoint.x()) 1398 { 1399 lonWest = d->firstSelectionPoint.lon(); 1400 lonEast = secondSelectionPoint.lon(); 1401 } 1402 else 1403 { 1404 lonWest = secondSelectionPoint.lon(); 1405 lonEast = d->firstSelectionPoint.lon(); 1406 } 1407 1408 if (d->firstSelectionScreenPoint.y() < secondSelectionScreenPoint.y()) 1409 { 1410 latNorth = d->firstSelectionPoint.lat(); 1411 latSouth = secondSelectionPoint.lat(); 1412 } 1413 else 1414 { 1415 latNorth = secondSelectionPoint.lat(); 1416 latSouth = d->firstSelectionPoint.lat(); 1417 } 1418 1419 const GeoCoordinates::Pair selectionCoordinates( 1420 GeoCoordinates(latNorth, lonWest), 1421 GeoCoordinates(latSouth, lonEast)); 1422 1423 d->firstSelectionPoint.clear(); 1424 d->displayedRectangle.first.clear(); 1425 1426 Q_EMIT signalSelectionHasBeenMade(selectionCoordinates); 1427 } 1428 1429 doFilterEvent = true; 1430 } 1431 } 1432 else 1433 { 1434 if ((event->type() == QEvent::MouseButtonPress) && 1435 (mouseEvent->button() == Qt::LeftButton)) 1436 { 1437 // check whether the user clicked on one of our items: 1438 // scan in reverse order, because the user would expect 1439 // the topmost marker to be picked up and not the 1440 // one below 1441 /* 1442 if (s->specialMarkersModel) 1443 { 1444 for (int row = s->specialMarkersModel->rowCount()-1 ; row >= 0 ; --row) 1445 { 1446 const QModelIndex currentIndex = s->specialMarkersModel->index(row, 0); 1447 const GeoCoordinates currentCoordinates = s->specialMarkersModel->data(currentIndex, s->specialMarkersCoordinatesRole).value<GeoCoordinates>(); 1448 1449 QPoint markerPoint; 1450 1451 if (!screenCoordinates(currentCoordinates, &markerPoint)) 1452 { 1453 continue; 1454 } 1455 1456 const int markerPixmapHeight = s->markerPixmap.height(); 1457 const int markerPixmapWidth = s->markerPixmap.width(); 1458 const QRect markerRect(markerPoint.x()-markerPixmapWidth/2, markerPoint.y()-markerPixmapHeight, markerPixmapWidth, markerPixmapHeight); 1459 1460 if (!markerRect.contains(mouseEvent->pos())) 1461 { 1462 continue; 1463 } 1464 1465 // the user clicked on a marker: 1466 1467 d->mouseMoveMarkerIndex = QPersistentModelIndex(currentIndex); 1468 d->mouseMoveCenterOffset = mouseEvent->pos() - markerPoint; 1469 d->mouseMoveObjectCoordinates = currentCoordinates; 1470 doFilterEvent = true; 1471 d->havePotentiallyMouseMovingObject = true; 1472 1473 break; 1474 } 1475 } 1476 */ 1477 if ( 1478 /* 1479 s->inEditMode && 1480 */ 1481 // cppcheck-suppress knownConditionTrueFalse 1482 !doFilterEvent 1483 ) 1484 { 1485 // scan in reverse order of painting! 1486 1487 for (int clusterIndex = s->clusterList.count()-1 ; 1488 clusterIndex >= 0 ; 1489 --clusterIndex) 1490 { 1491 const GeoIfaceCluster& cluster = s->clusterList.at(clusterIndex); 1492 const GeoCoordinates currentCoordinates = cluster.coordinates; 1493 QPoint clusterPoint; 1494 1495 if (!screenCoordinates(currentCoordinates, &clusterPoint)) 1496 { 1497 continue; 1498 } 1499 1500 QRect markerRect; 1501 markerRect.setSize(cluster.pixmapSize); 1502 markerRect.moveTopLeft(clusterPoint); 1503 markerRect.translate(-cluster.pixmapOffset); 1504 1505 if (!markerRect.contains(mouseEvent->pos())) 1506 { 1507 continue; 1508 } 1509 1510 /// @todo For circles, make sure the mouse is really above the circle and not just in the rectangle! 1511 1512 // the user clicked on a cluster: 1513 1514 d->mouseMoveClusterIndex = clusterIndex; 1515 d->mouseMoveCenterOffset = mouseEvent->pos() - clusterPoint; 1516 d->mouseMoveObjectCoordinates = currentCoordinates; 1517 doFilterEvent = true; 1518 d->havePotentiallyMouseMovingObject = true; 1519 s->haveMovingCluster = true; 1520 1521 break; 1522 } 1523 } 1524 } 1525 else if ((event->type() == QEvent::MouseMove) && 1526 (d->havePotentiallyMouseMovingObject || d->haveMouseMovingObject)) 1527 { 1528 if ((!s->modificationsAllowed) || 1529 (!s->markerModel->tilerFlags().testFlag(AbstractMarkerTiler::FlagMovable)) || 1530 ((d->mouseMoveClusterIndex >= 0) && s->showThumbnails)) 1531 { 1532 // clusters only move in edit mode and when edit mode is enabled 1533 /// @todo This blocks moving of the map in non-edit mode 1534 1535 d->havePotentiallyMouseMovingObject = false; 1536 d->mouseMoveClusterIndex = -1; 1537 d->mouseMoveMarkerIndex = QPersistentModelIndex(); 1538 s->haveMovingCluster = false; 1539 } 1540 else 1541 { 1542 1543 // mark the object as really moving: 1544 1545 d->havePotentiallyMouseMovingObject = false; 1546 d->haveMouseMovingObject = true; 1547 1548 // a cluster or marker is being moved. update its position: 1549 1550 QPoint newMarkerPoint = mouseEvent->pos() - d->mouseMoveCenterOffset; 1551 QPoint snapPoint; 1552 1553 if (findSnapPoint(newMarkerPoint, &snapPoint, nullptr, nullptr)) 1554 { 1555 newMarkerPoint = snapPoint; 1556 } 1557 1558 GeoCoordinates newCoordinates; 1559 1560 if (geoCoordinates(newMarkerPoint, &newCoordinates)) 1561 { 1562 d->mouseMoveObjectCoordinates = newCoordinates; 1563 d->marbleWidget->update(); 1564 } 1565 } 1566 } 1567 else if ((event->type() == QEvent::MouseButtonRelease) && 1568 (d->havePotentiallyMouseMovingObject)) 1569 { 1570 // the object was not moved, but just clicked once 1571 1572 if (d->mouseMoveClusterIndex >= 0) 1573 { 1574 const int mouseMoveClusterIndex = d->mouseMoveClusterIndex; 1575 1576 // we are done with the clicked object 1577 // reset these before sending the signal 1578 1579 d->havePotentiallyMouseMovingObject = false; 1580 d->mouseMoveClusterIndex = -1; 1581 d->mouseMoveMarkerIndex = QPersistentModelIndex(); 1582 s->haveMovingCluster = false; 1583 doFilterEvent = true; 1584 1585 Q_EMIT signalClustersClicked(QIntList() << mouseMoveClusterIndex); 1586 } 1587 else 1588 { 1589 // we are done with the clicked object: 1590 1591 d->havePotentiallyMouseMovingObject = false; 1592 d->mouseMoveClusterIndex = -1; 1593 d->mouseMoveMarkerIndex = QPersistentModelIndex(); 1594 s->haveMovingCluster = false; 1595 } 1596 } 1597 else if ((event->type() == QEvent::MouseButtonRelease) && 1598 (d->haveMouseMovingObject)) 1599 { 1600 // the object was dropped, apply the coordinates if it is on screen: 1601 1602 const QPoint dropMarkerPoint = mouseEvent->pos() - d->mouseMoveCenterOffset; 1603 1604 QPair<int, QModelIndex> snapTargetIndex(-1, QModelIndex()); 1605 GeoCoordinates newCoordinates; 1606 bool haveValidPoint = findSnapPoint(dropMarkerPoint, nullptr, 1607 &newCoordinates, 1608 &snapTargetIndex); 1609 1610 if (!haveValidPoint) 1611 { 1612 haveValidPoint = geoCoordinates(dropMarkerPoint, &newCoordinates); 1613 } 1614 1615 if (haveValidPoint) 1616 { 1617 if (d->mouseMoveMarkerIndex.isValid()) 1618 { 1619 /* 1620 // the marker was dropped to valid coordinates 1621 1622 s->specialMarkersModel->setData(d->mouseMoveMarkerIndex, QVariant::fromValue(newCoordinates), s->specialMarkersCoordinatesRole); 1623 1624 QList<QPersistentModelIndex> markerIndices; 1625 markerIndices << d->mouseMoveMarkerIndex; 1626 1627 // also Q_EMIT a signal that the marker was moved: 1628 1629 Q_EMIT signalSpecialMarkersMoved(markerIndices); 1630 */ 1631 } 1632 else 1633 { 1634 // a cluster is being moved 1635 1636 s->clusterList[d->mouseMoveClusterIndex].coordinates = newCoordinates; 1637 Q_EMIT signalClustersMoved(QIntList() << d->mouseMoveClusterIndex, snapTargetIndex); 1638 } 1639 } 1640 1641 d->haveMouseMovingObject = false; 1642 d->mouseMoveClusterIndex = -1; 1643 d->mouseMoveMarkerIndex = QPersistentModelIndex(); 1644 d->marbleWidget->update(); 1645 s->haveMovingCluster = false; 1646 } 1647 } 1648 1649 if (doFilterEvent) 1650 { 1651 return true; 1652 } 1653 1654 return QObject::eventFilter(object, event); 1655 } 1656 1657 /* 1658 void BackendMarble::updateDragDropMarker(const QPoint& pos, const GeoIfaceDragData* const dragData) 1659 { 1660 if (!dragData) 1661 { 1662 d->dragDropMarkerCount = 0; 1663 } 1664 else 1665 { 1666 d->dragDropMarkerPos = pos; 1667 d->dragDropMarkerCount = dragData->itemCount; 1668 } 1669 1670 d->marbleWidget->update(); 1671 1672 // TODO: hide dragged markers on the map 1673 } 1674 1675 void BackendMarble::updateDragDropMarkerPosition(const QPoint& pos) 1676 { 1677 d->dragDropMarkerPos = pos; 1678 d->marbleWidget->update(); 1679 } 1680 */ 1681 1682 void BackendMarble::updateActionAvailability() 1683 { 1684 if ((!d->activeState) || (!d->marbleWidget)) 1685 { 1686 return; 1687 } 1688 1689 qCDebug(DIGIKAM_GEOIFACE_LOG) << d->cacheZoom 1690 << d->marbleWidget->maximumZoom() 1691 << d->marbleWidget->minimumZoom(); 1692 1693 s->worldMapWidget->getControlAction(QLatin1String("zoomin"))->setEnabled(d->cacheZoom<d->marbleWidget->maximumZoom()); 1694 s->worldMapWidget->getControlAction(QLatin1String("zoomout"))->setEnabled(d->cacheZoom>d->marbleWidget->minimumZoom()); 1695 const QList<QAction*> mapThemeActions = d->actionGroupMapTheme->actions(); 1696 1697 for (int i = 0 ; i < mapThemeActions.size() ; ++i) 1698 { 1699 mapThemeActions.at(i)->setChecked(mapThemeActions.at(i)->data().toString() == getMapTheme()); 1700 } 1701 1702 const QList<QAction*> projectionActions = d->actionGroupProjection->actions(); 1703 1704 for (int i = 0 ; i < projectionActions.size() ; ++i) 1705 { 1706 projectionActions.at(i)->setChecked(projectionActions.at(i)->data().toString() == d->cacheProjection); 1707 } 1708 1709 d->actionShowCompass->setChecked(d->cacheShowCompass); 1710 d->actionShowScaleBar->setChecked(d->cacheShowScaleBar); 1711 d->actionShowNavigation->setChecked(d->cacheShowNavigation); 1712 d->actionShowOverviewMap->setChecked(d->cacheShowOverviewMap); 1713 } 1714 1715 void BackendMarble::slotThumbnailAvailableForIndex(const QVariant& index, const QPixmap& pixmap) 1716 { 1717 if (!d->marbleWidget) 1718 { 1719 return; 1720 } 1721 1722 qCDebug(DIGIKAM_GEOIFACE_LOG) << index << pixmap.size(); 1723 1724 if (pixmap.isNull() || !s->showThumbnails) 1725 { 1726 return; 1727 } 1728 1729 // TODO: properly reject pixmaps with the wrong size 1730 1731 const int expectedThumbnailSize = s->worldMapWidget->getUndecoratedThumbnailSize(); 1732 1733 if ((pixmap.size().height() > expectedThumbnailSize) || 1734 (pixmap.size().width() > expectedThumbnailSize)) 1735 { 1736 return; 1737 } 1738 1739 // re-paint the map 1740 1741 d->marbleWidget->update(); 1742 } 1743 1744 void BackendMarble::slotUngroupedModelChanged(const int index) 1745 { 1746 Q_UNUSED(index) 1747 1748 if (!d->marbleWidget) 1749 { 1750 return; 1751 } 1752 1753 d->marbleWidget->update(); 1754 } 1755 1756 void BackendMarble::slotTrackManagerChanged() 1757 { 1758 d->trackCache.clear(); 1759 1760 if (s->trackManager) 1761 { 1762 connect(s->trackManager, SIGNAL(signalTracksChanged(QList<TrackManager::TrackChanges>)), 1763 this, SLOT(slotTracksChanged(QList<TrackManager::TrackChanges>))); 1764 1765 // when the visibility of the tracks is changed, we simple schedule a redraw 1766 1767 connect(s->trackManager, SIGNAL(signalVisibilityChanged(bool)), 1768 this, SLOT(slotScheduleUpdate())); 1769 } 1770 1771 slotScheduleUpdate(); 1772 } 1773 1774 bool BackendMarble::findSnapPoint(const QPoint& actualPoint, 1775 QPoint* const snapPoint, 1776 GeoCoordinates* const snapCoordinates, 1777 QPair<int, QModelIndex>* const snapTargetIndex) 1778 { 1779 QPoint bestSnapPoint; 1780 GeoCoordinates bestSnapCoordinates; 1781 int bestSnapDistanceSquared = -1; 1782 QModelIndex bestSnapIndex; 1783 int bestSnapUngroupedModel = -1; 1784 1785 // now handle snapping: is there any object close by? 1786 1787 for (int im = 0 ; im < s->ungroupedModels.count() ; ++im) 1788 { 1789 GeoModelHelper* const modelHelper = s->ungroupedModels.at(im); 1790 1791 // TODO: test for active snapping 1792 1793 if ((!modelHelper->modelFlags().testFlag(GeoModelHelper::FlagVisible)) || 1794 (!modelHelper->modelFlags().testFlag(GeoModelHelper::FlagSnaps))) 1795 { 1796 continue; 1797 } 1798 1799 // TODO: configurable snapping radius 1800 1801 const int snapRadiusSquared = 10*10; 1802 QAbstractItemModel* const itemModel = modelHelper->model(); 1803 1804 for (int row = 0 ; row < itemModel->rowCount() ; ++row) 1805 { 1806 const QModelIndex currentIndex = itemModel->index(row, 0); 1807 GeoCoordinates currentCoordinates; 1808 1809 if (!modelHelper->itemCoordinates(currentIndex, ¤tCoordinates)) 1810 { 1811 continue; 1812 } 1813 1814 QPoint snapMarkerPoint; 1815 1816 if (!screenCoordinates(currentCoordinates, &snapMarkerPoint)) 1817 { 1818 continue; 1819 } 1820 1821 const QPoint distancePoint = snapMarkerPoint - actualPoint; 1822 const int snapDistanceSquared = (distancePoint.x()*distancePoint.x()+distancePoint.y()*distancePoint.y()); 1823 1824 if ((snapDistanceSquared <= snapRadiusSquared) && 1825 ((bestSnapDistanceSquared == -1) || (bestSnapDistanceSquared > snapDistanceSquared))) 1826 { 1827 bestSnapDistanceSquared = snapDistanceSquared; 1828 bestSnapPoint = snapMarkerPoint; 1829 bestSnapCoordinates = currentCoordinates; 1830 bestSnapIndex = currentIndex; 1831 bestSnapUngroupedModel = im; 1832 } 1833 } 1834 } 1835 1836 const bool foundSnapPoint = (bestSnapDistanceSquared >= 0); 1837 1838 if (foundSnapPoint) 1839 { 1840 if (snapPoint) 1841 { 1842 *snapPoint = bestSnapPoint; 1843 } 1844 1845 if (snapCoordinates) 1846 { 1847 *snapCoordinates = bestSnapCoordinates; 1848 } 1849 1850 if (snapTargetIndex) 1851 { 1852 *snapTargetIndex = QPair<int, QModelIndex>(bestSnapUngroupedModel, bestSnapIndex); 1853 } 1854 } 1855 1856 return foundSnapPoint; 1857 } 1858 1859 void BackendMarble::regionSelectionChanged() 1860 { 1861 if (d->marbleWidget && d->activeState) 1862 { 1863 d->marbleWidget->update(); 1864 } 1865 } 1866 1867 void BackendMarble::mouseModeChanged() 1868 { 1869 if (s->currentMouseMode != MouseModeRegionSelection) 1870 { 1871 d->firstSelectionPoint.clear(); 1872 d->intermediateSelectionPoint.clear(); 1873 1874 if (d->marbleWidget && d->activeState) 1875 { 1876 d->marbleWidget->update(); 1877 } 1878 } 1879 } 1880 1881 void BackendMarble::centerOn(const Marble::GeoDataLatLonBox& box, const bool useSaneZoomLevel) 1882 { 1883 if (!d->marbleWidget) 1884 { 1885 return; 1886 } 1887 1888 /** 1889 * @todo Boxes with very small width or height (<1e-6 or so) cause a deadlock in Marble 1890 * in spherical projection. 1891 * So instead, we just center on the center of the box and go to maximum zoom. 1892 * This does not yet handle the case of only width or height being too small though. 1893 */ 1894 1895 const bool boxTooSmall = qMin(box.width(), box.height()) < 0.000001; 1896 1897 if (boxTooSmall) 1898 { 1899 d->marbleWidget->centerOn(box.center()); 1900 d->marbleWidget->zoomView(useSaneZoomLevel ? qMin(3400, d->marbleWidget->maximumZoom()) 1901 : d->marbleWidget->maximumZoom()); 1902 } 1903 else 1904 { 1905 d->marbleWidget->centerOn(box, false); 1906 } 1907 1908 // simple check to see whether the zoom level is now too high 1909 /// @todo for very small boxes, Marbles zoom becomes -2billion. Catch this case here. 1910 1911 int maxZoomLevel = d->marbleWidget->maximumZoom(); 1912 1913 if (useSaneZoomLevel) 1914 { 1915 maxZoomLevel = qMin(maxZoomLevel, 3400); 1916 } 1917 1918 if ((d->marbleWidget->zoom()>maxZoomLevel) || 1919 (d->marbleWidget->zoom()<d->marbleWidget->minimumZoom())) 1920 { 1921 d->marbleWidget->zoomView(maxZoomLevel); 1922 } 1923 } 1924 1925 void BackendMarble::setActive(const bool state) 1926 { 1927 const bool oldState = d->activeState; 1928 d->activeState = state; 1929 1930 if (oldState != state) 1931 { 1932 if ((!state) && d->marbleWidget) 1933 { 1934 // we should share our widget in the list of widgets in the global object 1935 1936 GeoIfaceInternalWidgetInfo info; 1937 info.deleteFunction = deleteInfoFunction; 1938 info.widget = d->marbleWidget; 1939 info.currentOwner = this; 1940 info.backendName = backendName(); 1941 info.state = d->widgetIsDocked ? GeoIfaceInternalWidgetInfo::InternalWidgetStillDocked 1942 : GeoIfaceInternalWidgetInfo::InternalWidgetUndocked; 1943 1944 BMInternalWidgetInfo intInfo; 1945 intInfo.bmLayer = d->bmLayer; 1946 info.backendData.setValue(intInfo); 1947 1948 GeoIfaceGlobalObject* const go = GeoIfaceGlobalObject::instance(); 1949 go->addMyInternalWidgetToPool(info); 1950 } 1951 1952 if (state && d->marbleWidget) 1953 { 1954 // we should remove our widget from the list of widgets in the global object 1955 1956 GeoIfaceGlobalObject* const go = GeoIfaceGlobalObject::instance(); 1957 go->removeMyInternalWidgetFromPool(this); 1958 } 1959 } 1960 } 1961 1962 void BackendMarble::mapWidgetDocked(const bool state) 1963 { 1964 if (d->widgetIsDocked!=state) 1965 { 1966 GeoIfaceGlobalObject* const go = GeoIfaceGlobalObject::instance(); 1967 go->updatePooledWidgetState(d->marbleWidget, 1968 state ? GeoIfaceInternalWidgetInfo::InternalWidgetStillDocked 1969 : GeoIfaceInternalWidgetInfo::InternalWidgetUndocked); 1970 } 1971 1972 d->widgetIsDocked = state; 1973 } 1974 1975 void BackendMarble::drawSearchRectangle(Marble::GeoPainter* const painter, 1976 const GeoCoordinates::Pair& searchRectangle, 1977 const bool isOldRectangle) 1978 { 1979 const GeoCoordinates& topLeft = searchRectangle.first; 1980 const GeoCoordinates& bottomRight = searchRectangle.second; 1981 const qreal lonWest = topLeft.lon(); 1982 const qreal latNorth = topLeft.lat(); 1983 const qreal lonEast = bottomRight.lon(); 1984 const qreal latSouth = bottomRight.lat(); 1985 1986 Marble::GeoDataCoordinates coordTopLeft(lonWest, latNorth, 0, Marble::GeoDataCoordinates::Degree); 1987 Marble::GeoDataCoordinates coordTopRight(lonEast, latNorth, 0, Marble::GeoDataCoordinates::Degree); 1988 Marble::GeoDataCoordinates coordBottomLeft(lonWest, latSouth, 0, Marble::GeoDataCoordinates::Degree); 1989 Marble::GeoDataCoordinates coordBottomRight(lonEast, latSouth, 0, Marble::GeoDataCoordinates::Degree); 1990 Marble::GeoDataLinearRing polyRing; 1991 1992 polyRing << coordTopLeft << coordTopRight << coordBottomRight << coordBottomLeft; 1993 1994 QPen selectionPen; 1995 1996 if (isOldRectangle) 1997 { 1998 // there is a new selection in progress, 1999 // therefore display the current search rectangle in red 2000 2001 selectionPen.setColor(Qt::red); 2002 } 2003 else 2004 { 2005 selectionPen.setColor(Qt::blue); 2006 } 2007 2008 selectionPen.setStyle(Qt::SolidLine); 2009 selectionPen.setWidth(1); 2010 painter->setPen(selectionPen); 2011 painter->setBrush(Qt::NoBrush); 2012 painter->drawPolygon(polyRing); 2013 } 2014 2015 void BackendMarble::deleteInfoFunction(GeoIfaceInternalWidgetInfo* const info) 2016 { 2017 if (info->currentOwner) 2018 { 2019 qobject_cast<MapBackend*>(info->currentOwner.data())->releaseWidget(info); 2020 } 2021 2022 BMInternalWidgetInfo intInfo = info->backendData.value<BMInternalWidgetInfo>(); 2023 2024 if (intInfo.bmLayer) 2025 { 2026 delete intInfo.bmLayer; 2027 } 2028 2029 delete info->widget.data(); 2030 } 2031 2032 void BackendMarble::applyCacheToWidget() 2033 { 2034 /// @todo Do this only when the widget is active! 2035 2036 if (!d->marbleWidget) 2037 { 2038 return; 2039 } 2040 2041 setMapTheme(d->cacheMapTheme); 2042 setProjection(d->cacheProjection); 2043 setShowCompass(d->cacheShowCompass); 2044 setShowScaleBar(d->cacheShowScaleBar); 2045 setShowNavigation(d->cacheShowNavigation); 2046 setShowOverviewMap(d->cacheShowOverviewMap); 2047 } 2048 2049 void BackendMarble::slotTracksChanged(const QList<TrackManager::TrackChanges>& trackChanges) 2050 { 2051 // invalidate the cache for all changed tracks 2052 2053 Q_FOREACH (const TrackManager::TrackChanges& tc, trackChanges) 2054 { 2055 if (tc.second & (TrackManager::ChangeTrackPoints | TrackManager::ChangeRemoved)) 2056 { 2057 d->trackCache.remove(tc.first); 2058 } 2059 } 2060 2061 slotScheduleUpdate(); 2062 } 2063 2064 void BackendMarble::slotScheduleUpdate() 2065 { 2066 if (d->marbleWidget && d->activeState) 2067 { 2068 /// @TODO Put this onto the eventloop to collect update calls into one. 2069 2070 d->marbleWidget->update(); 2071 } 2072 } 2073 2074 } // namespace Digikam 2075 2076 #include "moc_backendmarble.cpp"