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"