File indexing completed on 2024-04-28 15:14:32
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 // 0006 0007 0008 #include "ControlView.h" 0009 0010 #include <QCloseEvent> 0011 #include <QLayout> 0012 #include <QPrintDialog> 0013 #include <QPrintPreviewDialog> 0014 #include <QPrinter> 0015 #include <QPainter> 0016 #include <QTextDocument> 0017 #include <QUrl> 0018 #include <QDesktopServices> 0019 #include <QNetworkAccessManager> 0020 #include <QNetworkReply> 0021 #include <QNetworkRequest> 0022 #include <QProcess> 0023 #include <QTimer> 0024 #include <QFileInfo> 0025 #include <QMessageBox> 0026 #include <QMainWindow> 0027 #include <QDockWidget> 0028 #include <QShortcut> 0029 #include <QToolBar> 0030 #include <QMimeData> 0031 #include <QPixmap> 0032 0033 #ifdef MARBLE_DBUS 0034 #include <QDBusConnection> 0035 #include "MarbleDBusInterface.h" 0036 #endif 0037 0038 #include "GeoDataLatLonAltBox.h" 0039 #include "GeoSceneDocument.h" 0040 #include "GeoSceneHead.h" 0041 #include "GeoUriParser.h" 0042 #include "MarbleDebug.h" 0043 #include "MarbleDirs.h" 0044 #include "MarbleModel.h" 0045 #include "MarbleMap.h" 0046 #include "MapThemeManager.h" 0047 #include "PrintOptionsWidget.h" 0048 #include "ViewportParams.h" 0049 #include "ViewParams.h" 0050 #include "routing/Route.h" 0051 #include "routing/RoutingManager.h" 0052 #include "routing/RoutingModel.h" 0053 #include "routing/RouteRequest.h" 0054 #include "routing/RoutingWidget.h" 0055 #include "ExternalEditorDialog.h" 0056 #include "CurrentLocationWidget.h" 0057 #include "SearchWidget.h" 0058 #include "TourWidget.h" 0059 #include "MapViewWidget.h" 0060 #include "FileViewWidget.h" 0061 #include "LegendWidget.h" 0062 #include "BookmarkManager.h" 0063 #include "cloudsync/CloudSyncManager.h" 0064 #include "cloudsync/BookmarkSyncManager.h" 0065 #include "cloudsync/RouteSyncManager.h" 0066 #include "cloudsync/ConflictDialog.h" 0067 #include "cloudsync/MergeItem.h" 0068 #include "RenderPlugin.h" 0069 0070 namespace Marble 0071 { 0072 0073 ControlView::ControlView( QWidget *parent ) 0074 : QWidget( parent ), 0075 m_mapThemeManager( new MapThemeManager( this ) ), 0076 m_searchDock( nullptr ), 0077 m_locationWidget( nullptr ), 0078 m_conflictDialog( nullptr ), 0079 m_togglePanelVisibilityAction( nullptr ), 0080 m_isPanelVisible( true ), 0081 m_tourWidget( nullptr ), 0082 m_annotationDock( nullptr ), 0083 m_annotationPlugin( nullptr ) 0084 { 0085 setWindowTitle( tr( "Marble - Virtual Globe" ) ); 0086 0087 resize( 680, 640 ); 0088 0089 m_marbleWidget = new MarbleWidget( this ); 0090 m_marbleWidget->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding, 0091 QSizePolicy::MinimumExpanding ) ); 0092 #ifdef MARBLE_DBUS 0093 new MarbleDBusInterface( m_marbleWidget ); 0094 QDBusConnection::sessionBus().registerObject( "/Marble", m_marbleWidget ); 0095 if (!QDBusConnection::sessionBus().registerService( "org.kde.marble" )) { 0096 QString const urlWithPid = QString("org.kde.marble-%1").arg( QCoreApplication::applicationPid() ); 0097 if ( !QDBusConnection::sessionBus().registerService( urlWithPid ) ) { 0098 mDebug() << "Failed to register service org.kde.marble and " << urlWithPid << " with the DBus session bus."; 0099 } 0100 } 0101 #endif 0102 0103 0104 QVBoxLayout* layout = new QVBoxLayout; 0105 layout->addWidget( m_marbleWidget ); 0106 layout->setMargin( 0 ); 0107 setLayout( layout ); 0108 0109 m_cloudSyncManager = new CloudSyncManager( this ); 0110 m_cloudSyncManager->routeSyncManager()->setRoutingManager( m_marbleWidget->model()->routingManager() ); 0111 BookmarkSyncManager* bookmarkSyncManager = m_cloudSyncManager->bookmarkSyncManager(); 0112 bookmarkSyncManager->setBookmarkManager( m_marbleWidget->model()->bookmarkManager() ); 0113 m_conflictDialog = new ConflictDialog( m_marbleWidget ); 0114 connect( bookmarkSyncManager, SIGNAL(mergeConflict(MergeItem*)), this, SLOT(showConflictDialog(MergeItem*)) ); 0115 connect( bookmarkSyncManager, SIGNAL(syncComplete()), m_conflictDialog, SLOT(stopAutoResolve()) ); 0116 connect( m_conflictDialog, SIGNAL(resolveConflict(MergeItem*)), bookmarkSyncManager, SLOT(resolveConflict(MergeItem*)) ); 0117 0118 setAcceptDrops(true); 0119 } 0120 0121 ControlView::~ControlView() 0122 { 0123 // nothing to do 0124 } 0125 0126 QString ControlView::applicationVersion() 0127 { 0128 return MARBLE_VERSION_STRING; 0129 } 0130 0131 MapThemeManager *ControlView::mapThemeManager() 0132 { 0133 return m_mapThemeManager; 0134 } 0135 0136 void ControlView::zoomIn() 0137 { 0138 m_marbleWidget->zoomIn(); 0139 } 0140 0141 void ControlView::zoomOut() 0142 { 0143 m_marbleWidget->zoomOut(); 0144 } 0145 0146 void ControlView::moveLeft() 0147 { 0148 m_marbleWidget->moveLeft(Marble::Linear); 0149 } 0150 0151 void ControlView::moveRight() 0152 { 0153 m_marbleWidget->moveRight(Marble::Linear); 0154 } 0155 0156 void ControlView::moveUp() 0157 { 0158 m_marbleWidget->moveUp(Marble::Linear); 0159 } 0160 0161 void ControlView::moveDown() 0162 { 0163 m_marbleWidget->moveDown(Marble::Linear); 0164 } 0165 0166 QString ControlView::defaultMapThemeId() const 0167 { 0168 QStringList fallBackThemes; 0169 fallBackThemes << "earth/srtm/srtm.dgml"; 0170 fallBackThemes << "earth/bluemarble/bluemarble.dgml"; 0171 fallBackThemes << "earth/openstreetmap/openstreetmap.dgml"; 0172 0173 const QStringList installedThemes = m_mapThemeManager->mapThemeIds(); 0174 0175 for(const QString &fallback: fallBackThemes) { 0176 if (installedThemes.contains(fallback)) { 0177 return fallback; 0178 } 0179 } 0180 0181 if (installedThemes.size()) { 0182 return installedThemes.first(); 0183 } 0184 0185 return QString(); 0186 } 0187 0188 void ControlView::printMapScreenShot( const QPointer<QPrintDialog>& printDialog) 0189 { 0190 #ifndef QT_NO_PRINTER 0191 PrintOptionsWidget* printOptions = new PrintOptionsWidget( this ); 0192 bool const mapCoversViewport = m_marbleWidget->viewport()->mapCoversViewport(); 0193 printOptions->setBackgroundControlsEnabled( !mapCoversViewport ); 0194 bool hasLegend = m_marbleWidget->model()->legend() != nullptr; 0195 printOptions->setLegendControlsEnabled( hasLegend ); 0196 bool hasRoute = marbleWidget()->model()->routingManager()->routingModel()->rowCount() > 0; 0197 printOptions->setPrintRouteSummary( hasRoute ); 0198 printOptions->setPrintDrivingInstructions( hasRoute ); 0199 printOptions->setPrintDrivingInstructionsAdvice( hasRoute ); 0200 printOptions->setRouteControlsEnabled( hasRoute ); 0201 printDialog->setOptionTabs( QList<QWidget*>() << printOptions ); 0202 0203 if ( printDialog->exec() == QDialog::Accepted ) { 0204 QTextDocument document; 0205 QString text = "<html><head><title>Marble Printout</title></head><body>"; 0206 QPalette const originalPalette = m_marbleWidget->palette(); 0207 bool const wasBackgroundVisible = m_marbleWidget->showBackground(); 0208 bool const hideBackground = !mapCoversViewport && !printOptions->printBackground(); 0209 if ( hideBackground ) { 0210 // Temporarily remove the black background and layers painting on it 0211 m_marbleWidget->setShowBackground( false ); 0212 m_marbleWidget->setPalette( QPalette ( Qt::white ) ); 0213 m_marbleWidget->update(); 0214 } 0215 0216 if ( printOptions->printMap() ) { 0217 printMap( document, text, printDialog->printer() ); 0218 } 0219 0220 if ( printOptions->printLegend() ) { 0221 printLegend( document, text ); 0222 } 0223 0224 if ( printOptions->printRouteSummary() ) { 0225 printRouteSummary( document, text ); 0226 } 0227 0228 if ( printOptions->printDrivingInstructions() ) { 0229 printDrivingInstructions( document, text ); 0230 } 0231 0232 if ( printOptions->printDrivingInstructionsAdvice() ) { 0233 printDrivingInstructionsAdvice( document, text ); 0234 } 0235 0236 text += QLatin1String("</body></html>"); 0237 document.setHtml( text ); 0238 document.print( printDialog->printer() ); 0239 0240 if ( hideBackground ) { 0241 m_marbleWidget->setShowBackground( wasBackgroundVisible ); 0242 m_marbleWidget->setPalette( originalPalette ); 0243 m_marbleWidget->update(); 0244 } 0245 } 0246 #endif 0247 } 0248 0249 bool ControlView::openGeoUri( const QString& geoUriString ) 0250 { 0251 GeoUriParser uriParser( geoUriString ); 0252 const bool success = uriParser.parse(); 0253 if ( success ) { 0254 if ( uriParser.planet().id() != marbleModel()->planet()->id() ) { 0255 MapThemeManager *manager = mapThemeManager(); 0256 for( const QString& planetName: manager->mapThemeIds()) { 0257 if ( planetName.startsWith(uriParser.planet().id(), Qt::CaseInsensitive)) { 0258 m_marbleWidget->setMapThemeId(planetName); 0259 break; 0260 } 0261 } 0262 } 0263 m_marbleWidget->centerOn( uriParser.coordinates() ); 0264 if ( uriParser.coordinates().altitude() > 0.0 ) 0265 { 0266 m_marbleWidget->setDistance( uriParser.coordinates().altitude() * METER2KM ); 0267 } 0268 } 0269 return success; 0270 } 0271 0272 QActionGroup *ControlView::createViewSizeActionGroup( QObject* parent ) 0273 { 0274 QActionGroup* actionGroup = new QActionGroup( parent ); 0275 0276 QAction *defaultAction = new QAction( tr( "Default (Resizable)" ), parent ); 0277 defaultAction->setCheckable( true ); 0278 defaultAction->setChecked( true ); 0279 actionGroup->addAction(defaultAction); 0280 0281 QAction *separator = new QAction( parent ); 0282 separator->setSeparator( true ); 0283 actionGroup->addAction(separator); 0284 0285 addViewSizeAction( actionGroup, tr("NTSC (%1x%2)"), 720, 486 ); 0286 addViewSizeAction( actionGroup, tr("PAL (%1x%2)"), 720, 576 ); 0287 addViewSizeAction( actionGroup, tr("NTSC 16:9 (%1x%2)"), 864, 486 ); 0288 addViewSizeAction( actionGroup, tr("PAL 16:9 (%1x%2)"), 1024, 576 ); 0289 // xgettext:no-c-format 0290 addViewSizeAction( actionGroup, tr("DVD (%1x%2p)"), 852, 480 ); 0291 // xgettext:no-c-format 0292 addViewSizeAction( actionGroup, tr("HD (%1x%2p)"), 1280, 720 ); 0293 // xgettext:no-c-format 0294 addViewSizeAction( actionGroup, tr("Full HD (%1x%2p)"), 1920, 1080 ); 0295 addViewSizeAction( actionGroup, tr("Digital Cinema (%1x%2)"), 2048, 1536 ); 0296 /** FIXME: Needs testing, worked with errors. 0297 addViewSizeAction(actionGroup, "4K UHD (%1x%2)", 3840, 2160); 0298 addViewSizeAction(actionGroup, "4K (%1x%2)", 4096, 3072); 0299 */ 0300 0301 return actionGroup; 0302 } 0303 0304 void ControlView::printPixmap( QPrinter * printer, const QPixmap& pixmap ) 0305 { 0306 #ifndef QT_NO_PRINTER 0307 QSize printSize = pixmap.size(); 0308 QRect mapPageRect = printer->pageRect(); 0309 printSize.scale( printer->pageRect().size(), Qt::KeepAspectRatio ); 0310 QPoint printTopLeft( ( mapPageRect.width() - printSize.width() ) / 2 , 0311 ( mapPageRect.height() - printSize.height() ) / 2 ); 0312 QRect mapPrintRect( printTopLeft, printSize ); 0313 0314 QPainter painter; 0315 if (!painter.begin(printer)) 0316 return; 0317 painter.drawPixmap( mapPrintRect, pixmap, pixmap.rect() ); 0318 painter.end(); 0319 #endif 0320 } 0321 0322 // QPointer is used because of issues described in https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0 0323 void ControlView::printPreview() 0324 { 0325 #ifndef QT_NO_PRINTER 0326 QPrinter printer( QPrinter::HighResolution ); 0327 0328 QPointer<QPrintPreviewDialog> preview = new QPrintPreviewDialog( &printer, this ); 0329 preview->setWindowFlags ( Qt::Window ); 0330 preview->resize(640, 480); 0331 connect( preview, SIGNAL(paintRequested(QPrinter*)), SLOT(paintPrintPreview(QPrinter*)) ); 0332 preview->exec(); 0333 delete preview; 0334 #endif 0335 } 0336 0337 void ControlView::paintPrintPreview( QPrinter * printer ) 0338 { 0339 #ifndef QT_NO_PRINTER 0340 QPixmap mapPixmap = mapScreenShot(); 0341 printPixmap( printer, mapPixmap ); 0342 #endif 0343 } 0344 0345 void ControlView::printMap( QTextDocument &document, QString &text, QPrinter *printer ) 0346 { 0347 #ifndef QT_NO_PRINTER 0348 QPixmap image = mapScreenShot(); 0349 0350 if ( m_marbleWidget->viewport()->mapCoversViewport() ) { 0351 // Paint a black frame. Looks better. 0352 QPainter painter(&image); 0353 painter.setPen( Qt::black ); 0354 painter.drawRect( 0, 0, image.width() - 2, image.height() - 2 ); 0355 } 0356 0357 QString uri = "marble://screenshot.png"; 0358 document.addResource( QTextDocument::ImageResource, QUrl( uri ), QVariant( image) ); 0359 QString img = "<img src=\"%1\" width=\"%2\" align=\"center\">"; 0360 int width = qRound( printer->pageRect( QPrinter::Point ).width() ); 0361 text += img.arg( uri ).arg( width ); 0362 #endif 0363 } 0364 0365 void ControlView::printLegend( QTextDocument &document, QString &text ) 0366 { 0367 #ifndef QT_NO_PRINTER 0368 QTextDocument *legend = m_marbleWidget->model()->legend(); 0369 if ( legend ) { 0370 legend->adjustSize(); 0371 QSize size = legend->size().toSize(); 0372 QSize imageSize = size + QSize( 4, 4 ); 0373 QImage image( imageSize, QImage::Format_ARGB32); 0374 QPainter painter( &image ); 0375 painter.setRenderHint( QPainter::Antialiasing, true ); 0376 painter.drawRoundedRect( QRect( QPoint( 0, 0 ), size ), 5, 5 ); 0377 legend->drawContents( &painter ); 0378 document.addResource( QTextDocument::ImageResource, QUrl( "marble://legend.png" ), QVariant(image) ); 0379 QString img = "<p><img src=\"%1\" align=\"center\"></p>"; 0380 text += img.arg( "marble://legend.png" ); 0381 } 0382 #endif 0383 } 0384 0385 void ControlView::printRouteSummary( QTextDocument &document, QString &text) 0386 { 0387 #ifndef QT_NO_PRINTER 0388 RoutingModel* routingModel = m_marbleWidget->model()->routingManager()->routingModel(); 0389 0390 if ( !routingModel ) { 0391 return; 0392 } 0393 0394 RouteRequest* routeRequest = m_marbleWidget->model()->routingManager()->routeRequest(); 0395 if ( routeRequest ) { 0396 QString summary = "<h3>Route to %1: %2 %3</h3>"; 0397 QString destination; 0398 if ( routeRequest->size() ) { 0399 destination = routeRequest->name( routeRequest->size()-1 ); 0400 } 0401 0402 QString label = "<p>%1 %2</p>"; 0403 qreal distance = routingModel->route().distance(); 0404 QString unit = distance > 1000 ? "km" : "m"; 0405 int precision = distance > 1000 ? 1 : 0; 0406 if ( distance > 1000 ) { 0407 distance /= 1000; 0408 } 0409 summary = summary.arg(destination).arg( distance, 0, 'f', precision ).arg( unit ); 0410 text += summary; 0411 0412 text += QLatin1String("<table cellpadding=\"2\">"); 0413 QString pixmapTemplate = "marble://viaPoint-%1.png"; 0414 for ( int i=0; i<routeRequest->size(); ++i ) { 0415 text += QLatin1String("<tr><td>"); 0416 QPixmap pixmap = routeRequest->pixmap(i); 0417 QString pixmapResource = pixmapTemplate.arg( i ); 0418 document.addResource(QTextDocument::ImageResource, 0419 QUrl( pixmapResource ), QVariant( pixmap ) ); 0420 QString myimg = "<img src=\"%1\">"; 0421 text += myimg.arg(pixmapResource) + 0422 QLatin1String("</td><td>"); 0423 routeRequest->name(i) + 0424 QLatin1String("</td></tr>"); 0425 } 0426 text += QLatin1String("</table>"); 0427 } 0428 #endif 0429 } 0430 0431 void ControlView::printDrivingInstructions( QTextDocument &document, QString &text ) 0432 { 0433 #ifndef QT_NO_PRINTER 0434 RoutingModel* routingModel = m_marbleWidget->model()->routingManager()->routingModel(); 0435 0436 if (!routingModel) { 0437 return; 0438 } 0439 0440 GeoDataLineString total = routingModel->route().path(); 0441 0442 text += QLatin1String("<table cellpadding=\"4\">" 0443 "<tr><th>No.</th><th>Distance</th><th>Instruction</th></tr>"); 0444 for ( int i=0; i<routingModel->rowCount(); ++i ) { 0445 QModelIndex index = routingModel->index(i, 0); 0446 GeoDataCoordinates coordinates = index.data( RoutingModel::CoordinateRole ).value<GeoDataCoordinates>(); 0447 GeoDataLineString accumulator; 0448 for (int k=0; k<total.size(); ++k) { 0449 accumulator << total.at(k); 0450 0451 if (total.at(k) == coordinates) 0452 break; 0453 } 0454 0455 if ( i%2 == 0 ) { 0456 text += QLatin1String("<tr bgcolor=\"lightGray\"><td align=\"right\" valign=\"middle\">"); 0457 } 0458 else { 0459 text += QLatin1String("<tr><td align=\"right\" valign=\"middle\">"); 0460 } 0461 text += QString::number(i+1) + 0462 QLatin1String("</td><td align=\"right\" valign=\"middle\">"); 0463 0464 qreal planetRadius = marbleModel()->planet()->radius(); 0465 text += QString::number(accumulator.length(planetRadius) * METER2KM, 'f', 1) + 0466 /** @todo: support localization */ 0467 QLatin1String(" km</td><td valign=\"middle\">"); 0468 0469 QPixmap instructionIcon = index.data( Qt::DecorationRole ).value<QPixmap>(); 0470 if ( !instructionIcon.isNull() ) { 0471 QString uri = QString("marble://turnIcon%1.png").arg(i); 0472 document.addResource( QTextDocument::ImageResource, QUrl( uri ), QVariant( instructionIcon ) ); 0473 text += QString("<img src=\"%1\">").arg(uri); 0474 } 0475 0476 text += routingModel->data( index ).toString() + 0477 QLatin1String("</td></tr>"); 0478 } 0479 text += QLatin1String("</table>"); 0480 #endif 0481 } 0482 0483 void ControlView::printDrivingInstructionsAdvice( QTextDocument &, QString &text ) 0484 { 0485 #ifndef QT_NO_PRINTER 0486 text += QLatin1String("<p>") + tr("The Marble development team wishes you a pleasant and safe journey.") + QLatin1String("</p>") + 0487 QLatin1String("<p>") + tr("Caution: Driving instructions may be incomplete or inaccurate.") + 0488 QLatin1Char(' ') + tr("Road construction, weather and other unforeseen variables can result in this suggested route not to be the most expedient or safest route to your destination.") + 0489 QLatin1Char(' ') + tr("Please use common sense while navigating.") + QLatin1String("</p>"); 0490 #endif 0491 } 0492 0493 void ControlView::addViewSizeAction( QActionGroup* actionGroup, const QString &nameTemplate, int width, int height ) 0494 { 0495 QString const name = nameTemplate.arg( width ).arg( height ); 0496 QAction *action = new QAction( name, actionGroup->parent() ); 0497 action->setCheckable( true ); 0498 action->setData( QSize( width, height ) ); 0499 actionGroup->addAction( action ); 0500 } 0501 0502 0503 void ControlView::launchExternalMapEditor() 0504 { 0505 QString editor = m_externalEditor; 0506 if ( editor.isEmpty() ) { 0507 QPointer<ExternalEditorDialog> dialog = new ExternalEditorDialog( this ); 0508 if( dialog->exec() == QDialog::Accepted ) { 0509 editor = dialog->externalEditor(); 0510 if ( dialog->saveDefault() ) { 0511 m_externalEditor = editor; 0512 } 0513 } else { 0514 return; 0515 } 0516 } 0517 0518 if (editor == QLatin1String("josm")) { 0519 // JOSM, the java based editor 0520 synchronizeWithExternalMapEditor( editor, "--download=%1,%4,%3,%2" ); 0521 } 0522 else if (editor == QLatin1String("merkaartor")) { 0523 // Merkaartor, a Qt based editor 0524 QString argument = "osm://download/load_and_zoom?top=%1&right=%2&bottom=%3&left=%4"; 0525 synchronizeWithExternalMapEditor( editor, argument ); 0526 } 0527 else { 0528 // Potlatch, the flash based editor running at the osm main website 0529 QString url = "http://www.openstreetmap.org/edit?lat=%1&lon=%2&zoom=%3"; 0530 qreal lat = m_marbleWidget->centerLatitude(); 0531 qreal lon = m_marbleWidget->centerLongitude(); 0532 int zoom = m_marbleWidget->tileZoomLevel(); 0533 url = url.arg( lat, 0, 'f', 8 ).arg( lon, 0, 'f', 8 ).arg( zoom ); 0534 QDesktopServices::openUrl( QUrl(url) ); 0535 } 0536 } 0537 0538 void ControlView::synchronizeWithExternalMapEditor( const QString &application, const QString &argument ) 0539 { 0540 QTimer watchdog; // terminates network connection after a short timeout 0541 watchdog.setSingleShot( true ); 0542 QEventLoop localEventLoop; 0543 connect( &watchdog, SIGNAL(timeout()), &localEventLoop, SLOT(quit()) ); 0544 QNetworkAccessManager manager; 0545 connect( &manager, SIGNAL(finished(QNetworkReply*)), &localEventLoop, SLOT(quit()) ); 0546 0547 // Wait at most two seconds for the local server to respond 0548 QNetworkReply *reply = manager.get( QNetworkRequest( QUrl( "http://localhost:8111/") ) ); 0549 watchdog.start( 2000 ); 0550 localEventLoop.exec(); 0551 0552 GeoDataLatLonAltBox box = m_marbleWidget->viewport()->viewLatLonAltBox(); 0553 qreal north = box.north( GeoDataCoordinates::Degree ); 0554 qreal east = box.east( GeoDataCoordinates::Degree ); 0555 qreal south = box.south( GeoDataCoordinates::Degree ); 0556 qreal west = box.west( GeoDataCoordinates::Degree ); 0557 0558 if( watchdog.isActive() && reply->bytesAvailable() > 0 ) { 0559 // The local server is alive. Tell it to download the current region 0560 watchdog.stop(); 0561 QString serverUrl = "http://localhost:8111/load_and_zoom?top=%1&right=%2&bottom=%3&left=%4"; 0562 serverUrl = serverUrl.arg( north, 0, 'f', 8 ).arg( east, 0, 'f', 8 ); 0563 serverUrl = serverUrl.arg( south, 0, 'f', 8 ).arg( west, 0, 'f', 8 ); 0564 mDebug() << "Connecting to local server URL " << serverUrl; 0565 manager.get( QNetworkRequest( QUrl( serverUrl ) ) ); 0566 0567 // Give it five seconds to process the request 0568 watchdog.start( 5000 ); 0569 localEventLoop.exec(); 0570 } else { 0571 // The local server is not alive. Start the application 0572 QString applicationArgument = argument.arg( south, 0, 'f', 8 ).arg( east, 0, 'f', 8 ); 0573 applicationArgument = applicationArgument.arg( north, 0, 'f', 8 ).arg( west, 0, 'f', 8 ); 0574 mDebug() << "No local server found. Launching " << application << " with argument " << applicationArgument; 0575 if ( !QProcess::startDetached( application, QStringList() << applicationArgument ) ) { 0576 QString text = tr( "Unable to start the external editor. Check that %1 is installed or choose a different external editor in the settings dialog." ); 0577 text = text.arg( application ); 0578 QMessageBox::warning( this, tr( "Cannot start external editor" ), text ); 0579 } 0580 } 0581 } 0582 0583 void ControlView::setExternalMapEditor( const QString &editor ) 0584 { 0585 m_externalEditor = editor; 0586 } 0587 0588 QList<QAction*> ControlView::setupDockWidgets( QMainWindow *mainWindow ) 0589 { 0590 Q_ASSERT( !m_searchDock && "Please create dock widgets just once" ); 0591 0592 mainWindow->setTabPosition( Qt::LeftDockWidgetArea, QTabWidget::North ); 0593 mainWindow->setTabPosition( Qt::RightDockWidgetArea, QTabWidget::North ); 0594 0595 QDockWidget* legendDock = new QDockWidget( tr( "Legend" ), this ); 0596 legendDock->setObjectName( "legendDock" ); 0597 legendDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea ); 0598 LegendWidget* legendWidget = new LegendWidget( this ); 0599 legendWidget->setMarbleModel( m_marbleWidget->model() ); 0600 connect( legendWidget, SIGNAL(tourLinkClicked(QString)), 0601 this, SLOT(handleTourLinkClicked(QString)) ); 0602 connect( legendWidget, SIGNAL(propertyValueChanged(QString,bool)), 0603 marbleWidget(), SLOT(setPropertyValue(QString,bool)) ); 0604 legendDock->setWidget( legendWidget ); 0605 0606 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; 0607 if ( smallScreen ) { 0608 // Show only the legend as a dock widget on small screen, the others are dialogs 0609 mainWindow->addDockWidget( Qt::LeftDockWidgetArea, legendDock ); 0610 return QList<QAction*>() << legendDock->toggleViewAction(); 0611 } 0612 0613 QDockWidget *routingDock = new QDockWidget( tr( "Routing" ), mainWindow ); 0614 routingDock->setObjectName( "routingDock" ); 0615 routingDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea ); 0616 RoutingWidget* routingWidget = new RoutingWidget( marbleWidget(), mainWindow ); 0617 routingWidget->setRouteSyncManager( cloudSyncManager()->routeSyncManager() ); 0618 routingDock->setWidget( routingWidget ); 0619 mainWindow->addDockWidget( Qt::LeftDockWidgetArea, routingDock ); 0620 0621 QDockWidget *locationDock = new QDockWidget( tr( "Location" ), this ); 0622 locationDock->setObjectName( "locationDock" ); 0623 locationDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea ); 0624 m_locationWidget = new CurrentLocationWidget( this ); 0625 m_locationWidget->setMarbleWidget( marbleWidget() ); 0626 locationDock->setWidget( m_locationWidget ); 0627 mainWindow->addDockWidget( Qt::LeftDockWidgetArea, locationDock ); 0628 0629 m_searchDock = new QDockWidget( tr( "Search" ), this ); 0630 m_searchDock->setObjectName( "searchDock" ); 0631 m_searchDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea ); 0632 SearchWidget* searchWidget = new SearchWidget( this ); 0633 searchWidget->setMarbleWidget( marbleWidget() ); 0634 m_searchDock->setWidget( searchWidget ); 0635 mainWindow->addDockWidget( Qt::LeftDockWidgetArea, m_searchDock ); 0636 0637 mainWindow->tabifyDockWidget( m_searchDock, routingDock ); 0638 mainWindow->tabifyDockWidget( routingDock, locationDock ); 0639 m_searchDock->raise(); 0640 0641 QKeySequence searchSequence( Qt::CTRL | Qt::Key_F ); 0642 searchWidget->setToolTip( tr( "Search for cities, addresses, points of interest and more (%1)" ).arg( searchSequence.toString() ) ); 0643 QShortcut* searchShortcut = new QShortcut( mainWindow ); 0644 connect( searchShortcut, SIGNAL(activated()), this, SLOT(showSearch()) ); 0645 0646 QDockWidget *mapViewDock = new QDockWidget( tr( "Map View" ), this ); 0647 mapViewDock->setObjectName( "mapViewDock" ); 0648 mapViewDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea ); 0649 MapViewWidget* mapViewWidget = new MapViewWidget( this ); 0650 mapViewWidget->setMarbleWidget( marbleWidget(), m_mapThemeManager ); 0651 connect( mapViewWidget, SIGNAL(showMapWizard()), this, SIGNAL(showMapWizard()) ); 0652 connect( mapViewWidget, SIGNAL(mapThemeDeleted()), this, SIGNAL(mapThemeDeleted()) ); 0653 mapViewDock->setWidget( mapViewWidget ); 0654 mainWindow->addDockWidget( Qt::LeftDockWidgetArea, mapViewDock ); 0655 0656 QDockWidget *fileViewDock = new QDockWidget( tr( "Files" ), this ); 0657 fileViewDock->setObjectName( "fileViewDock" ); 0658 fileViewDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea ); 0659 FileViewWidget* fileViewWidget = new FileViewWidget( this ); 0660 fileViewWidget->setMarbleWidget( marbleWidget() ); 0661 fileViewDock->setWidget( fileViewWidget ); 0662 mainWindow->addDockWidget( Qt::LeftDockWidgetArea, fileViewDock ); 0663 fileViewDock->hide(); 0664 0665 QDockWidget *tourDock = new QDockWidget( tr( "Tour" ), this ); 0666 tourDock->setObjectName( "tourDock" ); 0667 tourDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea ); 0668 m_tourWidget = new TourWidget( this ); 0669 m_tourWidget->setMarbleWidget( marbleWidget() ); 0670 tourDock->setWidget( m_tourWidget ); 0671 mainWindow->addDockWidget( Qt::LeftDockWidgetArea, tourDock ); 0672 tourDock->hide(); 0673 0674 mainWindow->addDockWidget( Qt::LeftDockWidgetArea, legendDock ); 0675 mainWindow->tabifyDockWidget( mapViewDock, legendDock ); 0676 mapViewDock->raise(); 0677 0678 m_annotationDock = new QDockWidget( QObject::tr( "Edit Maps" ) ); 0679 m_annotationDock->setObjectName( "annotateDock" ); 0680 m_annotationDock->hide(); 0681 m_annotationDock->toggleViewAction()->setVisible( false ); 0682 0683 QList<RenderPlugin *> renderPluginList = marbleWidget()->renderPlugins(); 0684 QList<RenderPlugin *>::const_iterator i = renderPluginList.constBegin(); 0685 QList<RenderPlugin *>::const_iterator const end = renderPluginList.constEnd(); 0686 0687 for (; i != end; ++i ) { 0688 if ((*i)->nameId() == QLatin1String("annotation")) { 0689 m_annotationPlugin = *i; 0690 QObject::connect(m_annotationPlugin, SIGNAL(enabledChanged(bool)), 0691 this, SLOT(updateAnnotationDockVisibility())); 0692 QObject::connect(m_annotationPlugin, SIGNAL(visibilityChanged(bool,QString)), 0693 this, SLOT(updateAnnotationDockVisibility())); 0694 QObject::connect(m_annotationPlugin, SIGNAL(actionGroupsChanged()), 0695 this, SLOT(updateAnnotationDock())); 0696 updateAnnotationDock(); 0697 updateAnnotationDockVisibility(); 0698 mainWindow->addDockWidget( Qt::LeftDockWidgetArea, m_annotationDock ); 0699 } 0700 } 0701 0702 mainWindow->tabifyDockWidget( tourDock, m_annotationDock ); 0703 mainWindow->tabifyDockWidget( m_annotationDock, fileViewDock ); 0704 0705 QList<QAction*> panelActions; 0706 panelActions << routingDock->toggleViewAction(); 0707 panelActions << locationDock->toggleViewAction(); 0708 panelActions << m_searchDock->toggleViewAction(); 0709 panelActions << mapViewDock->toggleViewAction(); 0710 panelActions << fileViewDock->toggleViewAction(); 0711 panelActions << m_annotationDock->toggleViewAction(); 0712 panelActions << legendDock->toggleViewAction(); 0713 panelActions << tourDock->toggleViewAction(); 0714 0715 // Local list of panel view toggle actions 0716 m_panelActions << routingDock->toggleViewAction(); 0717 m_panelActions << locationDock->toggleViewAction(); 0718 m_panelActions << m_searchDock->toggleViewAction(); 0719 m_panelActions << mapViewDock->toggleViewAction(); 0720 m_panelActions << fileViewDock->toggleViewAction(); 0721 m_panelActions << m_annotationDock->toggleViewAction(); 0722 m_panelActions << legendDock->toggleViewAction(); 0723 m_panelActions << tourDock->toggleViewAction(); 0724 for( QAction* action: m_panelActions ) { 0725 m_panelVisibility << action->isVisible(); 0726 } 0727 0728 // Create Settings->Panels Menu 0729 // Toggle All Panels action 0730 m_togglePanelVisibilityAction = new QAction( tr("Hide &All Panels"), this); 0731 m_togglePanelVisibilityAction->setShortcut( Qt::Key_F9 ); 0732 m_togglePanelVisibilityAction->setStatusTip(tr("Show or hide all panels.")); 0733 connect(m_togglePanelVisibilityAction, SIGNAL(triggered()), this, SLOT(togglePanelVisibility())); 0734 0735 // Include a Separator in the List 0736 QAction *panelSeparatorAct = new QAction( this ); 0737 panelSeparatorAct->setSeparator( true ); 0738 0739 // Return a list of panel view actions for Marble Menu including show/hide all 0740 QList<QAction*> panelMenuActions; 0741 panelMenuActions << m_togglePanelVisibilityAction; 0742 panelMenuActions << panelSeparatorAct; 0743 for( QAction* action: m_panelActions ) { 0744 panelMenuActions << action; 0745 } 0746 0747 return panelMenuActions; 0748 } 0749 0750 CurrentLocationWidget *ControlView::currentLocationWidget() 0751 { 0752 return m_locationWidget; 0753 } 0754 0755 void ControlView::setWorkOffline( bool offline ) 0756 { 0757 marbleWidget()->model()->setWorkOffline( offline ); 0758 if ( !offline ) { 0759 marbleWidget()->clearVolatileTileCache(); 0760 } 0761 } 0762 0763 CloudSyncManager *ControlView::cloudSyncManager() 0764 { 0765 return m_cloudSyncManager; 0766 } 0767 0768 QString ControlView::externalMapEditor() const 0769 { 0770 return m_externalEditor; 0771 } 0772 0773 void ControlView::addGeoDataFile( const QString &filename ) 0774 { 0775 QFileInfo const file( filename ); 0776 if ( file.exists() ) { 0777 m_marbleWidget->model()->addGeoDataFile( file.absoluteFilePath() ); 0778 } else { 0779 qWarning() << "File" << filename << "does not exist, cannot open it."; 0780 } 0781 } 0782 0783 void ControlView::showSearch() 0784 { 0785 if ( !m_searchDock ) { 0786 return; 0787 } 0788 0789 m_searchDock->show(); 0790 m_searchDock->raise(); 0791 m_searchDock->widget()->setFocus(); 0792 } 0793 0794 void ControlView::showConflictDialog( MergeItem *item ) 0795 { 0796 Q_ASSERT( m_conflictDialog ); 0797 m_conflictDialog->setMergeItem( item ); 0798 m_conflictDialog->open(); 0799 } 0800 0801 void ControlView::updateAnnotationDockVisibility() 0802 { 0803 if( m_annotationPlugin != nullptr && m_annotationDock != nullptr ) { 0804 if( m_annotationPlugin->visible() && m_annotationPlugin->enabled() ) { 0805 m_annotationDock->toggleViewAction()->setVisible( true ); 0806 } else { 0807 m_annotationDock->setVisible( false ); 0808 m_annotationDock->toggleViewAction()->setVisible( false ); 0809 } 0810 } 0811 } 0812 0813 void ControlView::updateAnnotationDock() 0814 { 0815 const QList<QActionGroup*> *tmp_actionGroups = m_annotationPlugin->actionGroups(); 0816 QWidget *widget = new QWidget( m_annotationDock ); 0817 QVBoxLayout *layout = new QVBoxLayout; 0818 QToolBar *firstToolbar = new QToolBar( widget ); 0819 QToolBar *secondToolbar = new QToolBar( widget ); 0820 QSpacerItem *spacer = new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding); 0821 if( !tmp_actionGroups->isEmpty() ) { 0822 bool firstToolbarFilled = false; 0823 for( QAction *action: tmp_actionGroups->first()->actions() ) { 0824 if (action->objectName() == QLatin1String("toolbarSeparator")) { 0825 firstToolbarFilled = true; 0826 } else { 0827 if( !firstToolbarFilled ) { 0828 firstToolbar->addAction( action ); 0829 } else { 0830 secondToolbar->addAction( action ); 0831 } 0832 } 0833 } 0834 } 0835 layout->addWidget( firstToolbar ); 0836 layout->addWidget( secondToolbar ); 0837 layout->addSpacerItem( spacer ); 0838 widget->setLayout( layout ); 0839 m_annotationDock->setWidget( widget ); 0840 } 0841 0842 void ControlView::togglePanelVisibility() 0843 { 0844 Q_ASSERT( m_panelVisibility.size() == m_panelActions.size() ); 0845 if ( m_isPanelVisible ) { 0846 for( int p=0; p<m_panelActions.size(); ++p ) { 0847 // Save state of individual dock visibility 0848 m_panelVisibility[p] = m_panelActions.at(p)->isChecked(); 0849 0850 // hide panel if it is showing 0851 if ( m_panelActions.at(p)->isChecked() ) { 0852 m_panelActions.at(p)->activate( QAction::Trigger ); 0853 } 0854 } 0855 0856 // Change Menu Item Text 0857 m_togglePanelVisibilityAction->setText( tr("Show &All Panels") ); 0858 m_isPanelVisible = false; 0859 } else { 0860 for( int p=0; p<m_panelActions.size(); ++p ) { 0861 // show panel if it was showing before all panels were hidden 0862 if ( m_panelVisibility.at(p) && !m_panelActions.at(p)->isChecked() ) { 0863 m_panelActions.at(p)->activate( QAction::Trigger ); 0864 } 0865 } 0866 0867 // Change Menu Item Text 0868 m_togglePanelVisibilityAction->setText( tr("Hide &All Panels") ); 0869 m_isPanelVisible = true; 0870 } 0871 } 0872 0873 void ControlView::handleTourLinkClicked(const QString& path) 0874 { 0875 QString tourPath = MarbleDirs::path( path ); 0876 if ( !tourPath.isEmpty() ) { 0877 openTour( tourPath ); 0878 } 0879 } 0880 0881 void ControlView::openTour( const QString &filename ) 0882 { 0883 if ( m_tourWidget->openTour( filename ) ) { 0884 m_tourWidget->startPlaying(); 0885 } 0886 } 0887 0888 void ControlView::closeEvent( QCloseEvent *event ) 0889 { 0890 QCloseEvent newEvent; 0891 QCoreApplication::sendEvent( m_tourWidget, &newEvent ); 0892 0893 if ( newEvent.isAccepted() ) { 0894 event->accept(); 0895 } else { 0896 event->ignore(); 0897 } 0898 } 0899 0900 void ControlView::dragEnterEvent(QDragEnterEvent *event) 0901 { 0902 bool success = false; 0903 0904 const QMimeData *mimeData = event->mimeData(); 0905 0906 GeoUriParser uriParser; 0907 0908 // prefer urls 0909 if (mimeData->hasUrls()) { 0910 // be generous and take the first usable url 0911 for(const QUrl& url: mimeData->urls()) { 0912 uriParser.setGeoUri(url.url()); 0913 success = uriParser.parse(); 0914 if (success) { 0915 break; 0916 } 0917 } 0918 } 0919 0920 // fall back to own string parsing 0921 if (!success && mimeData->hasText()) { 0922 const QString text = mimeData->text(); 0923 // first try human readable coordinates 0924 GeoDataCoordinates::fromString(text, success); 0925 // next geo uri 0926 if (!success) { 0927 uriParser.setGeoUri(text); 0928 success = uriParser.parse(); 0929 } 0930 } 0931 0932 if (success) { 0933 event->acceptProposedAction(); 0934 } 0935 } 0936 0937 void ControlView::dropEvent(QDropEvent *event) 0938 { 0939 bool success = false; 0940 0941 const QMimeData *mimeData = event->mimeData(); 0942 0943 // prefer urls 0944 if (mimeData->hasUrls()) { 0945 // be generous and take the first usable url 0946 for(const QUrl& url: mimeData->urls()) { 0947 success = openGeoUri(url.url()); 0948 if (success) { 0949 break; 0950 } 0951 } 0952 } 0953 0954 // fall back to own string parsing 0955 if (!success && mimeData->hasText()) { 0956 const QString text = mimeData->text(); 0957 // first try human readable coordinates 0958 const GeoDataCoordinates coordinates = GeoDataCoordinates::fromString(text, success); 0959 if (success) { 0960 const qreal longitude = coordinates.longitude(GeoDataCoordinates::Degree); 0961 const qreal latitude = coordinates.latitude(GeoDataCoordinates::Degree); 0962 m_marbleWidget->centerOn(longitude, latitude); 0963 } else { 0964 success = openGeoUri(text); 0965 } 0966 } 0967 if (success) { 0968 event->acceptProposedAction(); 0969 } 0970 } 0971 0972 } 0973 0974 #include "moc_ControlView.cpp"