File indexing completed on 2024-04-28 15:15:57
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org> 0004 // SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org> 0005 // SPDX-FileCopyrightText: 2012 Illya Kovalevskyy <illya.kovalevskyy@gmail.com> 0006 // SPDX-FileCopyrightText: 2014 Gábor Péterffy <peterffy95@gmail.com> 0007 // 0008 0009 // Self 0010 #include "MarbleWidgetPopupMenu.h" 0011 0012 // Marble 0013 #include "AbstractDataPluginItem.h" 0014 #include "AbstractFloatItem.h" 0015 #include "MarbleAboutDialog.h" 0016 #include "MarbleDirs.h" 0017 #include "MarbleWidget.h" 0018 #include "MarbleModel.h" 0019 #include "GeoDataExtendedData.h" 0020 #include "GeoDataFolder.h" 0021 #include "GeoDataPlacemark.h" 0022 #include "GeoDataLookAt.h" 0023 #include "GeoDataData.h" 0024 #include "GeoDataSnippet.h" 0025 #include "GeoDataStyle.h" 0026 #include "GeoDataBalloonStyle.h" 0027 #include "GeoDataIconStyle.h" 0028 #include "GeoDataPoint.h" 0029 #include "GeoDataPhotoOverlay.h" 0030 #include "GeoSceneDocument.h" 0031 #include "GeoSceneHead.h" 0032 #include "MarbleClock.h" 0033 #include "MarbleDebug.h" 0034 #include "PopupLayer.h" 0035 #include "Planet.h" 0036 #include "routing/RoutingManager.h" 0037 #include "routing/RoutingLayer.h" 0038 #include "routing/RouteRequest.h" 0039 #include "EditBookmarkDialog.h" 0040 #include "BookmarkManager.h" 0041 #include "ReverseGeocodingRunnerManager.h" 0042 #include "TemplateDocument.h" 0043 #include "OsmPlacemarkData.h" 0044 #include "StyleBuilder.h" 0045 0046 // Qt 0047 #include <QApplication> 0048 #include <QFile> 0049 #include <QMimeData> 0050 #include <QPointer> 0051 #include <QAction> 0052 #include <QClipboard> 0053 #include <QMenu> 0054 #include <QMessageBox> 0055 0056 namespace Marble { 0057 /* TRANSLATOR Marble::MarbleWidgetPopupMenu */ 0058 0059 class Q_DECL_HIDDEN MarbleWidgetPopupMenu::Private { 0060 public: 0061 const MarbleModel *const m_model; 0062 MarbleWidget *const m_widget; 0063 0064 QVector<const GeoDataFeature*> m_featurelist; 0065 QList<AbstractDataPluginItem *> m_itemList; 0066 0067 QMenu m_lmbMenu; 0068 QMenu m_rmbMenu; 0069 0070 QAction *m_infoDialogAction; 0071 QAction *m_directionsFromHereAction; 0072 QAction *m_directionsToHereAction; 0073 0074 QAction *const m_copyCoordinateAction; 0075 QAction *const m_copyGeoAction; 0076 0077 QAction *m_rmbExtensionPoint; 0078 0079 ReverseGeocodingRunnerManager m_runnerManager; 0080 0081 QPoint m_mousePosition; 0082 0083 public: 0084 Private( MarbleWidget *widget, const MarbleModel *model, MarbleWidgetPopupMenu* parent ); 0085 QMenu* createInfoBoxMenu(QWidget *parent); 0086 0087 /** 0088 * Returns the geo coordinates of the mouse pointer at the last right button menu. 0089 * You must not pass 0 as coordinates parameter. The result indicates whether the 0090 * coordinates are valid, which will be true if the right button menu was opened at least once. 0091 */ 0092 GeoDataCoordinates mouseCoordinates( QAction* dataContainer ) const; 0093 0094 static QString filterEmptyShortDescription( const QString &description ); 0095 void setupDialogOsm( PopupLayer *popup, const GeoDataPlacemark* placemark ); 0096 void setupDialogSatellite( const GeoDataPlacemark *placemark ); 0097 static void setupDialogCity( PopupLayer *popup, const GeoDataPlacemark *placemark ); 0098 static void setupDialogNation( PopupLayer *popup, const GeoDataPlacemark *placemark ); 0099 static void setupDialogGeoPlaces( PopupLayer *popup, const GeoDataPlacemark *placemark ); 0100 static void setupDialogSkyPlaces( PopupLayer *popup, const GeoDataPlacemark *placemark ); 0101 static void setupDialogPhotoOverlay( PopupLayer *popup, const GeoDataPhotoOverlay *overlay); 0102 }; 0103 0104 MarbleWidgetPopupMenu::Private::Private( MarbleWidget *widget, const MarbleModel *model, MarbleWidgetPopupMenu* parent ) : 0105 m_model(model), 0106 m_widget(widget), 0107 m_lmbMenu( m_widget ), 0108 m_rmbMenu( m_widget ), 0109 m_directionsFromHereAction( nullptr ), 0110 m_directionsToHereAction( nullptr ), 0111 m_copyCoordinateAction(new QAction(QIcon(QStringLiteral(":/icons/copy-coordinates.png")), tr("Copy Coordinates"), parent)), 0112 m_copyGeoAction(new QAction(QIcon(QStringLiteral(":/icons/copy-coordinates.png")), tr("Copy geo: URL"), parent)), 0113 m_rmbExtensionPoint( nullptr ), 0114 m_runnerManager( model ) 0115 { 0116 // Property actions (Left mouse button) 0117 m_infoDialogAction = new QAction( parent ); 0118 m_infoDialogAction->setData( 0 ); 0119 0120 // Tool actions (Right mouse button) 0121 m_directionsFromHereAction = new QAction( tr( "Directions &from here" ), parent ); 0122 m_directionsToHereAction = new QAction( tr( "Directions &to here" ), parent ); 0123 RouteRequest* request = m_widget->model()->routingManager()->routeRequest(); 0124 if ( request ) { 0125 m_directionsFromHereAction->setIcon( QIcon( request->pixmap( 0, 16 ) ) ); 0126 int const lastIndex = qMax( 1, request->size()-1 ); 0127 m_directionsToHereAction->setIcon( QIcon( request->pixmap( lastIndex, 16 ) ) ); 0128 } 0129 QAction* addBookmark = new QAction( QIcon(QStringLiteral(":/icons/bookmark-new.png")), 0130 tr( "Add &Bookmark" ), parent ); 0131 QAction* fullscreenAction = new QAction( tr( "&Full Screen Mode" ), parent ); 0132 fullscreenAction->setCheckable( true ); 0133 0134 QAction* aboutDialogAction = new QAction(QIcon(QStringLiteral(":/icons/marble.png")), tr("&About"), parent); 0135 0136 QMenu* infoBoxMenu = createInfoBoxMenu(m_widget); 0137 0138 const bool smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; 0139 0140 if ( !smallScreen ) { 0141 m_rmbExtensionPoint = m_rmbMenu.addSeparator(); 0142 } 0143 0144 m_rmbMenu.addAction( m_directionsFromHereAction ); 0145 m_rmbMenu.addAction( m_directionsToHereAction ); 0146 m_rmbMenu.addSeparator(); 0147 m_rmbMenu.addAction( addBookmark ); 0148 if ( !smallScreen ) { 0149 m_rmbMenu.addAction( m_copyCoordinateAction ); 0150 m_rmbMenu.addAction( m_copyGeoAction ); 0151 } 0152 m_rmbMenu.addAction(QIcon(QStringLiteral(":/icons/addressbook-details.png")), tr("&Address Details"), parent, SLOT(startReverseGeocoding())); 0153 m_rmbMenu.addSeparator(); 0154 m_rmbMenu.addMenu( infoBoxMenu ); 0155 0156 if ( !smallScreen ) { 0157 m_rmbMenu.addAction( aboutDialogAction ); 0158 } else { 0159 m_rmbMenu.addAction( fullscreenAction ); 0160 } 0161 0162 parent->connect( &m_lmbMenu, SIGNAL(aboutToHide()), SLOT(resetMenu()) ); 0163 parent->connect( m_directionsFromHereAction, SIGNAL(triggered()), SLOT(directionsFromHere()) ); 0164 parent->connect( m_directionsToHereAction, SIGNAL(triggered()), SLOT(directionsToHere()) ); 0165 parent->connect( addBookmark, SIGNAL(triggered()), SLOT(addBookmark()) ); 0166 parent->connect( aboutDialogAction, SIGNAL(triggered()), SLOT(slotAboutDialog()) ); 0167 parent->connect( m_copyCoordinateAction, SIGNAL(triggered()), SLOT(slotCopyCoordinates()) ); 0168 parent->connect( m_copyGeoAction, SIGNAL(triggered()), SLOT(slotCopyGeo()) ); 0169 parent->connect( m_infoDialogAction, SIGNAL(triggered()), SLOT(slotInfoDialog()) ); 0170 parent->connect( fullscreenAction, SIGNAL(triggered(bool)), parent, SLOT(toggleFullscreen(bool)) ); 0171 0172 parent->connect( &m_runnerManager, SIGNAL(reverseGeocodingFinished(GeoDataCoordinates,GeoDataPlacemark)), 0173 parent, SLOT(showAddressInformation(GeoDataCoordinates,GeoDataPlacemark)) ); 0174 } 0175 0176 QString MarbleWidgetPopupMenu::Private::filterEmptyShortDescription(const QString &description) 0177 { 0178 if(description.isEmpty()) 0179 return tr("No description available."); 0180 return description; 0181 } 0182 0183 void MarbleWidgetPopupMenu::Private::setupDialogOsm( PopupLayer *popup, const GeoDataPlacemark *placemark ) 0184 { 0185 const GeoDataCoordinates location = placemark->coordinate(); 0186 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter); 0187 0188 QFile descriptionFile(QStringLiteral(":/marble/webpopup/osm.html")); 0189 if (!descriptionFile.open(QIODevice::ReadOnly)) { 0190 return; 0191 } 0192 0193 const QString none = QStringLiteral("none"); 0194 0195 QString description = descriptionFile.readAll(); 0196 const OsmPlacemarkData& data = placemark->osmData(); 0197 if (!data.containsTagKey("addr:street") && !data.containsTagKey("addr:housenumber")){ 0198 description.replace(QStringLiteral("<br> %postcode%"), QStringLiteral("%postcode%")); 0199 } 0200 TemplateDocument doc(description); 0201 0202 doc[QStringLiteral("name")] = data.tagValue(QStringLiteral("name")); 0203 0204 QString natural = data.tagValue(QStringLiteral("natural")); 0205 if (!natural.isEmpty()) { 0206 natural[0] = natural[0].toUpper(); 0207 if (natural == QLatin1String("Peak")) { 0208 QString elevation = data.tagValue(QStringLiteral("ele")); 0209 if (!elevation.isEmpty()) { 0210 natural = natural + QLatin1String(" - ") + elevation + QLatin1String(" m"); 0211 } 0212 } 0213 doc[QStringLiteral("details")] = natural; 0214 } else { 0215 doc[QStringLiteral("detailsVisibility")] = none; 0216 } 0217 0218 QString amenity; 0219 QString shop = data.tagValue(QStringLiteral("shop")); 0220 if (!shop.isEmpty()) { 0221 shop[0] = shop[0].toUpper(); 0222 0223 if (shop == QLatin1String("Clothes")) { 0224 QString type = data.tagValue(QStringLiteral("clothes")); 0225 if (type.isEmpty()) { 0226 type = data.tagValue(QStringLiteral("designation")); 0227 } 0228 if (!type.isEmpty()) { 0229 type[0] = type[0].toUpper(); 0230 amenity = QLatin1String("Shop - ") + shop + QLatin1String(" (") + type + QLatin1Char(')'); 0231 } 0232 } 0233 if (amenity.isEmpty()) { 0234 amenity = QLatin1String("Shop - ") + shop; 0235 } 0236 } else { 0237 amenity = data.tagValue(QStringLiteral("amenity")); 0238 if (!amenity.isEmpty()) { 0239 amenity[0] = amenity[0].toUpper(); 0240 } 0241 } 0242 if (!amenity.isEmpty()) { 0243 doc[QStringLiteral("amenity")] = amenity; 0244 } else { 0245 doc[QStringLiteral("amenityVisibility")] = none; 0246 } 0247 0248 QString cuisine = data.tagValue(QStringLiteral("cuisine")); 0249 if (!cuisine.isEmpty()) { 0250 cuisine[0] = cuisine[0].toUpper(); 0251 doc[QStringLiteral("cuisine")] = cuisine; 0252 } else { 0253 doc[QStringLiteral("cuisineVisibility")] = none; 0254 } 0255 0256 QString openingHours = data.tagValue(QStringLiteral("opening_hours")); 0257 if (!openingHours.isEmpty()) { 0258 doc[QStringLiteral("openinghours")] = openingHours; 0259 } else { 0260 doc[QStringLiteral("openinghoursVisibility")] = none; 0261 } 0262 0263 bool hasContactsData = false; 0264 0265 const QStringList addressItemKeys = QStringList() 0266 << QStringLiteral("street") 0267 << QStringLiteral("housenumber") 0268 << QStringLiteral("postcode") 0269 << QStringLiteral("city"); 0270 bool hasAddressItem = false; 0271 QStringList addressItems; 0272 for (const QString& key: addressItemKeys) { 0273 const QString item = data.tagValue(QLatin1String("addr:") + key); 0274 if (!item.isEmpty()) { 0275 hasAddressItem = true; 0276 } 0277 addressItems << item; 0278 } 0279 if (hasAddressItem) { 0280 hasContactsData = true; 0281 for(int i = 0; i < addressItemKeys.size(); ++i) { 0282 doc[addressItemKeys[i]] = addressItems[i]; 0283 } 0284 } else { 0285 doc[QStringLiteral("addressVisibility")] = none; 0286 } 0287 0288 QString phoneData = data.tagValue(QStringLiteral("phone")); 0289 if (!phoneData.isEmpty()) { 0290 hasContactsData = true; 0291 doc[QStringLiteral("phone")] = phoneData; 0292 } else { 0293 doc[QStringLiteral("phoneVisibility")] = none; 0294 } 0295 0296 QString websiteData; 0297 auto const tags = QStringList() << "website" << "contact:website" << "facebook" << "contact:facebook" << "url"; 0298 for(const QString &tag: tags) { 0299 websiteData = data.tagValue(tag); 0300 if (!websiteData.isEmpty()) { 0301 break; 0302 } 0303 } 0304 if (!websiteData.isEmpty()) { 0305 hasContactsData = true; 0306 doc[QStringLiteral("website")] = websiteData; 0307 } else { 0308 doc[QStringLiteral("websiteVisibility")] = none; 0309 } 0310 0311 if (!hasContactsData) { 0312 doc[QStringLiteral("contactVisibility")] = none; 0313 } 0314 0315 bool hasFacilitiesData = false; 0316 0317 const QString wheelchair = data.tagValue(QStringLiteral("wheelchair")); 0318 if (!wheelchair.isEmpty()) { 0319 hasFacilitiesData = true; 0320 doc[QStringLiteral("wheelchair")] = wheelchair; 0321 } else { 0322 doc[QStringLiteral("wheelchairVisibility")] = none; 0323 } 0324 0325 const QString internetAccess = data.tagValue(QStringLiteral("internet_access")); 0326 if (!internetAccess.isEmpty()) { 0327 hasFacilitiesData = true; 0328 doc[QStringLiteral("internetaccess")] = internetAccess; 0329 } else { 0330 doc[QStringLiteral("internetVisibility")] = none; 0331 } 0332 0333 const QString smoking = data.tagValue(QStringLiteral("smoking")); 0334 if (!smoking.isEmpty()) { 0335 hasFacilitiesData = true; 0336 doc[QStringLiteral("smoking")] = smoking; 0337 } else { 0338 doc[QStringLiteral("smokingVisibility")] = none; 0339 } 0340 0341 if (!hasFacilitiesData) { 0342 doc[QStringLiteral("facilitiesVisibility")] = none; 0343 } 0344 0345 const QString flagPath = m_widget->styleBuilder()->createStyle(StyleParameters(placemark))->iconStyle().iconPath(); 0346 doc["flag"] = flagPath; 0347 popup->setContent(doc.finalText()); 0348 } 0349 0350 void MarbleWidgetPopupMenu::Private::setupDialogSatellite( const GeoDataPlacemark *placemark ) 0351 { 0352 PopupLayer *const popup = m_widget->popupLayer(); 0353 const GeoDataCoordinates location = placemark->coordinate(m_widget->model()->clockDateTime()); 0354 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter); 0355 0356 const QString description = placemark->description(); 0357 TemplateDocument doc(description); 0358 doc["altitude"] = QString::number(location.altitude(), 'f', 2); 0359 doc["latitude"] = location.latToString(); 0360 doc["longitude"] = location.lonToString(); 0361 popup->setContent(doc.finalText()); 0362 } 0363 0364 void MarbleWidgetPopupMenu::Private::setupDialogCity( PopupLayer *popup, const GeoDataPlacemark *placemark ) 0365 { 0366 const GeoDataCoordinates location = placemark->coordinate(); 0367 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter); 0368 0369 QFile descriptionFile(QStringLiteral(":/marble/webpopup/city.html")); 0370 if (!descriptionFile.open(QIODevice::ReadOnly)) { 0371 return; 0372 } 0373 0374 const QString description = descriptionFile.readAll(); 0375 TemplateDocument doc(description); 0376 0377 doc["name"] = placemark->name(); 0378 QString roleString; 0379 const QString role = placemark->role(); 0380 if (role == QLatin1String("PPLC")) { 0381 roleString = tr("National Capital"); 0382 } else if (role == QLatin1String("PPL")) { 0383 roleString = tr("City"); 0384 } else if (role == QLatin1String("PPLA")) { 0385 roleString = tr("State Capital"); 0386 } else if (role == QLatin1String("PPLA2")) { 0387 roleString = tr("County Capital"); 0388 } else if (role == QLatin1String("PPLA3") || 0389 role == QLatin1String("PPLA4")) { 0390 roleString = tr("Capital"); 0391 } else if (role == QLatin1String("PPLF") || 0392 role == QLatin1String("PPLG") || 0393 role == QLatin1String("PPLL") || 0394 role == QLatin1String("PPLQ") || 0395 role == QLatin1String("PPLR") || 0396 role == QLatin1String("PPLS") || 0397 role == QLatin1String("PPLW")) { 0398 roleString = tr("Village"); 0399 } 0400 0401 doc["category"] = roleString; 0402 doc["shortDescription"] = filterEmptyShortDescription(placemark->description()); 0403 doc["latitude"] = location.latToString(); 0404 doc["longitude"] = location.lonToString(); 0405 doc["elevation"] = QString::number(location.altitude(), 'f', 2); 0406 doc["population"] = QString::number(placemark->population()); 0407 doc["country"] = placemark->countryCode(); 0408 doc["state"] = placemark->state(); 0409 0410 QString dst = QStringLiteral("%1").arg((placemark->extendedData().value(QStringLiteral("gmt")).value().toInt() + 0411 placemark->extendedData().value(QStringLiteral("dst")).value().toInt()) / 0412 ( double ) 100, 0, 'f', 1 ); 0413 // There is an issue about UTC. 0414 // It's possible to variants (e.g.): 0415 // +1.0 and -1.0, but dst does not have + an the start 0416 if (dst.startsWith(QLatin1Char('-'))) { 0417 doc["timezone"] = dst; 0418 } else { 0419 doc["timezone"] = QLatin1Char('+') + dst; 0420 } 0421 0422 const QString flagPath = MarbleDirs::path( 0423 QLatin1String("flags/flag_") + placemark->countryCode().toLower() + QLatin1String(".svg")); 0424 doc["flag"] = flagPath; 0425 0426 popup->setContent(doc.finalText()); 0427 } 0428 0429 void MarbleWidgetPopupMenu::Private::setupDialogNation( PopupLayer *popup, const GeoDataPlacemark *index) 0430 { 0431 const GeoDataCoordinates location = index->coordinate(); 0432 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter); 0433 0434 QFile descriptionFile(QStringLiteral(":/marble/webpopup/nation.html")); 0435 if (!descriptionFile.open(QIODevice::ReadOnly)) { 0436 return; 0437 } 0438 0439 const QString description = descriptionFile.readAll(); 0440 TemplateDocument doc(description); 0441 0442 doc["name"] = index->name(); 0443 doc["shortDescription"] = filterEmptyShortDescription(index->description()); 0444 doc["latitude"] = location.latToString(); 0445 doc["longitude"] = location.lonToString(); 0446 doc["elevation"] = QString::number(location.altitude(), 'f', 2); 0447 doc["population"] = QString::number(index->population()); 0448 doc["area"] = QString::number(index->area(), 'f', 2); 0449 0450 const QString flagPath = MarbleDirs::path(QString("flags/flag_%1.svg").arg(index->countryCode().toLower()) ); 0451 doc["flag"] = flagPath; 0452 0453 popup->setContent(doc.finalText()); 0454 } 0455 0456 void MarbleWidgetPopupMenu::Private::setupDialogGeoPlaces( PopupLayer *popup, const GeoDataPlacemark *index) 0457 { 0458 const GeoDataCoordinates location = index->coordinate(); 0459 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter); 0460 0461 QFile descriptionFile(QStringLiteral(":/marble/webpopup/geoplace.html")); 0462 if (!descriptionFile.open(QIODevice::ReadOnly)) { 0463 return; 0464 } 0465 0466 const QString description = descriptionFile.readAll(); 0467 TemplateDocument doc(description); 0468 0469 doc["name"] = index->name(); 0470 doc["latitude"] = location.latToString(); 0471 doc["longitude"] = location.lonToString(); 0472 doc["elevation"] = QString::number(location.altitude(), 'f', 2); 0473 doc["shortDescription"] = filterEmptyShortDescription(index->description()); 0474 0475 popup->setContent(doc.finalText()); 0476 } 0477 0478 void MarbleWidgetPopupMenu::Private::setupDialogSkyPlaces( PopupLayer *popup, const GeoDataPlacemark *index) 0479 { 0480 const GeoDataCoordinates location = index->coordinate(); 0481 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter); 0482 0483 QFile descriptionFile(QStringLiteral(":/marble/webpopup/skyplace.html")); 0484 if (!descriptionFile.open(QIODevice::ReadOnly)) { 0485 return; 0486 } 0487 0488 const QString description = descriptionFile.readAll(); 0489 TemplateDocument doc(description); 0490 0491 doc["name"] = index->name(); 0492 doc["latitude"] = GeoDataCoordinates::latToString( 0493 location.latitude(), GeoDataCoordinates::Astro, GeoDataCoordinates::Radian, -1, 'f'); 0494 doc["longitude"] = GeoDataCoordinates::lonToString( 0495 location.longitude(), GeoDataCoordinates::Astro, GeoDataCoordinates::Radian, -1, 'f'); 0496 doc["shortDescription"] = filterEmptyShortDescription(index->description()); 0497 0498 popup->setContent(doc.finalText()); 0499 } 0500 0501 void MarbleWidgetPopupMenu::Private::setupDialogPhotoOverlay( PopupLayer *popup, const GeoDataPhotoOverlay *index ) 0502 { 0503 const GeoDataCoordinates location = index->point().coordinates(); 0504 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter); 0505 0506 QFile descriptionFile(QStringLiteral(":/marble/webpopup/photooverlay.html")); 0507 0508 if ( !descriptionFile.open(QIODevice::ReadOnly) ) { 0509 return; 0510 } 0511 0512 const QString description = descriptionFile.readAll(); 0513 TemplateDocument doc(description); 0514 doc["name"] = index->name(); 0515 doc["latitude"] = location.latToString(); 0516 doc["longitude"] = location.lonToString(); 0517 doc["elevation"] = QString::number(location.altitude(), 'f', 2); 0518 doc["shortDescription"] = filterEmptyShortDescription(index->description()); 0519 doc["source"] = index->absoluteIconFile(); 0520 doc["width"] = QString::number(200); 0521 doc["height"] = QString::number(100); 0522 QString const basePath = index->resolvePath("."); 0523 QUrl const baseUrl = (basePath != QLatin1String(".")) ? QUrl::fromLocalFile(basePath + QLatin1Char('/')) : QUrl(); 0524 popup->setContent(doc.finalText(), baseUrl ); 0525 } 0526 0527 MarbleWidgetPopupMenu::MarbleWidgetPopupMenu(MarbleWidget *widget, 0528 const MarbleModel *model) 0529 : QObject(widget), 0530 d( new Private( widget, model, this ) ) 0531 { 0532 // nothing to do 0533 } 0534 0535 MarbleWidgetPopupMenu::~MarbleWidgetPopupMenu() 0536 { 0537 delete d; 0538 } 0539 0540 QMenu* MarbleWidgetPopupMenu::Private::createInfoBoxMenu(QWidget* parent) 0541 { 0542 QMenu* menu = new QMenu(tr("&Info Boxes"), parent); 0543 QList<AbstractFloatItem *> floatItemList = m_widget->floatItems(); 0544 0545 QList<AbstractFloatItem *>::const_iterator iter = floatItemList.constBegin(); 0546 QList<AbstractFloatItem *>::const_iterator const end = floatItemList.constEnd(); 0547 for (; iter != end; ++iter ) 0548 { 0549 menu->addAction( (*iter)->action() ); 0550 } 0551 return menu; 0552 } 0553 0554 void MarbleWidgetPopupMenu::showLmbMenu( int xpos, int ypos ) 0555 { 0556 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; 0557 if ( smallScreen ) { 0558 showRmbMenu( xpos, ypos ); 0559 return; 0560 } 0561 0562 d->m_mousePosition.setX(xpos); 0563 d->m_mousePosition.setY(ypos); 0564 0565 const QPoint curpos = QPoint( xpos, ypos ); 0566 d->m_featurelist = d->m_widget->whichFeatureAt( curpos ); 0567 0568 int actionidx = 1; 0569 0570 QVector<const GeoDataFeature*>::const_iterator it = d->m_featurelist.constBegin(); 0571 QVector<const GeoDataFeature*>::const_iterator const itEnd = d->m_featurelist.constEnd(); 0572 for (; it != itEnd; ++it ) 0573 { 0574 QString name = (*it)->name(); 0575 QPixmap icon = QPixmap::fromImage( ( *it)->style()->iconStyle().icon() ); 0576 d->m_infoDialogAction->setData( actionidx ); 0577 d->m_infoDialogAction->setText( name ); 0578 d->m_infoDialogAction->setIcon( icon ); 0579 // Insert as first action in the menu 0580 QAction *firstAction = nullptr; 0581 if( !d->m_lmbMenu.actions().isEmpty() ) { 0582 firstAction = d->m_lmbMenu.actions().first(); 0583 } 0584 d->m_lmbMenu.insertAction( firstAction, d->m_infoDialogAction ); 0585 actionidx++; 0586 } 0587 0588 d->m_itemList = d->m_widget->whichItemAt( curpos ); 0589 QList<AbstractDataPluginItem *>::const_iterator itW = d->m_itemList.constBegin(); 0590 QList<AbstractDataPluginItem *>::const_iterator const itWEnd = d->m_itemList.constEnd(); 0591 for (; itW != itWEnd; ++itW ) 0592 { 0593 for ( QAction* action: (*itW)->actions() ) { 0594 d->m_lmbMenu.addAction( action ); 0595 } 0596 } 0597 0598 switch ( d->m_lmbMenu.actions().size() ) { 0599 case 0: // nothing to do, ignore 0600 break; 0601 0602 case 1: // one action? perform immediately 0603 d->m_lmbMenu.actions().first()->activate( QAction::Trigger ); 0604 d->m_lmbMenu.clear(); 0605 break; 0606 0607 default: 0608 d->m_lmbMenu.popup( d->m_widget->mapToGlobal( curpos ) ); 0609 } 0610 } 0611 0612 0613 void MarbleWidgetPopupMenu::showRmbMenu( int xpos, int ypos ) 0614 { 0615 qreal lon, lat; 0616 const bool visible = d->m_widget->geoCoordinates( xpos, ypos, lon, lat, GeoDataCoordinates::Radian ); 0617 if ( !visible ) 0618 return; 0619 0620 d->m_mousePosition.setX(xpos); 0621 d->m_mousePosition.setY(ypos); 0622 0623 QPoint curpos = QPoint( xpos, ypos ); 0624 d->m_copyCoordinateAction->setData( curpos ); 0625 d->m_copyGeoAction->setData( curpos ); 0626 0627 bool const showDirectionButtons = d->m_widget->routingLayer() && d->m_widget->routingLayer()->isInteractive(); 0628 d->m_directionsFromHereAction->setVisible( showDirectionButtons ); 0629 d->m_directionsToHereAction->setVisible( showDirectionButtons ); 0630 RouteRequest* request = d->m_widget->model()->routingManager()->routeRequest(); 0631 if ( request ) { 0632 int const lastIndex = qMax( 1, request->size()-1 ); 0633 d->m_directionsToHereAction->setIcon( QIcon( request->pixmap( lastIndex, 16 ) ) ); 0634 } 0635 0636 d->m_rmbMenu.popup( d->m_widget->mapToGlobal( curpos ) ); 0637 } 0638 0639 void MarbleWidgetPopupMenu::resetMenu() 0640 { 0641 d->m_lmbMenu.clear(); 0642 } 0643 0644 void MarbleWidgetPopupMenu::slotInfoDialog() 0645 { 0646 QAction *action = qobject_cast<QAction *>( sender() ); 0647 if ( action == nullptr ) { 0648 mDebug() << "Warning: slotInfoDialog should be called by a QAction signal"; 0649 return; 0650 } 0651 0652 int actionidx = action->data().toInt(); 0653 0654 if ( actionidx > 0 ) { 0655 const GeoDataPlacemark *placemark = dynamic_cast<const GeoDataPlacemark*>(d->m_featurelist.at( actionidx -1 )); 0656 const GeoDataPhotoOverlay *overlay = dynamic_cast<const GeoDataPhotoOverlay*>(d->m_featurelist.at( actionidx - 1 )); 0657 PopupLayer* popup = d->m_widget->popupLayer(); 0658 bool isSatellite = false; 0659 bool isCity = false; 0660 bool isNation = false; 0661 0662 const OsmPlacemarkData& data = placemark->osmData(); 0663 0664 bool hasOsmData = false; 0665 0666 QStringList recognizedTags; 0667 recognizedTags << "name" << "amenity" << "cuisine" << "opening_hours"; 0668 recognizedTags << "addr:street" << "addr:housenumber" << "addr:postcode"; 0669 recognizedTags << "addr:city" << "phone" << "wheelchair" << "internet_access"; 0670 recognizedTags << "smoking" << "website" << "contact:website" << "facebook"; 0671 recognizedTags << "contact:facebook" << "url"; 0672 0673 for(const QString &tag: recognizedTags) { 0674 if (data.containsTagKey(tag)) { 0675 hasOsmData = true; 0676 break; 0677 } 0678 } 0679 0680 if ( placemark ) { 0681 isSatellite = (placemark->visualCategory() == GeoDataPlacemark::Satellite); 0682 isCity = (placemark->visualCategory() >= GeoDataPlacemark::SmallCity && 0683 placemark->visualCategory() <= GeoDataPlacemark::LargeNationCapital); 0684 isNation = (placemark->visualCategory() == GeoDataPlacemark::Nation); 0685 } 0686 0687 bool isSky = false; 0688 0689 if ( d->m_widget->model()->mapTheme() ) { 0690 isSky = d->m_widget->model()->mapTheme()->head()->target() == QLatin1String("sky"); 0691 } 0692 0693 popup->setSize(QSizeF(420, 420)); 0694 0695 if (hasOsmData){ 0696 d->setupDialogOsm( popup, placemark ); 0697 } else if (isSatellite) { 0698 d->setupDialogSatellite( placemark ); 0699 } else if (isCity) { 0700 Private::setupDialogCity( popup, placemark ); 0701 } else if (isNation) { 0702 Private::setupDialogNation( popup, placemark ); 0703 } else if (isSky) { 0704 Private::setupDialogSkyPlaces( popup, placemark ); 0705 } else if ( overlay ) { 0706 Private::setupDialogPhotoOverlay( popup, overlay ); 0707 } else if ( placemark && placemark->role().isEmpty() ) { 0708 popup->setContent( placemark->description() ); 0709 } else if ( placemark ) { 0710 Private::setupDialogGeoPlaces( popup, placemark ); 0711 } 0712 0713 if ( placemark ) { 0714 if ( placemark->style() == nullptr ) { 0715 popup->setBackgroundColor(QColor(Qt::white)); 0716 popup->setTextColor(QColor(Qt::black)); 0717 return; 0718 } 0719 if ( placemark->style()->balloonStyle().displayMode() == GeoDataBalloonStyle::Hide ) { 0720 popup->setVisible(false); 0721 return; 0722 } 0723 0724 QString content = placemark->style()->balloonStyle().text(); 0725 if (content.length() > 0) { 0726 content.replace(QStringLiteral("$[name]"), placemark->name(), Qt::CaseInsensitive); 0727 content.replace(QStringLiteral("$[description]"), placemark->description(), Qt::CaseInsensitive); 0728 content.replace(QStringLiteral("$[address]"), placemark->address(), Qt::CaseInsensitive); 0729 // @TODO: implement the line calculation, so that snippet().maxLines actually has effect. 0730 content.replace(QStringLiteral("$[snippet]"), placemark->snippet().text(), Qt::CaseInsensitive); 0731 content.replace(QStringLiteral("$[id]"), placemark->id(), Qt::CaseInsensitive); 0732 QString const basePath = placemark->resolvePath("."); 0733 QUrl const baseUrl = (basePath != QLatin1String(".")) ? QUrl::fromLocalFile(basePath + QLatin1Char('/')) : QUrl(); 0734 popup->setContent(content, baseUrl ); 0735 } 0736 0737 popup->setBackgroundColor(placemark->style()->balloonStyle().backgroundColor()); 0738 popup->setTextColor(placemark->style()->balloonStyle().textColor()); 0739 } 0740 0741 popup->popup(); 0742 } 0743 } 0744 0745 void MarbleWidgetPopupMenu::slotCopyCoordinates() 0746 { 0747 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction ); 0748 if ( coordinates.isValid() ) { 0749 const qreal longitude_degrees = coordinates.longitude(GeoDataCoordinates::Degree); 0750 const qreal latitude_degrees = coordinates.latitude(GeoDataCoordinates::Degree); 0751 0752 // importing this representation into Marble does not show anything, 0753 // but Merkaartor shows the point 0754 const QString kmlRepresentation = QString::fromLatin1( 0755 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 0756 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" 0757 "<Document>\n" 0758 " <Placemark>\n" 0759 // " <name></name>\n" 0760 " <Point>\n" 0761 " <coordinates>%1,%2</coordinates>\n" 0762 " </Point>\n" 0763 " </Placemark>\n" 0764 "</Document>\n" 0765 "</kml>\n" 0766 ).arg(longitude_degrees, 0, 'f', 10).arg(latitude_degrees, 0, 'f', 10); 0767 0768 // importing this data into Marble and Merkaartor works 0769 const QString gpxRepresentation = QString::fromLatin1( 0770 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" 0771 "<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" creator=\"trippy\" version=\"0.1\"\n" 0772 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" 0773 " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n" 0774 " <wpt lat=\"%1\" lon=\"%2\">\n" 0775 // " <ele>%3</ele>\n" 0776 // " <time></time>\n" 0777 // " <name>%4</name>\n" 0778 " </wpt>\n" 0779 "</gpx>\n" 0780 ).arg(latitude_degrees, 0, 'f', 10).arg(longitude_degrees, 0, 'f', 10); 0781 0782 QString positionString = coordinates.toString(); 0783 0784 QMimeData * const myMimeData = new QMimeData(); 0785 myMimeData->setText(positionString); 0786 myMimeData->setData(QLatin1String("application/vnd.google-earth.kml+xml"), kmlRepresentation.toUtf8()); 0787 myMimeData->setData(QLatin1String("application/gpx+xml"), gpxRepresentation.toUtf8()); 0788 0789 QClipboard * const clipboard = QApplication::clipboard(); 0790 clipboard->setMimeData(myMimeData); 0791 } 0792 } 0793 0794 void MarbleWidgetPopupMenu::slotCopyGeo() 0795 { 0796 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction ); 0797 if ( coordinates.isValid() ) { 0798 const qreal latitude_degrees = coordinates.latitude(GeoDataCoordinates::Degree); 0799 const qreal longitude_degrees = coordinates.longitude(GeoDataCoordinates::Degree); 0800 0801 QMimeData * const myMimeData = new QMimeData(); 0802 QList<QUrl> urls = { QUrl(QString("geo:%1,%2").arg(latitude_degrees, 0, 'f', 10).arg(longitude_degrees, 0, 'f', 10)) }; 0803 myMimeData->setUrls(urls); 0804 QClipboard * const clipboard = QApplication::clipboard(); 0805 clipboard->setMimeData(myMimeData); 0806 } 0807 } 0808 0809 0810 void MarbleWidgetPopupMenu::slotAboutDialog() 0811 { 0812 QPointer<MarbleAboutDialog> dialog = new MarbleAboutDialog( d->m_widget ); 0813 dialog->exec(); 0814 delete dialog; 0815 } 0816 0817 void MarbleWidgetPopupMenu::addAction( Qt::MouseButton button, QAction* action ) 0818 { 0819 if ( button == Qt::RightButton ) { 0820 d->m_rmbMenu.insertAction( d->m_rmbExtensionPoint, action ); 0821 } else { 0822 d->m_lmbMenu.addAction( action ); 0823 } 0824 } 0825 0826 void MarbleWidgetPopupMenu::directionsFromHere() 0827 { 0828 RouteRequest* request = d->m_widget->model()->routingManager()->routeRequest(); 0829 if ( request ) 0830 { 0831 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction ); 0832 if ( coordinates.isValid() ) { 0833 if ( request->size() > 0 ) { 0834 request->setPosition( 0, coordinates ); 0835 } else { 0836 request->append( coordinates ); 0837 } 0838 d->m_widget->model()->routingManager()->retrieveRoute(); 0839 } 0840 } 0841 } 0842 0843 void MarbleWidgetPopupMenu::directionsToHere() 0844 { 0845 RouteRequest* request = d->m_widget->model()->routingManager()->routeRequest(); 0846 if ( request ) 0847 { 0848 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction ); 0849 if ( coordinates.isValid() ) { 0850 if ( request->size() > 1 ) { 0851 request->setPosition( request->size()-1, coordinates ); 0852 } else { 0853 request->append( coordinates ); 0854 } 0855 d->m_widget->model()->routingManager()->retrieveRoute(); 0856 } 0857 } 0858 } 0859 0860 GeoDataCoordinates MarbleWidgetPopupMenu::Private::mouseCoordinates( QAction* dataContainer ) const 0861 { 0862 if ( !dataContainer ) { 0863 return GeoDataCoordinates(); 0864 } 0865 0866 if ( !m_featurelist.isEmpty() && geodata_cast<GeoDataPlacemark>(m_featurelist.first())) { 0867 const GeoDataPlacemark * placemark = static_cast<const GeoDataPlacemark*>( m_featurelist.first() ); 0868 return placemark->coordinate( m_model->clock()->dateTime() ); 0869 } else { 0870 QPoint p = dataContainer->data().toPoint(); 0871 qreal lat( 0.0 ), lon( 0.0 ); 0872 0873 const bool valid = m_widget->geoCoordinates( p.x(), p.y(), lon, lat, GeoDataCoordinates::Radian ); 0874 if ( valid ) { 0875 return GeoDataCoordinates( lon, lat ); 0876 } 0877 } 0878 0879 return GeoDataCoordinates(); 0880 } 0881 0882 void MarbleWidgetPopupMenu::startReverseGeocoding() 0883 { 0884 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction ); 0885 if ( coordinates.isValid() ) { 0886 d->m_runnerManager.reverseGeocoding( coordinates ); 0887 } 0888 } 0889 0890 void MarbleWidgetPopupMenu::showAddressInformation(const GeoDataCoordinates &, const GeoDataPlacemark &placemark) 0891 { 0892 QString text = placemark.address(); 0893 if ( !text.isEmpty() ) { 0894 QMessageBox::information( d->m_widget, tr( "Address Details" ), text, QMessageBox::Ok ); 0895 } 0896 } 0897 0898 void MarbleWidgetPopupMenu::addBookmark() 0899 { 0900 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction ); 0901 if ( coordinates.isValid() ) { 0902 QPointer<EditBookmarkDialog> dialog = new EditBookmarkDialog( d->m_widget->model()->bookmarkManager(), d->m_widget ); 0903 dialog->setMarbleWidget( d->m_widget ); 0904 dialog->setCoordinates( coordinates ); 0905 dialog->setRange( d->m_widget->lookAt().range() ); 0906 dialog->setReverseGeocodeName(); 0907 if ( dialog->exec() == QDialog::Accepted ) { 0908 d->m_widget->model()->bookmarkManager()->addBookmark( dialog->folder(), dialog->bookmark() ); 0909 } 0910 delete dialog; 0911 } 0912 } 0913 0914 void MarbleWidgetPopupMenu::toggleFullscreen( bool enabled ) 0915 { 0916 QWidget* parent = d->m_widget; 0917 for ( ; parent->parentWidget(); parent = parent->parentWidget() ) { 0918 // nothing to do 0919 } 0920 0921 if ( enabled ) { 0922 parent->setWindowState( parent->windowState() | Qt::WindowFullScreen ); 0923 } else { 0924 parent->setWindowState( parent->windowState() & ~Qt::WindowFullScreen ); 0925 } 0926 } 0927 0928 QPoint MarbleWidgetPopupMenu::mousePosition() const 0929 { 0930 return d->m_mousePosition; 0931 } 0932 0933 } 0934 0935 #include "moc_MarbleWidgetPopupMenu.cpp"