Marble

MarbleWidgetPopupMenu.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
4// SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
5// SPDX-FileCopyrightText: 2012 Illya Kovalevskyy <illya.kovalevskyy@gmail.com>
6// SPDX-FileCopyrightText: 2014 Gábor Péterffy <peterffy95@gmail.com>
7//
8
9// Self
10#include "MarbleWidgetPopupMenu.h"
11
12// Marble
13#include "AbstractDataPluginItem.h"
14#include "AbstractFloatItem.h"
15#include "MarbleAboutDialog.h"
16#include "MarbleDirs.h"
17#include "MarbleWidget.h"
18#include "MarbleModel.h"
19#include "GeoDataExtendedData.h"
20#include "GeoDataFolder.h"
21#include "GeoDataPlacemark.h"
22#include "GeoDataLookAt.h"
23#include "GeoDataData.h"
24#include "GeoDataSnippet.h"
25#include "GeoDataStyle.h"
26#include "GeoDataBalloonStyle.h"
27#include "GeoDataIconStyle.h"
28#include "GeoDataPoint.h"
29#include "GeoDataPhotoOverlay.h"
30#include "GeoSceneDocument.h"
31#include "GeoSceneHead.h"
32#include "MarbleClock.h"
33#include "MarbleDebug.h"
34#include "PopupLayer.h"
35#include "Planet.h"
36#include "routing/RoutingManager.h"
37#include "routing/RoutingLayer.h"
38#include "routing/RouteRequest.h"
39#include "EditBookmarkDialog.h"
40#include "BookmarkManager.h"
41#include "ReverseGeocodingRunnerManager.h"
42#include "TemplateDocument.h"
43#include "OsmPlacemarkData.h"
44#include "StyleBuilder.h"
45
46// Qt
47#include <QApplication>
48#include <QFile>
49#include <QMimeData>
50#include <QPointer>
51#include <QAction>
52#include <QClipboard>
53#include <QMenu>
54#include <QMessageBox>
55
56namespace Marble {
57/* TRANSLATOR Marble::MarbleWidgetPopupMenu */
58
59class Q_DECL_HIDDEN MarbleWidgetPopupMenu::Private {
60public:
61 const MarbleModel *const m_model;
62 MarbleWidget *const m_widget;
63
66
67 QMenu m_lmbMenu;
68 QMenu m_rmbMenu;
69
70 QAction *m_infoDialogAction;
71 QAction *m_directionsFromHereAction;
72 QAction *m_directionsToHereAction;
73
74 QAction *const m_copyCoordinateAction;
75 QAction *const m_copyGeoAction;
76
77 QAction *m_rmbExtensionPoint;
78
79 ReverseGeocodingRunnerManager m_runnerManager;
80
81 QPoint m_mousePosition;
82
83public:
84 Private( MarbleWidget *widget, const MarbleModel *model, MarbleWidgetPopupMenu* parent );
85 QMenu* createInfoBoxMenu(QWidget *parent);
86
87 /**
88 * Returns the geo coordinates of the mouse pointer at the last right button menu.
89 * You must not pass 0 as coordinates parameter. The result indicates whether the
90 * coordinates are valid, which will be true if the right button menu was opened at least once.
91 */
92 GeoDataCoordinates mouseCoordinates( QAction* dataContainer ) const;
93
94 static QString filterEmptyShortDescription( const QString &description );
95 void setupDialogOsm( PopupLayer *popup, const GeoDataPlacemark* placemark );
96 void setupDialogSatellite( const GeoDataPlacemark *placemark );
97 static void setupDialogCity( PopupLayer *popup, const GeoDataPlacemark *placemark );
98 static void setupDialogNation( PopupLayer *popup, const GeoDataPlacemark *placemark );
99 static void setupDialogGeoPlaces( PopupLayer *popup, const GeoDataPlacemark *placemark );
100 static void setupDialogSkyPlaces( PopupLayer *popup, const GeoDataPlacemark *placemark );
101 static void setupDialogPhotoOverlay( PopupLayer *popup, const GeoDataPhotoOverlay *overlay);
102};
103
104MarbleWidgetPopupMenu::Private::Private( MarbleWidget *widget, const MarbleModel *model, MarbleWidgetPopupMenu* parent ) :
105 m_model(model),
106 m_widget(widget),
107 m_lmbMenu( m_widget ),
108 m_rmbMenu( m_widget ),
109 m_directionsFromHereAction( nullptr ),
110 m_directionsToHereAction( nullptr ),
111 m_copyCoordinateAction(new QAction(QIcon(QStringLiteral(":/icons/copy-coordinates.png")), tr("Copy Coordinates"), parent)),
112 m_copyGeoAction(new QAction(QIcon(QStringLiteral(":/icons/copy-coordinates.png")), tr("Copy geo: URL"), parent)),
113 m_rmbExtensionPoint( nullptr ),
114 m_runnerManager( model )
115{
116 // Property actions (Left mouse button)
117 m_infoDialogAction = new QAction( parent );
118 m_infoDialogAction->setData( 0 );
119
120 // Tool actions (Right mouse button)
121 m_directionsFromHereAction = new QAction( tr( "Directions &from here" ), parent );
122 m_directionsToHereAction = new QAction( tr( "Directions &to here" ), parent );
123 RouteRequest* request = m_widget->model()->routingManager()->routeRequest();
124 if ( request ) {
125 m_directionsFromHereAction->setIcon( QIcon( request->pixmap( 0, 16 ) ) );
126 int const lastIndex = qMax( 1, request->size()-1 );
127 m_directionsToHereAction->setIcon( QIcon( request->pixmap( lastIndex, 16 ) ) );
128 }
129 QAction* addBookmark = new QAction( QIcon(QStringLiteral(":/icons/bookmark-new.png")),
130 tr( "Add &Bookmark" ), parent );
131 QAction* fullscreenAction = new QAction( tr( "&Full Screen Mode" ), parent );
132 fullscreenAction->setCheckable( true );
133
134 QAction* aboutDialogAction = new QAction(QIcon(QStringLiteral(":/icons/marble.png")), tr("&About"), parent);
135
136 QMenu* infoBoxMenu = createInfoBoxMenu(m_widget);
137
138 const bool smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
139
140 if ( !smallScreen ) {
141 m_rmbExtensionPoint = m_rmbMenu.addSeparator();
142 }
143
144 m_rmbMenu.addAction( m_directionsFromHereAction );
145 m_rmbMenu.addAction( m_directionsToHereAction );
146 m_rmbMenu.addSeparator();
147 m_rmbMenu.addAction( addBookmark );
148 if ( !smallScreen ) {
149 m_rmbMenu.addAction( m_copyCoordinateAction );
150 m_rmbMenu.addAction( m_copyGeoAction );
151 }
152 m_rmbMenu.addAction(QIcon(QStringLiteral(":/icons/addressbook-details.png")), tr("&Address Details"), parent, SLOT(startReverseGeocoding()));
153 m_rmbMenu.addSeparator();
154 m_rmbMenu.addMenu( infoBoxMenu );
155
156 if ( !smallScreen ) {
157 m_rmbMenu.addAction( aboutDialogAction );
158 } else {
159 m_rmbMenu.addAction( fullscreenAction );
160 }
161
162 parent->connect( &m_lmbMenu, SIGNAL(aboutToHide()), SLOT(resetMenu()) );
163 parent->connect( m_directionsFromHereAction, SIGNAL(triggered()), SLOT(directionsFromHere()) );
164 parent->connect( m_directionsToHereAction, SIGNAL(triggered()), SLOT(directionsToHere()) );
165 parent->connect( addBookmark, SIGNAL(triggered()), SLOT(addBookmark()) );
166 parent->connect( aboutDialogAction, SIGNAL(triggered()), SLOT(slotAboutDialog()) );
167 parent->connect( m_copyCoordinateAction, SIGNAL(triggered()), SLOT(slotCopyCoordinates()) );
168 parent->connect( m_copyGeoAction, SIGNAL(triggered()), SLOT(slotCopyGeo()) );
169 parent->connect( m_infoDialogAction, SIGNAL(triggered()), SLOT(slotInfoDialog()) );
170 parent->connect( fullscreenAction, SIGNAL(triggered(bool)), parent, SLOT(toggleFullscreen(bool)) );
171
172 parent->connect( &m_runnerManager, SIGNAL(reverseGeocodingFinished(GeoDataCoordinates,GeoDataPlacemark)),
173 parent, SLOT(showAddressInformation(GeoDataCoordinates,GeoDataPlacemark)) );
174}
175
176QString MarbleWidgetPopupMenu::Private::filterEmptyShortDescription(const QString &description)
177{
178 if(description.isEmpty())
179 return tr("No description available.");
180 return description;
181}
182
183void MarbleWidgetPopupMenu::Private::setupDialogOsm( PopupLayer *popup, const GeoDataPlacemark *placemark )
184{
185 const GeoDataCoordinates location = placemark->coordinate();
186 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter);
187
188 QFile descriptionFile(QStringLiteral(":/marble/webpopup/osm.html"));
189 if (!descriptionFile.open(QIODevice::ReadOnly)) {
190 return;
191 }
192
193 const QString none = QStringLiteral("none");
194
195 QString description = descriptionFile.readAll();
196 const OsmPlacemarkData& data = placemark->osmData();
197 if (!data.containsTagKey("addr:street") && !data.containsTagKey("addr:housenumber")){
198 description.replace(QStringLiteral("<br> %postcode%"), QStringLiteral("%postcode%"));
199 }
200 TemplateDocument doc(description);
201
202 doc[QStringLiteral("name")] = data.tagValue(QStringLiteral("name"));
203
204 QString natural = data.tagValue(QStringLiteral("natural"));
205 if (!natural.isEmpty()) {
206 natural[0] = natural[0].toUpper();
207 if (natural == QLatin1String("Peak")) {
208 QString elevation = data.tagValue(QStringLiteral("ele"));
209 if (!elevation.isEmpty()) {
210 natural = natural + QLatin1String(" - ") + elevation + QLatin1String(" m");
211 }
212 }
213 doc[QStringLiteral("details")] = natural;
214 } else {
215 doc[QStringLiteral("detailsVisibility")] = none;
216 }
217
218 QString amenity;
219 QString shop = data.tagValue(QStringLiteral("shop"));
220 if (!shop.isEmpty()) {
221 shop[0] = shop[0].toUpper();
222
223 if (shop == QLatin1String("Clothes")) {
224 QString type = data.tagValue(QStringLiteral("clothes"));
225 if (type.isEmpty()) {
226 type = data.tagValue(QStringLiteral("designation"));
227 }
228 if (!type.isEmpty()) {
229 type[0] = type[0].toUpper();
230 amenity = QLatin1String("Shop - ") + shop + QLatin1String(" (") + type + QLatin1Char(')');
231 }
232 }
233 if (amenity.isEmpty()) {
234 amenity = QLatin1String("Shop - ") + shop;
235 }
236 } else {
237 amenity = data.tagValue(QStringLiteral("amenity"));
238 if (!amenity.isEmpty()) {
239 amenity[0] = amenity[0].toUpper();
240 }
241 }
242 if (!amenity.isEmpty()) {
243 doc[QStringLiteral("amenity")] = amenity;
244 } else {
245 doc[QStringLiteral("amenityVisibility")] = none;
246 }
247
248 QString cuisine = data.tagValue(QStringLiteral("cuisine"));
249 if (!cuisine.isEmpty()) {
250 cuisine[0] = cuisine[0].toUpper();
251 doc[QStringLiteral("cuisine")] = cuisine;
252 } else {
253 doc[QStringLiteral("cuisineVisibility")] = none;
254 }
255
256 QString openingHours = data.tagValue(QStringLiteral("opening_hours"));
257 if (!openingHours.isEmpty()) {
258 doc[QStringLiteral("openinghours")] = openingHours;
259 } else {
260 doc[QStringLiteral("openinghoursVisibility")] = none;
261 }
262
263 bool hasContactsData = false;
264
265 const QStringList addressItemKeys = QStringList()
266 << QStringLiteral("street")
267 << QStringLiteral("housenumber")
268 << QStringLiteral("postcode")
269 << QStringLiteral("city");
270 bool hasAddressItem = false;
271 QStringList addressItems;
272 for (const QString& key: addressItemKeys) {
273 const QString item = data.tagValue(QLatin1String("addr:") + key);
274 if (!item.isEmpty()) {
275 hasAddressItem = true;
276 }
277 addressItems << item;
278 }
279 if (hasAddressItem) {
280 hasContactsData = true;
281 for(int i = 0; i < addressItemKeys.size(); ++i) {
282 doc[addressItemKeys[i]] = addressItems[i];
283 }
284 } else {
285 doc[QStringLiteral("addressVisibility")] = none;
286 }
287
288 QString phoneData = data.tagValue(QStringLiteral("phone"));
289 if (!phoneData.isEmpty()) {
290 hasContactsData = true;
291 doc[QStringLiteral("phone")] = phoneData;
292 } else {
293 doc[QStringLiteral("phoneVisibility")] = none;
294 }
295
296 QString websiteData;
297 auto const tags = QStringList() << "website" << "contact:website" << "facebook" << "contact:facebook" << "url";
298 for(const QString &tag: tags) {
299 websiteData = data.tagValue(tag);
300 if (!websiteData.isEmpty()) {
301 break;
302 }
303 }
304 if (!websiteData.isEmpty()) {
305 hasContactsData = true;
306 doc[QStringLiteral("website")] = websiteData;
307 } else {
308 doc[QStringLiteral("websiteVisibility")] = none;
309 }
310
311 if (!hasContactsData) {
312 doc[QStringLiteral("contactVisibility")] = none;
313 }
314
315 bool hasFacilitiesData = false;
316
317 const QString wheelchair = data.tagValue(QStringLiteral("wheelchair"));
318 if (!wheelchair.isEmpty()) {
319 hasFacilitiesData = true;
320 doc[QStringLiteral("wheelchair")] = wheelchair;
321 } else {
322 doc[QStringLiteral("wheelchairVisibility")] = none;
323 }
324
325 const QString internetAccess = data.tagValue(QStringLiteral("internet_access"));
326 if (!internetAccess.isEmpty()) {
327 hasFacilitiesData = true;
328 doc[QStringLiteral("internetaccess")] = internetAccess;
329 } else {
330 doc[QStringLiteral("internetVisibility")] = none;
331 }
332
333 const QString smoking = data.tagValue(QStringLiteral("smoking"));
334 if (!smoking.isEmpty()) {
335 hasFacilitiesData = true;
336 doc[QStringLiteral("smoking")] = smoking;
337 } else {
338 doc[QStringLiteral("smokingVisibility")] = none;
339 }
340
341 if (!hasFacilitiesData) {
342 doc[QStringLiteral("facilitiesVisibility")] = none;
343 }
344
345 const QString flagPath = m_widget->styleBuilder()->createStyle(StyleParameters(placemark))->iconStyle().iconPath();
346 doc["flag"] = flagPath;
347 popup->setContent(doc.finalText());
348}
349
350void MarbleWidgetPopupMenu::Private::setupDialogSatellite( const GeoDataPlacemark *placemark )
351{
352 PopupLayer *const popup = m_widget->popupLayer();
353 const GeoDataCoordinates location = placemark->coordinate(m_widget->model()->clockDateTime());
354 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter);
355
356 const QString description = placemark->description();
357 TemplateDocument doc(description);
358 doc["altitude"] = QString::number(location.altitude(), 'f', 2);
359 doc["latitude"] = location.latToString();
360 doc["longitude"] = location.lonToString();
361 popup->setContent(doc.finalText());
362}
363
364void MarbleWidgetPopupMenu::Private::setupDialogCity( PopupLayer *popup, const GeoDataPlacemark *placemark )
365{
366 const GeoDataCoordinates location = placemark->coordinate();
367 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter);
368
369 QFile descriptionFile(QStringLiteral(":/marble/webpopup/city.html"));
370 if (!descriptionFile.open(QIODevice::ReadOnly)) {
371 return;
372 }
373
374 const QString description = descriptionFile.readAll();
375 TemplateDocument doc(description);
376
377 doc["name"] = placemark->name();
378 QString roleString;
379 const QString role = placemark->role();
380 if (role == QLatin1String("PPLC")) {
381 roleString = tr("National Capital");
382 } else if (role == QLatin1String("PPL")) {
383 roleString = tr("City");
384 } else if (role == QLatin1String("PPLA")) {
385 roleString = tr("State Capital");
386 } else if (role == QLatin1String("PPLA2")) {
387 roleString = tr("County Capital");
388 } else if (role == QLatin1String("PPLA3") ||
389 role == QLatin1String("PPLA4")) {
390 roleString = tr("Capital");
391 } else if (role == QLatin1String("PPLF") ||
392 role == QLatin1String("PPLG") ||
393 role == QLatin1String("PPLL") ||
394 role == QLatin1String("PPLQ") ||
395 role == QLatin1String("PPLR") ||
396 role == QLatin1String("PPLS") ||
397 role == QLatin1String("PPLW")) {
398 roleString = tr("Village");
399 }
400
401 doc["category"] = roleString;
402 doc["shortDescription"] = filterEmptyShortDescription(placemark->description());
403 doc["latitude"] = location.latToString();
404 doc["longitude"] = location.lonToString();
405 doc["elevation"] = QString::number(location.altitude(), 'f', 2);
406 doc["population"] = QString::number(placemark->population());
407 doc["country"] = placemark->countryCode();
408 doc["state"] = placemark->state();
409
410 QString dst = QStringLiteral("%1").arg((placemark->extendedData().value(QStringLiteral("gmt")).value().toInt() +
411 placemark->extendedData().value(QStringLiteral("dst")).value().toInt()) /
412 ( double ) 100, 0, 'f', 1 );
413 // There is an issue about UTC.
414 // It's possible to variants (e.g.):
415 // +1.0 and -1.0, but dst does not have + an the start
416 if (dst.startsWith(QLatin1Char('-'))) {
417 doc["timezone"] = dst;
418 } else {
419 doc["timezone"] = QLatin1Char('+') + dst;
420 }
421
422 const QString flagPath = MarbleDirs::path(
423 QLatin1String("flags/flag_") + placemark->countryCode().toLower() + QLatin1String(".svg"));
424 doc["flag"] = flagPath;
425
426 popup->setContent(doc.finalText());
427}
428
429void MarbleWidgetPopupMenu::Private::setupDialogNation( PopupLayer *popup, const GeoDataPlacemark *index)
430{
431 const GeoDataCoordinates location = index->coordinate();
432 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter);
433
434 QFile descriptionFile(QStringLiteral(":/marble/webpopup/nation.html"));
435 if (!descriptionFile.open(QIODevice::ReadOnly)) {
436 return;
437 }
438
439 const QString description = descriptionFile.readAll();
440 TemplateDocument doc(description);
441
442 doc["name"] = index->name();
443 doc["shortDescription"] = filterEmptyShortDescription(index->description());
444 doc["latitude"] = location.latToString();
445 doc["longitude"] = location.lonToString();
446 doc["elevation"] = QString::number(location.altitude(), 'f', 2);
447 doc["population"] = QString::number(index->population());
448 doc["area"] = QString::number(index->area(), 'f', 2);
449
450 const QString flagPath = MarbleDirs::path(QString("flags/flag_%1.svg").arg(index->countryCode().toLower()) );
451 doc["flag"] = flagPath;
452
453 popup->setContent(doc.finalText());
454}
455
456void MarbleWidgetPopupMenu::Private::setupDialogGeoPlaces( PopupLayer *popup, const GeoDataPlacemark *index)
457{
458 const GeoDataCoordinates location = index->coordinate();
459 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter);
460
461 QFile descriptionFile(QStringLiteral(":/marble/webpopup/geoplace.html"));
462 if (!descriptionFile.open(QIODevice::ReadOnly)) {
463 return;
464 }
465
466 const QString description = descriptionFile.readAll();
467 TemplateDocument doc(description);
468
469 doc["name"] = index->name();
470 doc["latitude"] = location.latToString();
471 doc["longitude"] = location.lonToString();
472 doc["elevation"] = QString::number(location.altitude(), 'f', 2);
473 doc["shortDescription"] = filterEmptyShortDescription(index->description());
474
475 popup->setContent(doc.finalText());
476}
477
478void MarbleWidgetPopupMenu::Private::setupDialogSkyPlaces( PopupLayer *popup, const GeoDataPlacemark *index)
479{
480 const GeoDataCoordinates location = index->coordinate();
481 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter);
482
483 QFile descriptionFile(QStringLiteral(":/marble/webpopup/skyplace.html"));
484 if (!descriptionFile.open(QIODevice::ReadOnly)) {
485 return;
486 }
487
488 const QString description = descriptionFile.readAll();
489 TemplateDocument doc(description);
490
491 doc["name"] = index->name();
492 doc["latitude"] = GeoDataCoordinates::latToString(
493 location.latitude(), GeoDataCoordinates::Astro, GeoDataCoordinates::Radian, -1, 'f');
494 doc["longitude"] = GeoDataCoordinates::lonToString(
495 location.longitude(), GeoDataCoordinates::Astro, GeoDataCoordinates::Radian, -1, 'f');
496 doc["shortDescription"] = filterEmptyShortDescription(index->description());
497
498 popup->setContent(doc.finalText());
499}
500
501void MarbleWidgetPopupMenu::Private::setupDialogPhotoOverlay( PopupLayer *popup, const GeoDataPhotoOverlay *index )
502{
503 const GeoDataCoordinates location = index->point().coordinates();
504 popup->setCoordinates(location, Qt::AlignRight | Qt::AlignVCenter);
505
506 QFile descriptionFile(QStringLiteral(":/marble/webpopup/photooverlay.html"));
507
508 if ( !descriptionFile.open(QIODevice::ReadOnly) ) {
509 return;
510 }
511
512 const QString description = descriptionFile.readAll();
513 TemplateDocument doc(description);
514 doc["name"] = index->name();
515 doc["latitude"] = location.latToString();
516 doc["longitude"] = location.lonToString();
517 doc["elevation"] = QString::number(location.altitude(), 'f', 2);
518 doc["shortDescription"] = filterEmptyShortDescription(index->description());
519 doc["source"] = index->absoluteIconFile();
520 doc["width"] = QString::number(200);
521 doc["height"] = QString::number(100);
522 QString const basePath = index->resolvePath(".");
523 QUrl const baseUrl = (basePath != QLatin1String(".")) ? QUrl::fromLocalFile(basePath + QLatin1Char('/')) : QUrl();
524 popup->setContent(doc.finalText(), baseUrl );
525}
526
527MarbleWidgetPopupMenu::MarbleWidgetPopupMenu(MarbleWidget *widget,
528 const MarbleModel *model)
529 : QObject(widget),
530 d( new Private( widget, model, this ) )
531{
532 // nothing to do
533}
534
535MarbleWidgetPopupMenu::~MarbleWidgetPopupMenu()
536{
537 delete d;
538}
539
540QMenu* MarbleWidgetPopupMenu::Private::createInfoBoxMenu(QWidget* parent)
541{
542 QMenu* menu = new QMenu(tr("&Info Boxes"), parent);
543 QList<AbstractFloatItem *> floatItemList = m_widget->floatItems();
544
547 for (; iter != end; ++iter )
548 {
549 menu->addAction( (*iter)->action() );
550 }
551 return menu;
552}
553
554void MarbleWidgetPopupMenu::showLmbMenu( int xpos, int ypos )
555{
556 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
557 if ( smallScreen ) {
558 showRmbMenu( xpos, ypos );
559 return;
560 }
561
562 d->m_mousePosition.setX(xpos);
563 d->m_mousePosition.setY(ypos);
564
565 const QPoint curpos = QPoint( xpos, ypos );
566 d->m_featurelist = d->m_widget->whichFeatureAt( curpos );
567
568 int actionidx = 1;
569
570 QVector<const GeoDataFeature*>::const_iterator it = d->m_featurelist.constBegin();
571 QVector<const GeoDataFeature*>::const_iterator const itEnd = d->m_featurelist.constEnd();
572 for (; it != itEnd; ++it )
573 {
574 QString name = (*it)->name();
575 QPixmap icon = QPixmap::fromImage( ( *it)->style()->iconStyle().icon() );
576 d->m_infoDialogAction->setData( actionidx );
577 d->m_infoDialogAction->setText( name );
578 d->m_infoDialogAction->setIcon( icon );
579 // Insert as first action in the menu
580 QAction *firstAction = nullptr;
581 if( !d->m_lmbMenu.actions().isEmpty() ) {
582 firstAction = d->m_lmbMenu.actions().first();
583 }
584 d->m_lmbMenu.insertAction( firstAction, d->m_infoDialogAction );
585 actionidx++;
586 }
587
588 d->m_itemList = d->m_widget->whichItemAt( curpos );
589 QList<AbstractDataPluginItem *>::const_iterator itW = d->m_itemList.constBegin();
590 QList<AbstractDataPluginItem *>::const_iterator const itWEnd = d->m_itemList.constEnd();
591 for (; itW != itWEnd; ++itW )
592 {
593 for ( QAction* action: (*itW)->actions() ) {
594 d->m_lmbMenu.addAction( action );
595 }
596 }
597
598 switch ( d->m_lmbMenu.actions().size() ) {
599 case 0: // nothing to do, ignore
600 break;
601
602 case 1: // one action? perform immediately
603 d->m_lmbMenu.actions().first()->activate( QAction::Trigger );
604 d->m_lmbMenu.clear();
605 break;
606
607 default:
608 d->m_lmbMenu.popup( d->m_widget->mapToGlobal( curpos ) );
609 }
610}
611
612
613void MarbleWidgetPopupMenu::showRmbMenu( int xpos, int ypos )
614{
615 qreal lon, lat;
616 const bool visible = d->m_widget->geoCoordinates( xpos, ypos, lon, lat, GeoDataCoordinates::Radian );
617 if ( !visible )
618 return;
619
620 d->m_mousePosition.setX(xpos);
621 d->m_mousePosition.setY(ypos);
622
623 QPoint curpos = QPoint( xpos, ypos );
624 d->m_copyCoordinateAction->setData( curpos );
625 d->m_copyGeoAction->setData( curpos );
626
627 bool const showDirectionButtons = d->m_widget->routingLayer() && d->m_widget->routingLayer()->isInteractive();
628 d->m_directionsFromHereAction->setVisible( showDirectionButtons );
629 d->m_directionsToHereAction->setVisible( showDirectionButtons );
630 RouteRequest* request = d->m_widget->model()->routingManager()->routeRequest();
631 if ( request ) {
632 int const lastIndex = qMax( 1, request->size()-1 );
633 d->m_directionsToHereAction->setIcon( QIcon( request->pixmap( lastIndex, 16 ) ) );
634 }
635
636 d->m_rmbMenu.popup( d->m_widget->mapToGlobal( curpos ) );
637}
638
639void MarbleWidgetPopupMenu::resetMenu()
640{
641 d->m_lmbMenu.clear();
642}
643
644void MarbleWidgetPopupMenu::slotInfoDialog()
645{
646 QAction *action = qobject_cast<QAction *>( sender() );
647 if ( action == nullptr ) {
648 mDebug() << "Warning: slotInfoDialog should be called by a QAction signal";
649 return;
650 }
651
652 int actionidx = action->data().toInt();
653
654 if ( actionidx > 0 ) {
655 const GeoDataPlacemark *placemark = dynamic_cast<const GeoDataPlacemark*>(d->m_featurelist.at( actionidx -1 ));
656 const GeoDataPhotoOverlay *overlay = dynamic_cast<const GeoDataPhotoOverlay*>(d->m_featurelist.at( actionidx - 1 ));
657 PopupLayer* popup = d->m_widget->popupLayer();
658 bool isSatellite = false;
659 bool isCity = false;
660 bool isNation = false;
661
662 const OsmPlacemarkData& data = placemark->osmData();
663
664 bool hasOsmData = false;
665
666 QStringList recognizedTags;
667 recognizedTags << "name" << "amenity" << "cuisine" << "opening_hours";
668 recognizedTags << "addr:street" << "addr:housenumber" << "addr:postcode";
669 recognizedTags << "addr:city" << "phone" << "wheelchair" << "internet_access";
670 recognizedTags << "smoking" << "website" << "contact:website" << "facebook";
671 recognizedTags << "contact:facebook" << "url";
672
673 for(const QString &tag: recognizedTags) {
674 if (data.containsTagKey(tag)) {
675 hasOsmData = true;
676 break;
677 }
678 }
679
680 if ( placemark ) {
681 isSatellite = (placemark->visualCategory() == GeoDataPlacemark::Satellite);
682 isCity = (placemark->visualCategory() >= GeoDataPlacemark::SmallCity &&
683 placemark->visualCategory() <= GeoDataPlacemark::LargeNationCapital);
684 isNation = (placemark->visualCategory() == GeoDataPlacemark::Nation);
685 }
686
687 bool isSky = false;
688
689 if ( d->m_widget->model()->mapTheme() ) {
690 isSky = d->m_widget->model()->mapTheme()->head()->target() == QLatin1String("sky");
691 }
692
693 popup->setSize(QSizeF(420, 420));
694
695 if (hasOsmData){
696 d->setupDialogOsm( popup, placemark );
697 } else if (isSatellite) {
698 d->setupDialogSatellite( placemark );
699 } else if (isCity) {
700 Private::setupDialogCity( popup, placemark );
701 } else if (isNation) {
702 Private::setupDialogNation( popup, placemark );
703 } else if (isSky) {
704 Private::setupDialogSkyPlaces( popup, placemark );
705 } else if ( overlay ) {
706 Private::setupDialogPhotoOverlay( popup, overlay );
707 } else if ( placemark && placemark->role().isEmpty() ) {
708 popup->setContent( placemark->description() );
709 } else if ( placemark ) {
710 Private::setupDialogGeoPlaces( popup, placemark );
711 }
712
713 if ( placemark ) {
714 if ( placemark->style() == nullptr ) {
715 popup->setBackgroundColor(QColor(Qt::white));
716 popup->setTextColor(QColor(Qt::black));
717 return;
718 }
719 if ( placemark->style()->balloonStyle().displayMode() == GeoDataBalloonStyle::Hide ) {
720 popup->setVisible(false);
721 return;
722 }
723
724 QString content = placemark->style()->balloonStyle().text();
725 if (content.length() > 0) {
726 content.replace(QStringLiteral("$[name]"), placemark->name(), Qt::CaseInsensitive);
727 content.replace(QStringLiteral("$[description]"), placemark->description(), Qt::CaseInsensitive);
728 content.replace(QStringLiteral("$[address]"), placemark->address(), Qt::CaseInsensitive);
729 // @TODO: implement the line calculation, so that snippet().maxLines actually has effect.
730 content.replace(QStringLiteral("$[snippet]"), placemark->snippet().text(), Qt::CaseInsensitive);
731 content.replace(QStringLiteral("$[id]"), placemark->id(), Qt::CaseInsensitive);
732 QString const basePath = placemark->resolvePath(".");
733 QUrl const baseUrl = (basePath != QLatin1String(".")) ? QUrl::fromLocalFile(basePath + QLatin1Char('/')) : QUrl();
734 popup->setContent(content, baseUrl );
735 }
736
737 popup->setBackgroundColor(placemark->style()->balloonStyle().backgroundColor());
738 popup->setTextColor(placemark->style()->balloonStyle().textColor());
739 }
740
741 popup->popup();
742 }
743}
744
745void MarbleWidgetPopupMenu::slotCopyCoordinates()
746{
747 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction );
748 if ( coordinates.isValid() ) {
749 const qreal longitude_degrees = coordinates.longitude(GeoDataCoordinates::Degree);
750 const qreal latitude_degrees = coordinates.latitude(GeoDataCoordinates::Degree);
751
752 // importing this representation into Marble does not show anything,
753 // but Merkaartor shows the point
754 const QString kmlRepresentation = QString::fromLatin1(
755 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
756 "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
757 "<Document>\n"
758 " <Placemark>\n"
759// " <name></name>\n"
760 " <Point>\n"
761 " <coordinates>%1,%2</coordinates>\n"
762 " </Point>\n"
763 " </Placemark>\n"
764 "</Document>\n"
765 "</kml>\n"
766 ).arg(longitude_degrees, 0, 'f', 10).arg(latitude_degrees, 0, 'f', 10);
767
768 // importing this data into Marble and Merkaartor works
769 const QString gpxRepresentation = QString::fromLatin1(
770 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
771 "<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" creator=\"trippy\" version=\"0.1\"\n"
772 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
773 " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n"
774 " <wpt lat=\"%1\" lon=\"%2\">\n"
775// " <ele>%3</ele>\n"
776// " <time></time>\n"
777// " <name>%4</name>\n"
778 " </wpt>\n"
779 "</gpx>\n"
780 ).arg(latitude_degrees, 0, 'f', 10).arg(longitude_degrees, 0, 'f', 10);
781
782 QString positionString = coordinates.toString();
783
784 QMimeData * const myMimeData = new QMimeData();
785 myMimeData->setText(positionString);
786 myMimeData->setData(QLatin1String("application/vnd.google-earth.kml+xml"), kmlRepresentation.toUtf8());
787 myMimeData->setData(QLatin1String("application/gpx+xml"), gpxRepresentation.toUtf8());
788
789 QClipboard * const clipboard = QApplication::clipboard();
790 clipboard->setMimeData(myMimeData);
791 }
792}
793
794void MarbleWidgetPopupMenu::slotCopyGeo()
795{
796 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction );
797 if ( coordinates.isValid() ) {
798 const qreal latitude_degrees = coordinates.latitude(GeoDataCoordinates::Degree);
799 const qreal longitude_degrees = coordinates.longitude(GeoDataCoordinates::Degree);
800
801 QMimeData * const myMimeData = new QMimeData();
802 QList<QUrl> urls = { QUrl(QString("geo:%1,%2").arg(latitude_degrees, 0, 'f', 10).arg(longitude_degrees, 0, 'f', 10)) };
803 myMimeData->setUrls(urls);
804 QClipboard * const clipboard = QApplication::clipboard();
805 clipboard->setMimeData(myMimeData);
806 }
807}
808
809
810void MarbleWidgetPopupMenu::slotAboutDialog()
811{
812 QPointer<MarbleAboutDialog> dialog = new MarbleAboutDialog( d->m_widget );
813 dialog->exec();
814 delete dialog;
815}
816
817void MarbleWidgetPopupMenu::addAction( Qt::MouseButton button, QAction* action )
818{
819 if ( button == Qt::RightButton ) {
820 d->m_rmbMenu.insertAction( d->m_rmbExtensionPoint, action );
821 } else {
822 d->m_lmbMenu.addAction( action );
823 }
824}
825
826void MarbleWidgetPopupMenu::directionsFromHere()
827{
828 RouteRequest* request = d->m_widget->model()->routingManager()->routeRequest();
829 if ( request )
830 {
831 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction );
832 if ( coordinates.isValid() ) {
833 if ( request->size() > 0 ) {
834 request->setPosition( 0, coordinates );
835 } else {
836 request->append( coordinates );
837 }
838 d->m_widget->model()->routingManager()->retrieveRoute();
839 }
840 }
841}
842
843void MarbleWidgetPopupMenu::directionsToHere()
844{
845 RouteRequest* request = d->m_widget->model()->routingManager()->routeRequest();
846 if ( request )
847 {
848 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction );
849 if ( coordinates.isValid() ) {
850 if ( request->size() > 1 ) {
851 request->setPosition( request->size()-1, coordinates );
852 } else {
853 request->append( coordinates );
854 }
855 d->m_widget->model()->routingManager()->retrieveRoute();
856 }
857 }
858}
859
860GeoDataCoordinates MarbleWidgetPopupMenu::Private::mouseCoordinates( QAction* dataContainer ) const
861{
862 if ( !dataContainer ) {
863 return GeoDataCoordinates();
864 }
865
866 if ( !m_featurelist.isEmpty() && geodata_cast<GeoDataPlacemark>(m_featurelist.first())) {
867 const GeoDataPlacemark * placemark = static_cast<const GeoDataPlacemark*>( m_featurelist.first() );
868 return placemark->coordinate( m_model->clock()->dateTime() );
869 } else {
870 QPoint p = dataContainer->data().toPoint();
871 qreal lat( 0.0 ), lon( 0.0 );
872
873 const bool valid = m_widget->geoCoordinates( p.x(), p.y(), lon, lat, GeoDataCoordinates::Radian );
874 if ( valid ) {
875 return GeoDataCoordinates( lon, lat );
876 }
877 }
878
879 return GeoDataCoordinates();
880}
881
882void MarbleWidgetPopupMenu::startReverseGeocoding()
883{
884 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction );
885 if ( coordinates.isValid() ) {
886 d->m_runnerManager.reverseGeocoding( coordinates );
887 }
888}
889
890void MarbleWidgetPopupMenu::showAddressInformation(const GeoDataCoordinates &, const GeoDataPlacemark &placemark)
891{
892 QString text = placemark.address();
893 if ( !text.isEmpty() ) {
894 QMessageBox::information( d->m_widget, tr( "Address Details" ), text, QMessageBox::Ok );
895 }
896}
897
898void MarbleWidgetPopupMenu::addBookmark()
899{
900 const GeoDataCoordinates coordinates = d->mouseCoordinates( d->m_copyCoordinateAction );
901 if ( coordinates.isValid() ) {
902 QPointer<EditBookmarkDialog> dialog = new EditBookmarkDialog( d->m_widget->model()->bookmarkManager(), d->m_widget );
903 dialog->setMarbleWidget( d->m_widget );
904 dialog->setCoordinates( coordinates );
905 dialog->setRange( d->m_widget->lookAt().range() );
906 dialog->setReverseGeocodeName();
907 if ( dialog->exec() == QDialog::Accepted ) {
908 d->m_widget->model()->bookmarkManager()->addBookmark( dialog->folder(), dialog->bookmark() );
909 }
910 delete dialog;
911 }
912}
913
914void MarbleWidgetPopupMenu::toggleFullscreen( bool enabled )
915{
916 QWidget* parent = d->m_widget;
917 for ( ; parent->parentWidget(); parent = parent->parentWidget() ) {
918 // nothing to do
919 }
920
921 if ( enabled ) {
922 parent->setWindowState( parent->windowState() | Qt::WindowFullScreen );
923 } else {
924 parent->setWindowState( parent->windowState() & ~Qt::WindowFullScreen );
925 }
926}
927
928QPoint MarbleWidgetPopupMenu::mousePosition() const
929{
930 return d->m_mousePosition;
931}
932
933}
934
935#include "moc_MarbleWidgetPopupMenu.cpp"
This file contains the headers for MarbleModel.
This file contains the headers for MarbleWidget.
A 3d point representation.
Points to be included in a route.
int size() const
Number of points in the route.
void setPosition(int index, const GeoDataCoordinates &position, const QString &name=QString())
Change the value of the element at the given position.
void append(const GeoDataCoordinates &coordinates, const QString &name=QString())
Add the given element to the end.
QVariant location(const QVariant &res)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QString name(StandardAction id)
QAction * addBookmark(const QObject *recvr, const char *slot, QObject *parent)
const QList< QKeySequence > & end()
Binds a QML item to a specific geodetic location in screen coordinates.
void setCheckable(bool)
QVariant data() const const
void setMimeData(QMimeData *src, Mode mode)
QClipboard * clipboard()
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype size() const const
QAction * addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut)
StandardButton information(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons, StandardButton defaultButton)
void setData(const QString &mimeType, const QByteArray &data)
void setText(const QString &text)
void setUrls(const QList< QUrl > &urls)
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
int x() const const
int y() const const
QString arg(Args &&... args) const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString toUpper() const const
QByteArray toUtf8() const const
AlignRight
CaseInsensitive
MouseButton
WindowFullScreen
QUrl fromLocalFile(const QString &localFile)
int toInt(bool *ok) const const
QPoint toPoint() const const
QWidget * parentWidget() const const
void setWindowState(Qt::WindowStates windowState)
Qt::WindowStates windowState() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 14 2024 11:54:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.