ControlView.cpp 33 KB
Newer Older
Torsten Rahn's avatar
Torsten Rahn committed
1
//
2
// This file is part of the Marble Virtual Globe.
Torsten Rahn's avatar
Torsten Rahn committed
3 4 5 6 7
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
8 9
// Copyright 2006-2007 Torsten Rahn <tackat@kde.org>
// Copyright 2007      Inge Wallin  <ingwa@kde.org>
Torsten Rahn's avatar
Torsten Rahn committed
10 11
//

12

13
#include "ControlView.h"
Torsten Rahn's avatar
Torsten Rahn committed
14

15
#include <QCloseEvent>
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#include <QLayout>
#include <QSplitter>
#include <QStringListModel>
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#include <QPrinter>
#include <QPainter>
#include <QTextDocument>
#include <QPointer>
#include <QUrl>
#include <QDesktopServices>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QProcess>
#include <QTimer>
#include <QFileInfo>
#include <QMessageBox>
#include <QMainWindow>
#include <QDockWidget>
#include <QShortcut>
#include <QMenu>
38
#include <QToolBar>
Torsten Rahn's avatar
Torsten Rahn committed
39

40 41 42 43 44
#ifdef MARBLE_DBUS
#include <QDBusConnection>
#include "MarbleDBusInterface.h"
#endif

45 46
#include "GeoSceneDocument.h"
#include "GeoSceneHead.h"
47
#include "GeoUriParser.h"
48
#include "MarbleWidget.h"
49
#include "MarbleDebug.h"
50
#include "MarbleDirs.h"
51
#include "MarbleModel.h"
52
#include "MarbleMap.h"
53
#include "MapThemeManager.h"
54
#include "PrintOptionsWidget.h"
55
#include "ViewportParams.h"
56
#include "ViewParams.h"
57
#include "routing/Route.h"
58 59 60
#include "routing/RoutingManager.h"
#include "routing/RoutingModel.h"
#include "routing/RouteRequest.h"
61
#include "routing/RoutingWidget.h"
62
#include "ExternalEditorDialog.h"
63 64
#include "CurrentLocationWidget.h"
#include "SearchWidget.h"
Mihail Ivchenko's avatar
Mihail Ivchenko committed
65
#include "TourWidget.h"
66 67 68
#include "MapViewWidget.h"
#include "FileViewWidget.h"
#include "LegendWidget.h"
69 70 71
#include "BookmarkManager.h"
#include "cloudsync/CloudSyncManager.h"
#include "cloudsync/BookmarkSyncManager.h"
72
#include "cloudsync/RouteSyncManager.h"
73 74
#include "cloudsync/ConflictDialog.h"
#include "cloudsync/MergeItem.h"
75
#include "RenderPlugin.h"
76

77 78 79
namespace Marble
{

Torsten Rahn's avatar
 
Torsten Rahn committed
80
ControlView::ControlView( QWidget *parent )
81
   : QWidget( parent ),
82
     m_mapThemeManager( new MapThemeManager( this ) ),
83
     m_searchDock( 0 ),
84
     m_locationWidget( 0 ),
85 86
     m_conflictDialog( 0 ),
     m_togglePanelVisibilityAction( 0 ),
87
     m_isPanelVisible( true ),
88 89 90
     m_tourWidget( 0 ),
     m_annotationDock( 0 ),
     m_annotationPlugin( 0 )
Torsten Rahn's avatar
Torsten Rahn committed
91
{
92
    setWindowTitle( tr( "Marble - Virtual Globe" ) );
93

Torsten Rahn's avatar
Torsten Rahn committed
94 95
    resize( 680, 640 );

Inge Wallin's avatar
Inge Wallin committed
96
    m_marbleWidget = new MarbleWidget( this );
Torsten Rahn's avatar
Torsten Rahn committed
97 98
    m_marbleWidget->setSizePolicy( QSizePolicy( QSizePolicy::MinimumExpanding,
                                                QSizePolicy::MinimumExpanding ) );
99 100 101 102 103 104 105 106 107 108 109
#ifdef MARBLE_DBUS
    new MarbleDBusInterface( m_marbleWidget );
    QDBusConnection::sessionBus().registerObject( "/Marble", m_marbleWidget );
    if (!QDBusConnection::sessionBus().registerService( "org.kde.marble" )) {
        QString const urlWithPid = QString("org.kde.marble-%1").arg( QCoreApplication::applicationPid() );
        if ( !QDBusConnection::sessionBus().registerService( urlWithPid ) ) {
            mDebug() << "Failed to register service org.kde.marble and " << urlWithPid << " with the DBus session bus.";
        }
    }
#endif

Torsten Rahn's avatar
Torsten Rahn committed
110

111 112
    QVBoxLayout* layout = new QVBoxLayout;
    layout->addWidget( m_marbleWidget );
113
    layout->setMargin( 0 );
114
    setLayout( layout );
115

Dennis Nienhüser's avatar
Dennis Nienhüser committed
116 117 118 119
    m_cloudSyncManager = new CloudSyncManager( this );
    m_cloudSyncManager->routeSyncManager()->setRoutingManager( m_marbleWidget->model()->routingManager() );
    BookmarkSyncManager* bookmarkSyncManager = m_cloudSyncManager->bookmarkSyncManager();
    bookmarkSyncManager->setBookmarkManager( m_marbleWidget->model()->bookmarkManager() );
120
    m_conflictDialog = new ConflictDialog( m_marbleWidget );
Dennis Nienhüser's avatar
Dennis Nienhüser committed
121 122 123
    connect( bookmarkSyncManager, SIGNAL(mergeConflict(MergeItem*)), this, SLOT(showConflictDialog(MergeItem*)) );
    connect( bookmarkSyncManager, SIGNAL(syncComplete()), m_conflictDialog, SLOT(stopAutoResolve()) );
    connect( m_conflictDialog, SIGNAL(resolveConflict(MergeItem*)), bookmarkSyncManager, SLOT(resolveConflict(MergeItem*)) );
Torsten Rahn's avatar
Torsten Rahn committed
124 125
}

126 127
ControlView::~ControlView()
{
128
    // nothing to do
129
}
Torsten Rahn's avatar
Torsten Rahn committed
130

131 132
QString ControlView::applicationVersion()
{
133
    return "1.11.23 (1.12 development version)";
134 135
}

136 137 138 139 140
MapThemeManager *ControlView::mapThemeManager()
{
    return m_mapThemeManager;
}

141
void ControlView::zoomIn()
Torsten Rahn's avatar
Torsten Rahn committed
142 143 144 145
{
    m_marbleWidget->zoomIn();
}

146
void ControlView::zoomOut()
Torsten Rahn's avatar
Torsten Rahn committed
147 148 149 150
{
    m_marbleWidget->zoomOut();
}

151
void ControlView::moveLeft()
Torsten Rahn's avatar
Torsten Rahn committed
152 153 154 155
{
    m_marbleWidget->moveLeft();
}

156
void ControlView::moveRight()
Torsten Rahn's avatar
Torsten Rahn committed
157 158 159 160
{
    m_marbleWidget->moveRight();
}

161
void ControlView::moveUp()
Torsten Rahn's avatar
Torsten Rahn committed
162 163 164 165
{
    m_marbleWidget->moveUp();
}

166
void ControlView::moveDown()
Torsten Rahn's avatar
Torsten Rahn committed
167 168 169 170
{
    m_marbleWidget->moveDown();
}

171 172
QString ControlView::defaultMapThemeId() const
{
173
    QStringList fallBackThemes;
174 175 176
      fallBackThemes << "earth/srtm/srtm.dgml";
      fallBackThemes << "earth/bluemarble/bluemarble.dgml";
      fallBackThemes << "earth/openstreetmap/openstreetmap.dgml";
177

178
    const QStringList installedThemes = m_mapThemeManager->mapThemeIds();
179 180 181 182

    foreach(const QString &fallback, fallBackThemes) {
        if (installedThemes.contains(fallback)) {
            return fallback;
183 184 185
        }
    }

186 187 188 189 190
    if (installedThemes.size()) {
        return installedThemes.first();
    }

    return QString();
191 192
}

193 194 195
void ControlView::printMapScreenShot( QPointer<QPrintDialog> printDialog)
{
#ifndef QT_NO_PRINTER
196
        PrintOptionsWidget* printOptions = new PrintOptionsWidget( this );
197
        bool const mapCoversViewport = m_marbleWidget->viewport()->mapCoversViewport();
198 199 200 201 202 203
        printOptions->setBackgroundControlsEnabled( !mapCoversViewport );
        bool hasLegend = m_marbleWidget->model()->legend() != 0;
        printOptions->setLegendControlsEnabled( hasLegend );
        bool hasRoute = marbleWidget()->model()->routingManager()->routingModel()->rowCount() > 0;
        printOptions->setPrintRouteSummary( hasRoute );
        printOptions->setPrintDrivingInstructions( hasRoute );
204
        printOptions->setPrintDrivingInstructionsAdvice( hasRoute );
205 206 207 208 209 210 211
        printOptions->setRouteControlsEnabled( hasRoute );
        printDialog->setOptionTabs( QList<QWidget*>() << printOptions );

        if ( printDialog->exec() == QDialog::Accepted ) {
            QTextDocument document;
            QString text = "<html><head><title>Marble Printout</title></head><body>";
            QPalette const originalPalette = m_marbleWidget->palette();
212
            bool const wasBackgroundVisible = m_marbleWidget->showBackground();
213 214 215
            bool const hideBackground = !mapCoversViewport && !printOptions->printBackground();
            if ( hideBackground ) {
                // Temporarily remove the black background and layers painting on it
216
                m_marbleWidget->setShowBackground( false );
217
                m_marbleWidget->setPalette( QPalette ( Qt::white ) );
218
                m_marbleWidget->update();
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
            }

            if ( printOptions->printMap() ) {
                printMap( document, text, printDialog->printer() );
            }

            if ( printOptions->printLegend() ) {
                printLegend( document, text );
            }

            if ( printOptions->printRouteSummary() ) {
                printRouteSummary( document, text );
            }

            if ( printOptions->printDrivingInstructions() ) {
                printDrivingInstructions( document, text );
            }

237 238 239 240
            if ( printOptions->printDrivingInstructionsAdvice() ) {
                printDrivingInstructionsAdvice( document, text );
            }

241 242 243 244 245
            text += "</body></html>";
            document.setHtml( text );
            document.print( printDialog->printer() );

            if ( hideBackground ) {
246
                m_marbleWidget->setShowBackground( wasBackgroundVisible );
247
                m_marbleWidget->setPalette( originalPalette );
248
                m_marbleWidget->update();
249
            }
250 251 252 253
    }
#endif
}

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
void ControlView::openGeoUri( const QString& geoUriString )
{
    GeoUriParser uriParser( geoUriString );
    if ( uriParser.parse() ) {
        if ( uriParser.planet().id() != marbleModel()->planet()->id() ) {
            MapThemeManager *manager = mapThemeManager();
            foreach( const QString& planetName, manager->mapThemeIds()) {
                if ( planetName.startsWith(uriParser.planet().id(), Qt::CaseInsensitive)) {
                    m_marbleWidget->setMapThemeId(planetName);
                    break;
                }
            }
        }
        m_marbleWidget->centerOn( uriParser.coordinates() );
        if ( uriParser.coordinates().altitude() > 0.0 )
        {
            m_marbleWidget->setDistance( uriParser.coordinates().altitude() * METER2KM );
        }
    }
}

Dennis Nienhüser's avatar
Dennis Nienhüser committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
QActionGroup *ControlView::createViewSizeActionGroup( QObject* parent )
{
    QActionGroup* actionGroup = new QActionGroup( parent );

    QAction *defaultAction = new QAction( tr( "Default (Resizable)" ), parent );
    defaultAction->setCheckable( true );
    defaultAction->setChecked( true );
    actionGroup->addAction(defaultAction);

    QAction *separator = new QAction( parent );
    separator->setSeparator( true );
    actionGroup->addAction(separator);

    addViewSizeAction( actionGroup, tr("NTSC (%1x%2)"), 720, 486 );
    addViewSizeAction( actionGroup, tr("PAL (%1x%2)"), 720, 576 );
    addViewSizeAction( actionGroup, tr("NTSC 16:9 (%1x%2)"), 864, 486 );
    addViewSizeAction( actionGroup, tr("PAL 16:9 (%1x%2)"), 1024, 576 );
    addViewSizeAction( actionGroup, tr("DVD (%1x%2p)"), 852, 480 );
    addViewSizeAction( actionGroup, tr("HD (%1x%2p)"), 1280, 720 );
    addViewSizeAction( actionGroup, tr("Full HD (%1x%2p)"), 1920, 1080 );
    addViewSizeAction( actionGroup, tr("Digital Cinema (%1x%2)"), 2048, 1536 );
    /** FIXME: Needs testing, worked with errors.
    addViewSizeAction(actionGroup, "4K UHD (%1x%2)", 3840, 2160);
    addViewSizeAction(actionGroup, "4K (%1x%2)", 4096, 3072);
    */

    return actionGroup;
}

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
void ControlView::printPixmap( QPrinter * printer, const QPixmap& pixmap  )
{
#ifndef QT_NO_PRINTER
    QSize printSize = pixmap.size();
    QRect mapPageRect = printer->pageRect();
    printSize.scale( printer->pageRect().size(), Qt::KeepAspectRatio );
    QPoint printTopLeft( ( mapPageRect.width() - printSize.width() ) / 2 ,
                         ( mapPageRect.height() - printSize.height() ) / 2 );
    QRect mapPrintRect( printTopLeft, printSize );

    QPainter painter;
    if (!painter.begin(printer))
        return;
    painter.drawPixmap( mapPrintRect, pixmap, pixmap.rect() );
    painter.end();
#endif
}

// QPointer is used because of issues described in http://www.kdedevelopers.org/node/3919
void ControlView::printPreview()
{
#ifndef QT_NO_PRINTER
    QPrinter printer( QPrinter::HighResolution );

    QPointer<QPrintPreviewDialog> preview = new QPrintPreviewDialog( &printer, this );
    preview->setWindowFlags ( Qt::Window );
330
    preview->resize(640, 480);
331
    connect( preview, SIGNAL(paintRequested(QPrinter*)), SLOT(paintPrintPreview(QPrinter*)) );
332 333 334 335 336 337 338 339 340 341 342 343 344
    preview->exec();
    delete preview;
#endif
}

void ControlView::paintPrintPreview( QPrinter * printer )
{
#ifndef QT_NO_PRINTER
    QPixmap mapPixmap = mapScreenShot();
    printPixmap( printer, mapPixmap );
#endif
}

345 346 347 348 349
void ControlView::printMap( QTextDocument &document, QString &text, QPrinter *printer )
{
#ifndef QT_NO_PRINTER
    QPixmap image = mapScreenShot();

350
    if ( m_marbleWidget->viewport()->mapCoversViewport() ) {
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
        // Paint a black frame. Looks better.
        QPainter painter(&image);
        painter.setPen( Qt::black );
        painter.drawRect( 0, 0, image.width() - 2, image.height() - 2 );
    }

    QString uri = "marble://screenshot.png";
    document.addResource( QTextDocument::ImageResource, QUrl( uri ), QVariant( image) );
    QString img = "<img src=\"%1\" width=\"%2\" align=\"center\">";
    int width = qRound( printer->pageRect( QPrinter::Point ).width() );
    text += img.arg( uri ).arg( width );
#endif
}

void ControlView::printLegend( QTextDocument &document, QString &text )
{
#ifndef QT_NO_PRINTER
    QTextDocument *legend = m_marbleWidget->model()->legend();
    if ( legend ) {
        legend->adjustSize();
        QSize size = legend->size().toSize();
        QSize imageSize = size + QSize( 4, 4 );
        QImage image( imageSize, QImage::Format_ARGB32);
        QPainter painter( &image );
        painter.setRenderHint( QPainter::Antialiasing, true );
        painter.drawRoundedRect( QRect( QPoint( 0, 0 ), size ), 5, 5 );
        legend->drawContents( &painter );
        document.addResource( QTextDocument::ImageResource, QUrl( "marble://legend.png" ), QVariant(image) );
        QString img = "<p><img src=\"%1\" align=\"center\"></p>";
        text += img.arg( "marble://legend.png" );
    }
#endif
}

void ControlView::printRouteSummary( QTextDocument &document, QString &text)
{
#ifndef QT_NO_PRINTER
    RoutingModel* routingModel = m_marbleWidget->model()->routingManager()->routingModel();

    if ( !routingModel ) {
        return;
    }

    RouteRequest* routeRequest = m_marbleWidget->model()->routingManager()->routeRequest();
    if ( routeRequest ) {
        QString summary = "<h3>Route to %1: %2 %3</h3>";
        QString destination;
        if ( routeRequest->size() ) {
            destination = routeRequest->name( routeRequest->size()-1 );
        }

        QString label = "<p>%1 %2</p>";
403
        qreal distance = routingModel->route().distance();
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
        QString unit = distance > 1000 ? "km" : "m";
        int precision = distance > 1000 ? 1 : 0;
        if ( distance > 1000 ) {
            distance /= 1000;
        }
        summary = summary.arg(destination).arg( distance, 0, 'f', precision ).arg( unit );
        text += summary;

        text += "<table cellpadding=\"2\">";
        QString pixmapTemplate = "marble://viaPoint-%1.png";
        for ( int i=0; i<routeRequest->size(); ++i ) {
            text += "<tr><td>";
            QPixmap pixmap = routeRequest->pixmap(i);
            QString pixmapResource = pixmapTemplate.arg( i );
            document.addResource(QTextDocument::ImageResource,
                                          QUrl( pixmapResource ), QVariant( pixmap ) );
            QString myimg = "<img src=\"%1\">";
            text += myimg.arg( pixmapResource );
            text += "</td><td>";
            text += routeRequest->name( i );
            text += "</td></tr>";
        }
        text += "</table>";
    }
#endif
}

void ControlView::printDrivingInstructions( QTextDocument &document, QString &text )
{
#ifndef QT_NO_PRINTER
    RoutingModel* routingModel = m_marbleWidget->model()->routingManager()->routingModel();

    if (!routingModel) {
        return;
    }

440
    GeoDataLineString total = routingModel->route().path();
441 442 443

    text += "<table cellpadding=\"4\">";
    text += "<tr><th>No.</th><th>Distance</th><th>Instruction</th></tr>";
444
    for ( int i=0; i<routingModel->rowCount(); ++i ) {
445
        QModelIndex index = routingModel->index(i, 0);
446
        GeoDataCoordinates coordinates = index.data( RoutingModel::CoordinateRole ).value<GeoDataCoordinates>();
447 448 449
        GeoDataLineString accumulator;
        for (int k=0; k<total.size(); ++k) {
            accumulator << total.at(k);
450

451 452 453
            if (total.at(k) == coordinates)
                break;
        }
454

455 456
        if ( i%2 == 0 ) {
            text += "<tr bgcolor=\"lightGray\"><td align=\"right\" valign=\"middle\">";
457
        }
458 459 460 461 462 463
        else {
            text += "<tr><td align=\"right\" valign=\"middle\">";
        }
        text += QString::number( i+1 );
        text += "</td><td align=\"right\" valign=\"middle\">";

464 465
        qreal planetRadius = marbleModel()->planet()->radius();
        text += QString::number( accumulator.length( planetRadius ) * METER2KM, 'f', 1 );
466 467 468
        /** @todo: support localization */
        text += " km</td><td valign=\"middle\">";

469
        QPixmap instructionIcon = index.data( Qt::DecorationRole ).value<QPixmap>();
470 471 472 473 474 475 476 477
        if ( !instructionIcon.isNull() ) {
            QString uri = QString("marble://turnIcon%1.png").arg(i);
            document.addResource( QTextDocument::ImageResource, QUrl( uri ), QVariant( instructionIcon ) );
            text += QString("<img src=\"%1\">").arg(uri);
        }

        text += routingModel->data( index ).toString();
        text += "</td></tr>";
478 479 480 481 482
    }
    text += "</table>";
#endif
}

483 484 485 486 487
void ControlView::printDrivingInstructionsAdvice( QTextDocument &, QString &text )
{
#ifndef QT_NO_PRINTER
    text += "<p>" + tr( "The Marble development team wishes you a pleasant and safe journey." ) + "</p>";
    text += "<p>" + tr( "Caution: Driving instructions may be incomplete or inaccurate." );
Kevin Krammer's avatar
Kevin Krammer committed
488 489
    text += ' ' + 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." );
    text += ' ' + tr( "Please use common sense while navigating." ) + "</p>";
490 491 492
#endif
}

Dennis Nienhüser's avatar
Dennis Nienhüser committed
493 494 495 496 497 498 499 500 501
void ControlView::addViewSizeAction( QActionGroup* actionGroup, const QString &nameTemplate, int width, int height )
{
    QString const name = nameTemplate.arg( width ).arg( height );
    QAction *action = new QAction( name, actionGroup->parent() );
    action->setCheckable( true );
    action->setData( QSize( width, height ) );
    actionGroup->addAction( action );
}

502

503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
void ControlView::launchExternalMapEditor()
{
    QString editor = m_externalEditor;
    if ( editor.isEmpty() ) {
        QPointer<ExternalEditorDialog> dialog = new ExternalEditorDialog( this );
        if( dialog->exec() == QDialog::Accepted ) {
            editor = dialog->externalEditor();
            if ( dialog->saveDefault() ) {
                m_externalEditor = editor;
            }
        } else {
            return;
        }
    }

    if ( editor == "josm" )
    {
        // JOSM, the java based editor
        synchronizeWithExternalMapEditor( editor, "--download=%1,%4,%3,%2" );
    }
    else if ( editor == "merkaartor" )
    {
        // Merkaartor, a Qt based editor
        QString argument = "osm://download/load_and_zoom?top=%1&right=%2&bottom=%3&left=%4";
        synchronizeWithExternalMapEditor( editor, argument );
    }
    else {
        // Potlatch, the flash based editor running at the osm main website
        QString url = "http://www.openstreetmap.org/edit?lat=%1&lon=%2&zoom=%3";
        qreal lat = m_marbleWidget->centerLatitude();
        qreal lon = m_marbleWidget->centerLongitude();
534
        int zoom = m_marbleWidget->tileZoomLevel();
535 536 537 538 539 540 541 542 543 544
        url = url.arg( lat, 0, 'f', 8 ).arg( lon, 0, 'f', 8 ).arg( zoom );
        QDesktopServices::openUrl( url );
    }
}

void ControlView::synchronizeWithExternalMapEditor( const QString &application, const QString &argument )
{
    QTimer watchdog; // terminates network connection after a short timeout
    watchdog.setSingleShot( true );
    QEventLoop localEventLoop;
545
    connect( &watchdog, SIGNAL(timeout()), &localEventLoop, SLOT(quit()) );
546
    QNetworkAccessManager manager;
547
    connect( &manager, SIGNAL(finished(QNetworkReply*)), &localEventLoop, SLOT(quit()) );
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573

    // Wait at most two seconds for the local server to respond
    QNetworkReply *reply = manager.get( QNetworkRequest( QUrl( "http://localhost:8111/") ) );
    watchdog.start( 2000 );
    localEventLoop.exec();

    GeoDataLatLonAltBox box = m_marbleWidget->viewport()->viewLatLonAltBox();
    qreal north = box.north( GeoDataCoordinates::Degree );
    qreal east  = box.east( GeoDataCoordinates::Degree );
    qreal south = box.south( GeoDataCoordinates::Degree );
    qreal west  = box.west( GeoDataCoordinates::Degree );

    if( watchdog.isActive() && reply->bytesAvailable() > 0 ) {
        // The local server is alive. Tell it to download the current region
        watchdog.stop();
        QString serverUrl = "http://localhost:8111/load_and_zoom?top=%1&right=%2&bottom=%3&left=%4";
        serverUrl = serverUrl.arg( north, 0, 'f', 8 ).arg( east, 0, 'f', 8 );
        serverUrl = serverUrl.arg( south, 0, 'f', 8 ).arg( west, 0, 'f', 8 );
        mDebug() << "Connecting to local server URL " << serverUrl;
        manager.get( QNetworkRequest( QUrl( serverUrl ) ) );

        // Give it five seconds to process the request
        watchdog.start( 5000 );
        localEventLoop.exec();
    } else {
        // The local server is not alive. Start the application
Dennis Nienhüser's avatar
Dennis Nienhüser committed
574 575
        QString applicationArgument = argument.arg( south, 0, 'f', 8 ).arg( east, 0, 'f', 8 );
        applicationArgument = applicationArgument.arg( north, 0, 'f', 8 ).arg( west, 0, 'f', 8 );
576 577 578 579 580 581 582 583 584 585 586 587 588 589
        mDebug() << "No local server found. Launching " << application << " with argument " << applicationArgument;
        if ( !QProcess::startDetached( application, QStringList() << applicationArgument ) ) {
            QString text = tr( "Unable to start the external editor. Check that %1 is installed or choose a different external editor in the settings dialog." );
            text = text.arg( application );
            QMessageBox::warning( this, tr( "Cannot start external editor" ), text );
        }
    }
}

void ControlView::setExternalMapEditor( const QString &editor )
{
    m_externalEditor = editor;
}

590 591 592 593 594 595 596
QList<QAction*> ControlView::setupDockWidgets( QMainWindow *mainWindow )
{
    Q_ASSERT( !m_searchDock && "Please create dock widgets just once" );

    mainWindow->setTabPosition( Qt::LeftDockWidgetArea, QTabWidget::North );
    mainWindow->setTabPosition( Qt::RightDockWidgetArea, QTabWidget::North );

597 598 599 600
    QDockWidget* legendDock = new QDockWidget( tr( "Legend" ), this );
    legendDock->setObjectName( "legendDock" );
    legendDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
    LegendWidget* legendWidget = new LegendWidget( this );
601 602 603
    legendWidget->setMarbleModel( m_marbleWidget->model() );
    connect( legendWidget, SIGNAL(tourLinkClicked(QString)),
             this, SLOT(handleTourLinkClicked(QString)) );
604 605 606 607 608 609 610 611 612 613 614
    connect( legendWidget, SIGNAL(propertyValueChanged(QString,bool)),
             marbleWidget(), SLOT(setPropertyValue(QString,bool)) );
    legendDock->setWidget( legendWidget );

    bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
    if ( smallScreen ) {
        // Show only the legend as a dock widget on small screen, the others are dialogs
        mainWindow->addDockWidget( Qt::LeftDockWidgetArea, legendDock );
        return QList<QAction*>() << legendDock->toggleViewAction();
    }

615 616 617 618
    QDockWidget *routingDock = new QDockWidget( tr( "Routing" ), mainWindow );
    routingDock->setObjectName( "routingDock" );
    routingDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
    RoutingWidget* routingWidget = new RoutingWidget( marbleWidget(), mainWindow );
Dennis Nienhüser's avatar
Dennis Nienhüser committed
619
    routingWidget->setRouteSyncManager( cloudSyncManager()->routeSyncManager() );
620 621 622 623 624 625
    routingDock->setWidget( routingWidget );
    mainWindow->addDockWidget( Qt::LeftDockWidgetArea, routingDock );

    QDockWidget *locationDock = new QDockWidget( tr( "Location" ), this );
    locationDock->setObjectName( "locationDock" );
    locationDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
626 627 628
    m_locationWidget = new CurrentLocationWidget( this );
    m_locationWidget->setMarbleWidget( marbleWidget() );
    locationDock->setWidget( m_locationWidget );
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
    mainWindow->addDockWidget( Qt::LeftDockWidgetArea, locationDock );

    m_searchDock = new QDockWidget( tr( "Search" ), this );
    m_searchDock->setObjectName( "searchDock" );
    m_searchDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
    SearchWidget* searchWidget = new SearchWidget( this );
    searchWidget->setMarbleWidget( marbleWidget() );
    m_searchDock->setWidget( searchWidget );
    mainWindow->addDockWidget( Qt::LeftDockWidgetArea, m_searchDock );

    mainWindow->tabifyDockWidget( m_searchDock, routingDock );
    mainWindow->tabifyDockWidget( routingDock, locationDock );
    m_searchDock->raise();

    QKeySequence searchSequence( Qt::CTRL + Qt::Key_F );
    searchWidget->setToolTip( tr( "Search for cities, addresses, points of interest and more (%1)" ).arg( searchSequence.toString() ) );
    QShortcut* searchShortcut = new QShortcut( mainWindow );
646
    connect( searchShortcut, SIGNAL(activated()), this, SLOT(showSearch()) );
647 648 649 650 651

    QDockWidget *mapViewDock = new QDockWidget( tr( "Map View" ), this );
    mapViewDock->setObjectName( "mapViewDock" );
    mapViewDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
    MapViewWidget* mapViewWidget = new MapViewWidget( this );
652
    mapViewWidget->setMarbleWidget( marbleWidget(), m_mapThemeManager );
653 654
    connect( mapViewWidget, SIGNAL(showMapWizard()), this, SIGNAL(showMapWizard()) );
    connect( mapViewWidget, SIGNAL(showUploadDialog()), this, SIGNAL(showUploadDialog()) );
655
    connect( mapViewWidget, SIGNAL(mapThemeDeleted()), this, SIGNAL(mapThemeDeleted()) );
656 657 658 659 660 661 662 663 664 665 666 667
    mapViewDock->setWidget( mapViewWidget );
    mainWindow->addDockWidget( Qt::LeftDockWidgetArea, mapViewDock );

    QDockWidget *fileViewDock = new QDockWidget( tr( "Files" ), this );
    fileViewDock->setObjectName( "fileViewDock" );
    fileViewDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
    FileViewWidget* fileViewWidget = new FileViewWidget( this );
    fileViewWidget->setMarbleWidget( marbleWidget() );
    fileViewDock->setWidget( fileViewWidget );
    mainWindow->addDockWidget( Qt::LeftDockWidgetArea, fileViewDock );
    fileViewDock->hide();

Mihail Ivchenko's avatar
Mihail Ivchenko committed
668 669 670
    QDockWidget *tourDock = new QDockWidget( tr( "Tour" ), this );
    tourDock->setObjectName( "tourDock" );
    tourDock->setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea );
671 672 673
    m_tourWidget = new TourWidget( this );
    m_tourWidget->setMarbleWidget( marbleWidget() );
    tourDock->setWidget( m_tourWidget );
Mihail Ivchenko's avatar
Mihail Ivchenko committed
674 675 676
    mainWindow->addDockWidget( Qt::LeftDockWidgetArea, tourDock );
    tourDock->hide();

677 678 679 680
    mainWindow->addDockWidget( Qt::LeftDockWidgetArea, legendDock );
    mainWindow->tabifyDockWidget( mapViewDock, legendDock );
    mapViewDock->raise();

681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
    m_annotationDock = new QDockWidget( QObject::tr( "Edit Maps" ) );
    m_annotationDock->setObjectName( "annotateDock" );
    m_annotationDock->hide();
    m_annotationDock->toggleViewAction()->setVisible( false );

    QList<RenderPlugin *> renderPluginList = marbleWidget()->renderPlugins();
    QList<RenderPlugin *>::const_iterator i = renderPluginList.constBegin();
    QList<RenderPlugin *>::const_iterator const end = renderPluginList.constEnd();

    for (; i != end; ++i ) {
        if( (*i)->nameId() == "annotation" ) {
            m_annotationPlugin = *i;
            QObject::connect(m_annotationPlugin, SIGNAL(enabledChanged(bool)),
                             this, SLOT(updateAnnotationDockVisibility()));
            QObject::connect(m_annotationPlugin, SIGNAL(visibilityChanged(bool,QString)),
                             this, SLOT(updateAnnotationDockVisibility()));
            QObject::connect(m_annotationPlugin, SIGNAL(actionGroupsChanged()),
                             this, SLOT(updateAnnotationDock()));
            updateAnnotationDock();
            updateAnnotationDockVisibility();
            mainWindow->addDockWidget( Qt::LeftDockWidgetArea, m_annotationDock );
        }
    }

Torsten Rahn's avatar
Torsten Rahn committed
705 706 707
    mainWindow->tabifyDockWidget( tourDock, m_annotationDock );
    mainWindow->tabifyDockWidget( m_annotationDock, fileViewDock );

708 709 710 711 712 713
    QList<QAction*> panelActions;
    panelActions << routingDock->toggleViewAction();
    panelActions << locationDock->toggleViewAction();
    panelActions << m_searchDock->toggleViewAction();
    panelActions << mapViewDock->toggleViewAction();
    panelActions << fileViewDock->toggleViewAction();
714
    panelActions << m_annotationDock->toggleViewAction();
715
    panelActions << legendDock->toggleViewAction();
Mihail Ivchenko's avatar
Mihail Ivchenko committed
716
    panelActions << tourDock->toggleViewAction();
717 718 719 720 721 722 723

    // Local list of panel view toggle actions
    m_panelActions << routingDock->toggleViewAction();
    m_panelActions << locationDock->toggleViewAction();
    m_panelActions << m_searchDock->toggleViewAction();
    m_panelActions << mapViewDock->toggleViewAction();
    m_panelActions << fileViewDock->toggleViewAction();
724
    m_panelActions << m_annotationDock->toggleViewAction();
725 726 727 728 729 730 731 732 733
    m_panelActions << legendDock->toggleViewAction();
    m_panelActions << tourDock->toggleViewAction();
    foreach( QAction* action, m_panelActions ) {
        m_panelVisibility << action->isVisible();
    }

    // Create Settings->Panels Menu
    // Toggle All Panels action
    m_togglePanelVisibilityAction = new QAction( tr("Hide &All Panels"), this);
Burkhard Lück's avatar
Burkhard Lück committed
734
    m_togglePanelVisibilityAction->setShortcut( Qt::Key_F9 );
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
    m_togglePanelVisibilityAction->setStatusTip(tr("Show or hide all panels."));
    connect(m_togglePanelVisibilityAction, SIGNAL(triggered()), this, SLOT(togglePanelVisibility()));

    // Include a Separator in the List
    QAction *panelSeparatorAct = new QAction( this );
    panelSeparatorAct->setSeparator( true );

    // Return a list of panel view actions for Marble Menu including show/hide all
    QList<QAction*> panelMenuActions;
    panelMenuActions << m_togglePanelVisibilityAction;
    panelMenuActions << panelSeparatorAct;
    foreach( QAction* action, m_panelActions ) {
        panelMenuActions << action;
    }

    return panelMenuActions;
751 752
}

753
CurrentLocationWidget *ControlView::currentLocationWidget()
Dennis Nienhüser's avatar
Dennis Nienhüser committed
754
{
755
    return m_locationWidget;
Dennis Nienhüser's avatar
Dennis Nienhüser committed
756 757
}

758
void ControlView::setWorkOffline( bool offline )
759
{
760 761 762 763
    marbleWidget()->model()->setWorkOffline( offline );
    if ( !offline ) {
        marbleWidget()->clearVolatileTileCache();
    }
764 765
}

Dennis Nienhüser's avatar
Dennis Nienhüser committed
766 767 768 769 770
CloudSyncManager *ControlView::cloudSyncManager()
{
    return m_cloudSyncManager;
}

771
QString ControlView::externalMapEditor() const
772
{
773
    return m_externalEditor;
774 775
}

776 777
void ControlView::addGeoDataFile( QString filename )
{
778 779 780 781 782 783
    QFileInfo const file( filename );
    if ( file.exists() ) {
        m_marbleWidget->model()->addGeoDataFile( file.absoluteFilePath() );
    } else {
        qWarning() << "File" << filename << "does not exist, cannot open it.";
    }
784 785
}

786 787 788 789 790 791 792 793 794 795 796
void ControlView::showSearch()
{
    if ( !m_searchDock ) {
        return;
    }

    m_searchDock->show();
    m_searchDock->raise();
    m_searchDock->widget()->setFocus();
}

797 798
void ControlView::showConflictDialog( MergeItem *item )
{
799
    Q_ASSERT( m_conflictDialog );
800 801 802 803
    m_conflictDialog->setMergeItem( item );
    m_conflictDialog->open();
}

804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
void ControlView::updateAnnotationDockVisibility()
{
    if( m_annotationPlugin != 0 && m_annotationDock != 0 ) {
        if( m_annotationPlugin->visible() && m_annotationPlugin->enabled() ) {
            m_annotationDock->toggleViewAction()->setVisible( true );
        } else {
            m_annotationDock->setVisible( false );
            m_annotationDock->toggleViewAction()->setVisible( false );
        }
    }
}

void ControlView::updateAnnotationDock()
{
    const QList<QActionGroup*> *tmp_actionGroups = m_annotationPlugin->actionGroups();
    QWidget *widget = new QWidget( m_annotationDock );
    QVBoxLayout *layout = new QVBoxLayout;
    QToolBar *firstToolbar = new QToolBar( widget );
    QToolBar *secondToolbar = new QToolBar( widget );
    QSpacerItem *spacer = new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding);
    if( !tmp_actionGroups->isEmpty() ) {
825
        bool firstToolbarFilled = false;
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
        foreach( QAction *action, tmp_actionGroups->first()->actions() ) {
            if( action->objectName() == "toolbarSeparator" ) {
                firstToolbarFilled = true;
            } else {
                if( !firstToolbarFilled ) {
                    firstToolbar->addAction( action );
                } else {
                    secondToolbar->addAction( action );
                }
            }
        }
    }
    layout->addWidget( firstToolbar );
    layout->addWidget( secondToolbar );
    layout->addSpacerItem( spacer );
    widget->setLayout( layout );
    m_annotationDock->setWidget( widget );
}

845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
void ControlView::togglePanelVisibility()
{
    Q_ASSERT( m_panelVisibility.size() == m_panelActions.size() );
    if ( m_isPanelVisible ) {
        for( int p=0; p<m_panelActions.size(); ++p ) {
            // Save state of individual dock visibility
            m_panelVisibility[p] = m_panelActions.at(p)->isChecked();

            // hide panel if it is showing
            if ( m_panelActions.at(p)->isChecked() ) {
                m_panelActions.at(p)->activate( QAction::Trigger );
            }
        }

        // Change Menu Item Text
        m_togglePanelVisibilityAction->setText( tr("Show &All Panels") );
        m_isPanelVisible = false;
    } else {
        for( int p=0; p<m_panelActions.size(); ++p ) {
            // show panel if it was showing before all panels were hidden
            if ( m_panelVisibility.at(p) && !m_panelActions.at(p)->isChecked() ) {
                m_panelActions.at(p)->activate( QAction::Trigger );
            }
        }

        // Change Menu Item Text
        m_togglePanelVisibilityAction->setText( tr("Hide &All Panels") );
        m_isPanelVisible = true;
    }
}

876 877 878 879
void ControlView::handleTourLinkClicked(const QString& path)
{
    QString tourPath = MarbleDirs::path( path );
    if ( !tourPath.isEmpty() ) {
880 881 882 883 884 885 886 887
        openTour( tourPath );
    }
}

void ControlView::openTour( const QString &filename )
{
    if ( m_tourWidget->openTour( filename ) ) {
        m_tourWidget->startPlaying();
888 889 890
    }
}

891 892 893 894 895 896 897 898 899 900 901 902
void ControlView::closeEvent( QCloseEvent *event )
{
    QCloseEvent newEvent;
    QCoreApplication::sendEvent( m_tourWidget, &newEvent );

    if ( newEvent.isAccepted() ) {
        event->accept();
    } else {
        event->ignore();
    }
}

903 904
}

905
#include "ControlView.moc"