MainWindow.cc 23.5 KB
Newer Older
1 2 3 4
/*=====================================================================

QGroundControl Open Source Ground Control Station

5
(c) 2009 - 2013 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Implementation of class MainWindow
 *   @author Lorenz Meier <mail@qgroundcontrol.org>
 */

#include <QSettings>
#include <QNetworkInterface>
#include <QDebug>
#include <QTimer>
#include <QHostInfo>
#include <QSplashScreen>
36
#include <QQuickView>
37
#include <QDesktopWidget>
38 39
#include <QScreen>
#include <QDesktopServices>
40
#include <QDockWidget>
41
#include <QMenuBar>
42

43 44 45 46 47
#include "QGC.h"
#include "MAVLinkProtocol.h"
#include "MainWindow.h"
#include "GAudioOutput.h"
#include "QGCMAVLinkLogPlayer.h"
Don Gagne's avatar
Don Gagne committed
48
#include "SettingsDialog.h"
49
#include "MAVLinkDecoder.h"
Don Gagne's avatar
Don Gagne committed
50
#include "QGCApplication.h"
51
#include "QGCFileDialog.h"
Don Gagne's avatar
Don Gagne committed
52
#include "QGCMessageBox.h"
53
#include "MultiVehicleManager.h"
54
#include "HomePositionManager.h"
55
#include "LogCompressor.h"
56
#include "UAS.h"
57 58

#ifndef __mobile__
59 60
#include "QGCDataPlot2D.h"
#include "Linecharts.h"
61 62 63 64 65 66 67 68
#include "QGCUASFileViewMulti.h"
#include "UASQuickView.h"
#include "QGCTabbedInfoView.h"
#include "UASRawStatusView.h"
#include "CustomCommandWidget.h"
#include "QGCDockWidget.h"
#include "FlightDisplayWidget.h"
#include "UASInfoWidget.h"
69 70 71 72 73 74
#include "HILDockWidget.h"
#endif

#ifndef __ios__
#include "SerialLink.h"
#endif
75

76 77 78 79
#ifdef UNITTEST_BUILD
#include "QmlControls/QmlTestWidget.h"
#endif

80 81 82 83 84
#ifdef QGC_OSG_ENABLED
#include "Q3DWidgetFactory.h"
#endif


85 86 87
/// The key under which the Main Window settings are saved
const char* MAIN_SETTINGS_GROUP = "QGC_MAINWINDOW";

88
#ifndef __mobile__
89 90 91 92 93 94 95
const char* MainWindow::_mavlinkDockWidgetName =            "MAVLink Inspector";
const char* MainWindow::_customCommandWidgetName =          "Custom Command";
const char* MainWindow::_filesDockWidgetName =              "Onboard Files";
const char* MainWindow::_uasStatusDetailsDockWidgetName =   "Status Details";
const char* MainWindow::_pfdDockWidgetName =                "Primary Flight Display";
const char* MainWindow::_uasInfoViewDockWidgetName =        "Info View";
const char* MainWindow::_hilDockWidgetName =                "HIL Config";
Don Gagne's avatar
Don Gagne committed
96
const char* MainWindow::_analyzeDockWidgetName =            "Analyze";
97 98

const char* MainWindow::_visibleWidgetsKey =                "VisibleWidgets";
99
#endif
100

Don Gagne's avatar
Don Gagne committed
101 102
static MainWindow* _instance = NULL;   ///< @brief MainWindow singleton

103
MainWindow* MainWindow::_create(QSplashScreen* splashScreen)
104
{
Don Gagne's avatar
Don Gagne committed
105
    Q_ASSERT(_instance == NULL);
106
    new MainWindow(splashScreen);
Don Gagne's avatar
Don Gagne committed
107 108
    // _instance is set in constructor
    Q_ASSERT(_instance);
109 110 111
    return _instance;
}

Don Gagne's avatar
Don Gagne committed
112
MainWindow* MainWindow::instance(void)
113
{
Don Gagne's avatar
Don Gagne committed
114
    return _instance;
115 116
}

117 118
void MainWindow::deleteInstance(void)
{
Don Gagne's avatar
Don Gagne committed
119
    delete this;
120 121
}

Don Gagne's avatar
Don Gagne committed
122 123 124
/// @brief Private constructor for MainWindow. MainWindow singleton is only ever created
///         by MainWindow::_create method. Hence no other code should have access to
///         constructor.
dogmaphobic's avatar
dogmaphobic committed
125 126 127
MainWindow::MainWindow(QSplashScreen* splashScreen)
    : _autoReconnect(false)
    , _lowPowerMode(false)
128
    , _showStatusBar(false)
dogmaphobic's avatar
dogmaphobic committed
129
    , _splashScreen(splashScreen)
130
    , _mainQmlWidgetHolder(NULL)
131
{
Don Gagne's avatar
Don Gagne committed
132 133
    Q_ASSERT(_instance == NULL);
    _instance = this;
134

135 136 137
    if (splashScreen) {
        connect(this, &MainWindow::initStatusChanged, splashScreen, &QSplashScreen::showMessage);
    }
138

139 140 141 142
    // Qt 4/5 on Ubuntu does place the native menubar correctly so on Linux we revert back to in-window menu bar.
#ifdef Q_OS_LINUX
    menuBar()->setNativeMenuBar(false);
#endif
dogmaphobic's avatar
dogmaphobic committed
143
    // Setup user interface
144
    loadSettings();
145
    emit initStatusChanged(tr("Setting up user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
146

dogmaphobic's avatar
dogmaphobic committed
147 148
    _ui.setupUi(this);
    // Make sure tool bar elements all fit before changing minimum width
dogmaphobic's avatar
dogmaphobic committed
149
    setMinimumWidth(1008);
dogmaphobic's avatar
dogmaphobic committed
150
    configureWindowName();
151

152 153
    // Setup central widget with a layout to hold the views
    _centralLayout = new QVBoxLayout();
154
    _centralLayout->setContentsMargins(0, 0, 0, 0);
155
    centralWidget()->setLayout(_centralLayout);
156 157 158 159 160 161 162 163

    _mainQmlWidgetHolder = new QGCQmlWidgetHolder(QString(), NULL, this);
    _centralLayout->addWidget(_mainQmlWidgetHolder);
    _mainQmlWidgetHolder->setVisible(true);

    _mainQmlWidgetHolder->setContextPropertyObject("controller", this);
    _mainQmlWidgetHolder->setSource(QUrl::fromUserInput("qrc:qml/MainWindow.qml"));

164
    // Set dock options
165
    setDockOptions(0);
166
    // Setup corners
167
    setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
168

dogmaphobic's avatar
dogmaphobic committed
169 170 171
    // On Mobile devices, we don't want any main menus at all.
#ifdef __mobile__
    menuBar()->setNativeMenuBar(false);
dogmaphobic's avatar
dogmaphobic committed
172
#endif
dogmaphobic's avatar
dogmaphobic committed
173

174 175 176
#ifdef UNITTEST_BUILD
    QAction* qmlTestAction = new QAction("Test QML palette and controls", NULL);
    connect(qmlTestAction, &QAction::triggered, this, &MainWindow::_showQmlTestWidget);
177
    _ui.menuWidgets->addAction(qmlTestAction);
178
#endif
179

dogmaphobic's avatar
dogmaphobic committed
180
    // Status Bar
181
    setStatusBar(new QStatusBar(this));
182
    statusBar()->setSizeGripEnabled(true);
183

184
#ifndef __mobile__
185
    emit initStatusChanged(tr("Building common widgets."), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
186
    _buildCommonWidgets();
187
    emit initStatusChanged(tr("Building common actions"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
188 189
#endif
    
190 191 192
    // Create actions
    connectCommonActions();
    // Connect user interface devices
193
#ifdef QGC_MOUSE_ENABLED_WIN
194
    emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
195 196
    mouseInput = new Mouse3DInput(this);
    mouse = new Mouse6dofInput(mouseInput);
197
#endif //QGC_MOUSE_ENABLED_WIN
198

199
#if QGC_MOUSE_ENABLED_LINUX
200
    emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
201 202

    mouse = new Mouse6dofInput(this);
203
    connect(this, SIGNAL(x11EventOccured(XEvent*)), mouse, SLOT(handleX11Event(XEvent*)));
204
#endif //QGC_MOUSE_ENABLED_LINUX
205

206 207 208 209
    // These also cause the screen to redraw so we need to update any OpenGL canvases in QML controls
    connect(LinkManager::instance(), &LinkManager::linkConnected,    this, &MainWindow::_linkStateChange);
    connect(LinkManager::instance(), &LinkManager::linkDisconnected, this, &MainWindow::_linkStateChange);

210
    // Connect link
dogmaphobic's avatar
dogmaphobic committed
211
    if (_autoReconnect)
212
    {
213
        restoreLastUsedConnection();
214 215 216
    }

    // Set low power mode
dogmaphobic's avatar
dogmaphobic committed
217
    enableLowPowerMode(_lowPowerMode);
218
    emit initStatusChanged(tr("Restoring last view state"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
219

dogmaphobic's avatar
dogmaphobic committed
220
#ifndef __mobile__
221

222
    // Restore the window position and size
dogmaphobic's avatar
dogmaphobic committed
223 224
    emit initStatusChanged(tr("Restoring last window size"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    if (settings.contains(_getWindowGeometryKey()))
225
    {
dogmaphobic's avatar
dogmaphobic committed
226
        restoreGeometry(settings.value(_getWindowGeometryKey()).toByteArray());
227 228 229 230
    }
    else
    {
        // Adjust the size
231 232 233
        QScreen* scr = QApplication::primaryScreen();
        QSize scrSize = scr->availableSize();
        if (scrSize.width() <= 1280)
234
        {
235
            resize(scrSize.width(), scrSize.height());
236 237 238
        }
        else
        {
239 240 241
            int w = scrSize.width()  > 1600 ? 1600 : scrSize.width();
            int h = scrSize.height() >  800 ?  800 : scrSize.height();
            resize(w, h);
242
            move((scrSize.width() - w) / 2, (scrSize.height() - h) / 2);
243 244 245
        }
    }

246 247 248
    // Make sure the proper fullscreen/normal menu item is checked properly.
    if (isFullScreen())
    {
dogmaphobic's avatar
dogmaphobic committed
249 250
        _ui.actionFullscreen->setChecked(true);
        _ui.actionNormal->setChecked(false);
251 252 253
    }
    else
    {
dogmaphobic's avatar
dogmaphobic committed
254 255
        _ui.actionFullscreen->setChecked(false);
        _ui.actionNormal->setChecked(true);
256 257 258
    }

    // And that they will stay checked properly after user input
259 260
    connect(_ui.actionFullscreen, &QAction::triggered, this, &MainWindow::fullScreenActionItemCallback);
    connect(_ui.actionNormal,     &QAction::triggered, this, &MainWindow::normalActionItemCallback);
261 262
#endif

263
    connect(_ui.actionStatusBar,  &QAction::triggered, this, &MainWindow::showStatusBarCallback);
264

265 266
    // Set OS dependent keyboard shortcuts for the main window, non OS dependent shortcuts are set in MainWindow.ui
#ifdef Q_OS_MACX
dogmaphobic's avatar
dogmaphobic committed
267
    _ui.actionSetup->setShortcut(QApplication::translate("MainWindow", "Meta+1", 0));
268 269
    _ui.actionPlan->setShortcut(QApplication::translate("MainWindow", "Meta+2", 0));
    _ui.actionFlight->setShortcut(QApplication::translate("MainWindow", "Meta+3", 0));
dogmaphobic's avatar
dogmaphobic committed
270
    _ui.actionFullscreen->setShortcut(QApplication::translate("MainWindow", "Meta+Return", 0));
271
#else
dogmaphobic's avatar
dogmaphobic committed
272
    _ui.actionSetup->setShortcut(QApplication::translate("MainWindow", "Ctrl+1", 0));
Gus Grubba's avatar
Gus Grubba committed
273
    _ui.actionPlan->setShortcut(QApplication::translate("MainWindow", "Ctrl+2", 0));
274
    _ui.actionFlight->setShortcut(QApplication::translate("MainWindow", "Ctrl+3", 0));
dogmaphobic's avatar
dogmaphobic committed
275
    _ui.actionFullscreen->setShortcut(QApplication::translate("MainWindow", "Ctrl+Return", 0));
276 277
#endif

278 279
    _ui.actionFlight->setChecked(true);

280 281
    connect(&windowNameUpdateTimer, SIGNAL(timeout()), this, SLOT(configureWindowName()));
    windowNameUpdateTimer.start(15000);
282
    emit initStatusChanged(tr("Done"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
283 284

    if (!qgcApp()->runningUnitTests()) {
285 286
        _ui.actionStatusBar->setChecked(_showStatusBar);
        showStatusBarCallback(_showStatusBar);
dogmaphobic's avatar
dogmaphobic committed
287
#ifdef __mobile__
288 289
        menuBar()->hide();
#endif
290
        show();
dogmaphobic's avatar
dogmaphobic committed
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
#ifdef Q_OS_MAC
        // TODO HACK
        // This is a really ugly hack. For whatever reason, by having a QQuickWidget inside a
        // QDockWidget (MainToolBar above), the main menu is not shown when the app first
        // starts. I looked everywhere and I could not find a solution. What I did notice was
        // that if any other window gets focus, the menu comes up when you come back to QGC.
        // That is, if you were to click on another window and then back to QGC, the menus
        // would appear. This hack below creates a 0x0 dialog and immediately closes it.
        // That works around the issue and it will do until I find the root of the problem.
        QDialog qd(this);
        qd.show();
        qd.raise();
        qd.activateWindow();
        qd.close();
#endif
306
    }
307 308 309 310
    
#ifndef __mobile__
    _loadVisibleWidgetsSettings();
#endif
311 312 313 314
}

MainWindow::~MainWindow()
{
Don Gagne's avatar
Don Gagne committed
315
    _instance = NULL;
316 317
}

dogmaphobic's avatar
dogmaphobic committed
318
QString MainWindow::_getWindowGeometryKey()
319 320 321 322
{
    return "_geometry";
}

323
#ifndef __mobile__
324
void MainWindow::_buildCommonWidgets(void)
325 326
{
    // Add generic MAVLink decoder
dogmaphobic's avatar
dogmaphobic committed
327
    // TODO: This is never deleted
328
    mavlinkDecoder = new MAVLinkDecoder(MAVLinkProtocol::instance(), this);
John Tapsell's avatar
John Tapsell committed
329 330
    connect(mavlinkDecoder, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)),
                      this, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)));
331

332
    // Log player
dogmaphobic's avatar
dogmaphobic committed
333
    // TODO: Make this optional with a preferences setting or under a "View" menu
Don Gagne's avatar
Don Gagne committed
334
    logPlayer = new QGCMAVLinkLogPlayer(statusBar());
335
    statusBar()->addPermanentWidget(logPlayer);
336

337 338 339 340 341 342 343 344
    static const char* rgDockWidgetNames[] = {
        _mavlinkDockWidgetName,
        _customCommandWidgetName,
        _filesDockWidgetName,
        _uasStatusDetailsDockWidgetName,
        _pfdDockWidgetName,
        _uasInfoViewDockWidgetName,
        _hilDockWidgetName,
Don Gagne's avatar
Don Gagne committed
345
        _analyzeDockWidgetName,
346
    };
347
    static const size_t cDockWidgetNames = sizeof(rgDockWidgetNames) / sizeof(rgDockWidgetNames[0]);
348

349 350 351 352 353 354 355 356 357 358 359
    for (size_t i=0; i<cDockWidgetNames; i++) {
        const char* pDockWidgetName = rgDockWidgetNames[i];
        
        // Add to menu
        QAction* action = new QAction(pDockWidgetName, NULL);
        action->setCheckable(true);
        action->setData(pDockWidgetName);
        connect(action, &QAction::triggered, this, &MainWindow::_showDockWidgetAction);
        _ui.menuWidgets->addAction(action);
        
        _mapName2Action[pDockWidgetName] = action;
360
    }
361
}
362

363 364
/// Shows or hides the specified dock widget, creating if necessary
void MainWindow::_showDockWidget(const QString& name, bool show)
365
{
366
    // Create the inner widget if we need to
367
    if (!_mapName2DockWidget.contains(name)) {
368
        _createInnerDockWidget(name);
369
    }
370
    
371
    Q_ASSERT(_mapName2DockWidget.contains(name));
372
    QGCDockWidget* dockWidget = _mapName2DockWidget[name];
373
    Q_ASSERT(dockWidget);
374
    
375
    dockWidget->setVisible(show);
376
    
377 378
    Q_ASSERT(_mapName2Action.contains(name));
    _mapName2Action[name]->setChecked(show);
379 380 381 382 383
}

/// Creates the specified inner dock widget and adds to the QDockWidget
void MainWindow::_createInnerDockWidget(const QString& widgetName)
{
384
    QGCDockWidget* widget = NULL;
385
    
386
    if (widgetName == _mavlinkDockWidgetName) {
387
        widget = new QGCMAVLinkInspector(widgetName, _mapName2Action[widgetName], MAVLinkProtocol::instance(),this);
Don Gagne's avatar
Don Gagne committed
388
    } else if (widgetName == _customCommandWidgetName) {
389
        widget = new CustomCommandWidget(widgetName, _mapName2Action[widgetName], this);
390
    } else if (widgetName == _filesDockWidgetName) {
391
        widget = new QGCUASFileViewMulti(widgetName, _mapName2Action[widgetName], this);
392
    } else if (widgetName == _uasStatusDetailsDockWidgetName) {
393
        widget = new UASInfoWidget(widgetName, _mapName2Action[widgetName], this);
394
    } else if (widgetName == _pfdDockWidgetName) {
395
        widget = new FlightDisplayWidget(widgetName, _mapName2Action[widgetName], this);
396
    } else if (widgetName == _hilDockWidgetName) {
397
        widget = new HILDockWidget(widgetName, _mapName2Action[widgetName], this);
Don Gagne's avatar
Don Gagne committed
398 399
    } else if (widgetName == _analyzeDockWidgetName) {
        widget = new Linecharts(widgetName, _mapName2Action[widgetName], mavlinkDecoder, this);
400
    } else if (widgetName == _uasInfoViewDockWidgetName) {
401
        QGCTabbedInfoView* pInfoView = new QGCTabbedInfoView(widgetName, _mapName2Action[widgetName], this);
402 403
        pInfoView->addSource(mavlinkDecoder);
        widget = pInfoView;
404 405
    } else {
        qWarning() << "Attempt to create unknown Inner Dock Widget" << widgetName;
406
    }
407
    
408
    _mapName2DockWidget[widgetName] = widget;
409
}
410

411 412
void MainWindow::_hideAllDockWidgets(void)
{
413
    foreach(QGCDockWidget* dockWidget, _mapName2DockWidget) {
414 415 416 417 418 419 420 421
        dockWidget->setVisible(false);
    }
}

void MainWindow::_showDockWidgetAction(bool show)
{
    QAction* action = dynamic_cast<QAction*>(QObject::sender());
    Q_ASSERT(action);
422
    _showDockWidget(action->text(), show);
423 424 425
}
#endif

426
void MainWindow::fullScreenActionItemCallback(bool)
427
{
dogmaphobic's avatar
dogmaphobic committed
428
    _ui.actionNormal->setChecked(false);
429 430
}

431
void MainWindow::normalActionItemCallback(bool)
432
{
dogmaphobic's avatar
dogmaphobic committed
433
    _ui.actionFullscreen->setChecked(false);
434 435
}

436 437 438 439 440 441
void MainWindow::showStatusBarCallback(bool checked)
{
    _showStatusBar = checked;
    checked ? statusBar()->show() : statusBar()->hide();
}

442 443
void MainWindow::closeEvent(QCloseEvent *event)
{
444
    // Disallow window close if there are active connections
445
    if (LinkManager::instance()->anyConnectedLinks()) {
dogmaphobic's avatar
dogmaphobic committed
446 447 448 449 450 451
        QGCMessageBox::StandardButton button =
            QGCMessageBox::warning(
                tr("QGroundControl close"),
                tr("There are still active connections to vehicles. Do you want to disconnect these before closing?"),
                QMessageBox::Yes | QMessageBox::Cancel,
                QMessageBox::Cancel);
452 453 454 455 456 457 458 459 460 461
		if (button == QMessageBox::Yes) {
			LinkManager::instance()->disconnectAll();
			// The above disconnect causes a flurry of activity as the vehicle components are removed. This in turn
			// causes the Windows Version of Qt to crash if you allow the close event to be accepted. In order to prevent
			// the crash, we ignore the close event and setup a delayed timer to close the window after things settle down.
			QTimer::singleShot(1500, this, &MainWindow::_closeWindow);
		}

        event->ignore();
        return;
462 463
    }

464 465
    // This will process any remaining flight log save dialogs
    qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
466
    
467
    // Should not be any active connections
468
    Q_ASSERT(!LinkManager::instance()->anyConnectedLinks());
469 470 471 472 473 474 475 476

    // We have to pull out the QmlWidget from the main window and delete it here, before
    // the MainWindow ends up getting deleted. Otherwise the Qml has a reference to MainWindow
    // inside it which in turn causes a shutdown crash.
    _centralLayout->removeWidget(_mainQmlWidgetHolder);
    delete _mainQmlWidgetHolder;
    _mainQmlWidgetHolder = NULL;

477
    _storeCurrentViewState();
478
    storeSettings();
479
    event->accept();
480 481 482 483
}

void MainWindow::loadSettings()
{
484
    // Why the screaming?
485
    QSettings settings;
486
    settings.beginGroup(MAIN_SETTINGS_GROUP);
487 488 489
    _autoReconnect  = settings.value("AUTO_RECONNECT",      _autoReconnect).toBool();
    _lowPowerMode   = settings.value("LOW_POWER_MODE",      _lowPowerMode).toBool();
    _showStatusBar  = settings.value("SHOW_STATUSBAR",      _showStatusBar).toBool();
490 491 492 493 494 495
    settings.endGroup();
}

void MainWindow::storeSettings()
{
    QSettings settings;
496
    settings.beginGroup(MAIN_SETTINGS_GROUP);
497 498 499
    settings.setValue("AUTO_RECONNECT",     _autoReconnect);
    settings.setValue("LOW_POWER_MODE",     _lowPowerMode);
    settings.setValue("SHOW_STATUSBAR",     _showStatusBar);
500
    settings.endGroup();
dogmaphobic's avatar
dogmaphobic committed
501
    settings.setValue(_getWindowGeometryKey(), saveGeometry());
502 503 504 505
    
#ifndef __mobile__
    _storeVisibleWidgetsSettings();
#endif
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
}

void MainWindow::configureWindowName()
{
    QList<QHostAddress> hostAddresses = QNetworkInterface::allAddresses();
    QString windowname = qApp->applicationName() + " " + qApp->applicationVersion();
    bool prevAddr = false;
    windowname.append(" (" + QHostInfo::localHostName() + ": ");
    for (int i = 0; i < hostAddresses.size(); i++)
    {
        // Exclude loopback IPv4 and all IPv6 addresses
        if (hostAddresses.at(i) != QHostAddress("127.0.0.1") && !hostAddresses.at(i).toString().contains(":"))
        {
            if(prevAddr) windowname.append("/");
            windowname.append(hostAddresses.at(i).toString());
            prevAddr = true;
        }
    }
    windowname.append(")");
    setWindowTitle(windowname);
}

void MainWindow::enableAutoReconnect(bool enabled)
{
dogmaphobic's avatar
dogmaphobic committed
530
    _autoReconnect = enabled;
531 532 533 534 535 536 537 538 539
}

/**
* @brief Create all actions associated to the main window
*
**/
void MainWindow::connectCommonActions()
{
    // Connect actions from ui
dogmaphobic's avatar
dogmaphobic committed
540
    connect(_ui.actionAdd_Link, SIGNAL(triggered()), this, SLOT(manageLinks()));
541 542

    // Audio output
dogmaphobic's avatar
dogmaphobic committed
543 544 545
    _ui.actionMuteAudioOutput->setChecked(GAudioOutput::instance()->isMuted());
    connect(GAudioOutput::instance(), SIGNAL(mutedChanged(bool)), _ui.actionMuteAudioOutput, SLOT(setChecked(bool)));
    connect(_ui.actionMuteAudioOutput, SIGNAL(triggered(bool)), GAudioOutput::instance(), SLOT(mute(bool)));
546 547

    // Application Settings
dogmaphobic's avatar
dogmaphobic committed
548
    connect(_ui.actionSettings, SIGNAL(triggered()), this, SLOT(showSettings()));
549

550 551 552 553 554 555 556
    // Views actions
    connect(_ui.actionFlight,   &QAction::triggered,    this, &MainWindow::showFlyView);
    connect(_ui.actionPlan,     &QAction::triggered,    this, &MainWindow::showPlanView);
    connect(_ui.actionSetup,    &QAction::triggered,    this, &MainWindow::showSetupView);

    // Connect internal actions
    connect(MultiVehicleManager::instance(), &MultiVehicleManager::vehicleAdded, this, &MainWindow::_vehicleAdded);
557 558
}

Don Gagne's avatar
Don Gagne committed
559
void MainWindow::_openUrl(const QString& url, const QString& errorMessage)
560
{
Don Gagne's avatar
Don Gagne committed
561
    if(!QDesktopServices::openUrl(QUrl(url))) {
dogmaphobic's avatar
dogmaphobic committed
562 563 564 565
        QMessageBox::critical(
            this,
            tr("Could not open information in browser"),
            errorMessage);
566 567 568 569 570
    }
}

void MainWindow::showSettings()
{
dogmaphobic's avatar
dogmaphobic committed
571
    SettingsDialog settings(this);
Don Gagne's avatar
Don Gagne committed
572
    settings.exec();
573 574
}

575
void MainWindow::_vehicleAdded(Vehicle* vehicle)
576
{
577
    connect(vehicle->uas(), SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)), this, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)));
578 579
}

580 581
/// Stores the state of the toolbar, status bar and widgets associated with the current view
void MainWindow::_storeCurrentViewState(void)
582
{
Don Gagne's avatar
Don Gagne committed
583
#ifndef __mobile__
584 585
    foreach(QGCDockWidget* dockWidget, _mapName2DockWidget) {
        dockWidget->saveSettings();
586
    }
Don Gagne's avatar
Don Gagne committed
587
#endif
588
    
dogmaphobic's avatar
dogmaphobic committed
589
    settings.setValue(_getWindowGeometryKey(), saveGeometry());
590 591
}

Don Gagne's avatar
Don Gagne committed
592
/// @brief Hides the spash screen if it is currently being shown
593
void MainWindow::hideSplashScreen(void)
Don Gagne's avatar
Don Gagne committed
594 595 596 597 598 599 600
{
    if (_splashScreen) {
        _splashScreen->hide();
        _splashScreen = NULL;
    }
}

601 602
void MainWindow::manageLinks()
{
dogmaphobic's avatar
dogmaphobic committed
603
    SettingsDialog settings(this, SettingsDialog::ShowCommLinks);
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
    settings.exec();
}

/// @brief Saves the last used connection
void MainWindow::saveLastUsedConnection(const QString connection)
{
    QSettings settings;
    QString key(MAIN_SETTINGS_GROUP);
    key += "/LAST_CONNECTION";
    settings.setValue(key, connection);
}

/// @brief Restore (and connects) the last used connection (if any)
void MainWindow::restoreLastUsedConnection()
{
    // TODO This should check and see of the port/whatever is present
    // first. That is, if the last connection was to a PX4 on some serial
    // port, it should check and see if the port is present before making
    // the connection.
    QSettings settings;
    QString key(MAIN_SETTINGS_GROUP);
    key += "/LAST_CONNECTION";
    if(settings.contains(key)) {
627
        QString connection = settings.value(key).toString();
628
        // Create a link for it
629
        LinkManager::instance()->createConnectedLink(connection);
630 631
    }
}
Don Gagne's avatar
Don Gagne committed
632

633 634 635 636 637
void MainWindow::_linkStateChange(LinkInterface*)
{
    emit repaintCanvas();
}

638
#ifdef QGC_MOUSE_ENABLED_LINUX
639 640 641
bool MainWindow::x11Event(XEvent *event)
{
    emit x11EventOccured(event);
642
    return false;
643
}
644
#endif // QGC_MOUSE_ENABLED_LINUX
645 646 647 648 649 650 651

#ifdef UNITTEST_BUILD
void MainWindow::_showQmlTestWidget(void)
{
    new QmlTestWidget();
}
#endif
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690

#ifndef __mobile__
void MainWindow::_loadVisibleWidgetsSettings(void)
{
    QSettings settings;
    
    QString widgets = settings.value(_visibleWidgetsKey).toString();
    
    if (!widgets.isEmpty()) {
        QStringList nameList = widgets.split(",");
        
        foreach (QString name, nameList) {
            _showDockWidget(name, true);
        }
    }
}

void MainWindow::_storeVisibleWidgetsSettings(void)
{
    QString widgetNames;
    bool firstWidget = true;
    
    foreach (QString name, _mapName2DockWidget.keys()) {
        if (_mapName2DockWidget[name]->isVisible()) {
            if (!firstWidget) {
                widgetNames += ",";
            } else {
                firstWidget = false;
            }
            
            widgetNames += name;
        }
    }
    
    QSettings settings;
    
    settings.setValue(_visibleWidgetsKey, widgetNames);
}
#endif