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

QGroundControl Open Source Ground Control Station

5
(c) 2009 - 2015 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

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>
35
#include <QQuickView>
36
#include <QDesktopWidget>
37 38
#include <QScreen>
#include <QDesktopServices>
39
#include <QDockWidget>
40
#include <QMenuBar>
41

42 43 44 45
#include "QGC.h"
#include "MAVLinkProtocol.h"
#include "MainWindow.h"
#include "GAudioOutput.h"
dogmaphobic's avatar
dogmaphobic committed
46
#ifndef __mobile__
47
#include "QGCMAVLinkLogPlayer.h"
dogmaphobic's avatar
dogmaphobic committed
48
#endif
49
#include "MAVLinkDecoder.h"
Don Gagne's avatar
Don Gagne committed
50
#include "QGCApplication.h"
51
#include "MultiVehicleManager.h"
52
#include "HomePositionManager.h"
53
#include "LogCompressor.h"
54
#include "UAS.h"
dogmaphobic's avatar
dogmaphobic committed
55
#include "QGCImageProvider.h"
56 57

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

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

74 75 76 77
#ifdef UNITTEST_BUILD
#include "QmlControls/QmlTestWidget.h"
#endif

78 79 80
/// The key under which the Main Window settings are saved
const char* MAIN_SETTINGS_GROUP = "QGC_MAINWINDOW";

81
#ifndef __mobile__
82 83 84
enum DockWidgetTypes {
    MAVLINK_INSPECTOR,
    CUSTOM_COMMAND,
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    ONBOARD_FILES,
    STATUS_DETAILS,
    INFO_VIEW,
    HIL_CONFIG,
    ANALYZE
};

static const char *rgDockWidgetNames[] = {
    "MAVLink Inspector",
    "Custom Command",
    "Onboard Files",
    "Status Details",
    "Info View",
    "HIL Config",
    "Analyze"
};

#define ARRAY_SIZE(ARRAY) (sizeof(ARRAY) / sizeof(ARRAY[0]))
103 104

static const char* _visibleWidgetsKey = "VisibleWidgets";
105
#endif
106

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

Lorenz Meier's avatar
Lorenz Meier committed
109
MainWindow* MainWindow::_create()
110
{
Don Gagne's avatar
Don Gagne committed
111
    Q_ASSERT(_instance == NULL);
Lorenz Meier's avatar
Lorenz Meier committed
112
    new MainWindow();
Don Gagne's avatar
Don Gagne committed
113 114
    // _instance is set in constructor
    Q_ASSERT(_instance);
115 116 117
    return _instance;
}

Don Gagne's avatar
Don Gagne committed
118
MainWindow* MainWindow::instance(void)
119
{
Don Gagne's avatar
Don Gagne committed
120
    return _instance;
121 122
}

123 124
void MainWindow::deleteInstance(void)
{
Don Gagne's avatar
Don Gagne committed
125
    delete this;
126 127
}

Don Gagne's avatar
Don Gagne committed
128 129 130
/// @brief Private constructor for MainWindow. MainWindow singleton is only ever created
///         by MainWindow::_create method. Hence no other code should have access to
///         constructor.
Lorenz Meier's avatar
Lorenz Meier committed
131
MainWindow::MainWindow()
132
    : _lowPowerMode(false)
133
    , _showStatusBar(false)
134
    , _mainQmlWidgetHolder(NULL)
135
{
Don Gagne's avatar
Don Gagne committed
136 137
    Q_ASSERT(_instance == NULL);
    _instance = this;
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

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

    _mainQmlWidgetHolder->setContextPropertyObject("controller", this);
Don Gagne's avatar
Don Gagne committed
162
    _mainQmlWidgetHolder->setSource(QUrl::fromUserInput("qrc:qml/MainWindowHybrid.qml"));
163

dogmaphobic's avatar
dogmaphobic committed
164 165 166 167
    // Image provider
    QQuickImageProvider* pImgProvider = dynamic_cast<QQuickImageProvider*>(qgcApp()->toolbox()->imageProvider());
    _mainQmlWidgetHolder->getEngine()->addImageProvider(QLatin1String("QGCImages"), pImgProvider);

168
    // Set dock options
169
    setDockOptions(0);
170
    // Setup corners
171
    setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
172

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

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

dogmaphobic's avatar
dogmaphobic committed
184
    // Status Bar
185
    setStatusBar(new QStatusBar(this));
186
    statusBar()->setSizeGripEnabled(true);
187

188
#ifndef __mobile__
189
    emit initStatusChanged(tr("Building common widgets."), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
190
    _buildCommonWidgets();
191
    emit initStatusChanged(tr("Building common actions"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
192
#endif
193

194 195 196
    // Create actions
    connectCommonActions();
    // Connect user interface devices
197
#ifdef QGC_MOUSE_ENABLED_WIN
198
    emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
199 200
    mouseInput = new Mouse3DInput(this);
    mouse = new Mouse6dofInput(mouseInput);
201
#endif //QGC_MOUSE_ENABLED_WIN
202

203
#if QGC_MOUSE_ENABLED_LINUX
204
    emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
205 206

    mouse = new Mouse6dofInput(this);
207
    connect(this, SIGNAL(x11EventOccured(XEvent*)), mouse, SLOT(handleX11Event(XEvent*)));
208
#endif //QGC_MOUSE_ENABLED_LINUX
209

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

dogmaphobic's avatar
dogmaphobic committed
214
#ifndef __mobile__
215

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

241
    connect(_ui.actionStatusBar,  &QAction::triggered, this, &MainWindow::showStatusBarCallback);
242

243
    // Set OS dependent keyboard shortcuts for the main window, non OS dependent shortcuts are set in MainWindow.ui
dogmaphobic's avatar
dogmaphobic committed
244
#ifdef __macos__
dogmaphobic's avatar
dogmaphobic committed
245
    _ui.actionSetup->setShortcut(QApplication::translate("MainWindow", "Meta+1", 0));
246 247
    _ui.actionPlan->setShortcut(QApplication::translate("MainWindow", "Meta+2", 0));
    _ui.actionFlight->setShortcut(QApplication::translate("MainWindow", "Meta+3", 0));
248
#else
dogmaphobic's avatar
dogmaphobic committed
249
    _ui.actionSetup->setShortcut(QApplication::translate("MainWindow", "Ctrl+1", 0));
Gus Grubba's avatar
Gus Grubba committed
250
    _ui.actionPlan->setShortcut(QApplication::translate("MainWindow", "Ctrl+2", 0));
251
    _ui.actionFlight->setShortcut(QApplication::translate("MainWindow", "Ctrl+3", 0));
252 253
#endif

254 255
    _ui.actionFlight->setChecked(true);

256 257
    connect(&windowNameUpdateTimer, SIGNAL(timeout()), this, SLOT(configureWindowName()));
    windowNameUpdateTimer.start(15000);
258
    emit initStatusChanged(tr("Done"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
259 260

    if (!qgcApp()->runningUnitTests()) {
261 262
        _ui.actionStatusBar->setChecked(_showStatusBar);
        showStatusBarCallback(_showStatusBar);
dogmaphobic's avatar
dogmaphobic committed
263
#ifdef __mobile__
264 265
        menuBar()->hide();
#endif
266
        show();
dogmaphobic's avatar
dogmaphobic committed
267
#ifdef __macos__
dogmaphobic's avatar
dogmaphobic committed
268 269 270 271 272 273 274 275 276 277 278 279 280 281
        // 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
282
    }
283

284 285 286
#ifndef __mobile__
    _loadVisibleWidgetsSettings();
#endif
287 288 289 290
}

MainWindow::~MainWindow()
{
Don Gagne's avatar
Don Gagne committed
291
    _instance = NULL;
292 293
}

dogmaphobic's avatar
dogmaphobic committed
294
QString MainWindow::_getWindowGeometryKey()
295 296 297 298
{
    return "_geometry";
}

299
#ifndef __mobile__
300
void MainWindow::_buildCommonWidgets(void)
301 302
{
    // Add generic MAVLink decoder
dogmaphobic's avatar
dogmaphobic committed
303
    // TODO: This is never deleted
304
    mavlinkDecoder = new MAVLinkDecoder(qgcApp()->toolbox()->mavlinkProtocol(), this);
John Tapsell's avatar
John Tapsell committed
305 306
    connect(mavlinkDecoder, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)),
                      this, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)));
307

308
    // Log player
dogmaphobic's avatar
dogmaphobic committed
309
    // TODO: Make this optional with a preferences setting or under a "View" menu
Don Gagne's avatar
Don Gagne committed
310
    logPlayer = new QGCMAVLinkLogPlayer(statusBar());
311
    statusBar()->addPermanentWidget(logPlayer);
312

313 314
    for (int i = 0, end = ARRAY_SIZE(rgDockWidgetNames); i < end; i++) {

315
        const char* pDockWidgetName = rgDockWidgetNames[i];
316

317
        // Add to menu
318
        QAction* action = new QAction(tr(pDockWidgetName), NULL);
319
        action->setCheckable(true);
320
        action->setData(i);
321 322 323
        connect(action, &QAction::triggered, this, &MainWindow::_showDockWidgetAction);
        _ui.menuWidgets->addAction(action);
        _mapName2Action[pDockWidgetName] = action;
324
    }
325
}
326

327 328
/// Shows or hides the specified dock widget, creating if necessary
void MainWindow::_showDockWidget(const QString& name, bool show)
329
{
330
    // Create the inner widget if we need to
331
    if (!_mapName2DockWidget.contains(name)) {
dogmaphobic's avatar
dogmaphobic committed
332 333 334 335
        if(!_createInnerDockWidget(name)) {
            qWarning() << "Trying to load non existing widget:" << name;
            return;
        }
336
    }
337
    Q_ASSERT(_mapName2DockWidget.contains(name));
338
    QGCDockWidget* dockWidget = _mapName2DockWidget[name];
339 340
    Q_ASSERT(dockWidget);
    dockWidget->setVisible(show);
341 342
    Q_ASSERT(_mapName2Action.contains(name));
    _mapName2Action[name]->setChecked(show);
343 344 345
}

/// Creates the specified inner dock widget and adds to the QDockWidget
dogmaphobic's avatar
dogmaphobic committed
346
bool MainWindow::_createInnerDockWidget(const QString& widgetName)
347
{
348
    QGCDockWidget* widget = NULL;
349
    QAction *action = _mapName2Action[widgetName];
dogmaphobic's avatar
dogmaphobic committed
350 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
    if(action) {
        switch(action->data().toInt()) {
            case MAVLINK_INSPECTOR:
                widget = new QGCMAVLinkInspector(widgetName, action, qgcApp()->toolbox()->mavlinkProtocol(),this);
                break;
            case CUSTOM_COMMAND:
                widget = new CustomCommandWidget(widgetName, action, this);
                break;
            case ONBOARD_FILES:
                widget = new QGCUASFileViewMulti(widgetName, action, this);
                break;
            case STATUS_DETAILS:
                widget = new UASInfoWidget(widgetName, action, this);
                break;
            case HIL_CONFIG:
                widget = new HILDockWidget(widgetName, action, this);
                break;
            case ANALYZE:
                widget = new Linecharts(widgetName, action, mavlinkDecoder, this);
                break;
            case INFO_VIEW:
                widget= new QGCTabbedInfoView(widgetName, action, this);
                break;
        }
        if(action->data().toInt() == INFO_VIEW) {
            qobject_cast<QGCTabbedInfoView*>(widget)->addSource(mavlinkDecoder);
        }
        if(widget) {
            _mapName2DockWidget[widgetName] = widget;
        }
380
    }
dogmaphobic's avatar
dogmaphobic committed
381
    return widget != NULL;
382
}
383

384 385
void MainWindow::_hideAllDockWidgets(void)
{
386
    foreach(QGCDockWidget* dockWidget, _mapName2DockWidget) {
387 388 389 390 391 392
        dockWidget->setVisible(false);
    }
}

void MainWindow::_showDockWidgetAction(bool show)
{
393
    QAction* action = qobject_cast<QAction*>(QObject::sender());
394
    Q_ASSERT(action);
395
    _showDockWidget(rgDockWidgetNames[action->data().toInt()], show);
396 397 398
}
#endif

399 400 401 402 403 404
void MainWindow::showStatusBarCallback(bool checked)
{
    _showStatusBar = checked;
    checked ? statusBar()->show() : statusBar()->hide();
}

405 406 407 408 409 410 411 412 413
void MainWindow::acceptWindowClose(void)
{
    qgcApp()->toolbox()->linkManager()->shutdown();
    // The above shutdown 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);
}

414 415
void MainWindow::closeEvent(QCloseEvent *event)
{
416
    // Disallow window close if there are active connections
417
    if (qgcApp()->toolbox()->multiVehicleManager()->vehicles()->count()) {
Don Gagne's avatar
Don Gagne committed
418
        qgcApp()->showWindowCloseMessage();
419 420
        event->ignore();
        return;
421 422
    }

Don Gagne's avatar
Don Gagne committed
423 424 425 426
    // We still need to shutdown LinkManager even though no active connections so that we don't get any
    // more auto-connect links during shutdown.
    qgcApp()->toolbox()->linkManager()->shutdown();

427 428
    // This will process any remaining flight log save dialogs
    qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
429

430
    // Should not be any active connections
Don Gagne's avatar
Don Gagne committed
431 432 433
    if (qgcApp()->toolbox()->linkManager()->anyActiveLinks()) {
        qWarning() << "All links should be disconnected by now";
    }
434 435 436 437

    // 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.
438 439 440 441 442 443 444 445 446 447 448

    //-- Unit test gets here with _mainQmlWidgetHolder being NULL

    if(_mainQmlWidgetHolder)
    {
        // Remove image provider
        _mainQmlWidgetHolder->getEngine()->removeImageProvider(QLatin1String("QGCImages"));
        _centralLayout->removeWidget(_mainQmlWidgetHolder);
        delete _mainQmlWidgetHolder;
        _mainQmlWidgetHolder = NULL;
    }
449

450
    _storeCurrentViewState();
451
    storeSettings();
452

453
    event->accept();
454

dogmaphobic's avatar
dogmaphobic committed
455 456
    //-- TODO: This effectively causes the QGCApplication destructor to not being able
    //   to access the pointer it is trying to delete.
457
    _instance = NULL;
dogmaphobic's avatar
dogmaphobic committed
458

459
    emit mainWindowClosed();
460 461 462 463
}

void MainWindow::loadSettings()
{
464
    // Why the screaming?
465
    QSettings settings;
466
    settings.beginGroup(MAIN_SETTINGS_GROUP);
467 468
    _lowPowerMode   = settings.value("LOW_POWER_MODE",      _lowPowerMode).toBool();
    _showStatusBar  = settings.value("SHOW_STATUSBAR",      _showStatusBar).toBool();
469 470 471 472 473 474
    settings.endGroup();
}

void MainWindow::storeSettings()
{
    QSettings settings;
475
    settings.beginGroup(MAIN_SETTINGS_GROUP);
476 477
    settings.setValue("LOW_POWER_MODE",     _lowPowerMode);
    settings.setValue("SHOW_STATUSBAR",     _showStatusBar);
478
    settings.endGroup();
dogmaphobic's avatar
dogmaphobic committed
479
    settings.setValue(_getWindowGeometryKey(), saveGeometry());
480

481 482 483
#ifndef __mobile__
    _storeVisibleWidgetsSettings();
#endif
484 485 486 487 488 489
}

void MainWindow::configureWindowName()
{
    QList<QHostAddress> hostAddresses = QNetworkInterface::allAddresses();
    QString windowname = qApp->applicationName() + " " + qApp->applicationVersion();
490 491 492 493 494

    // XXX we do have UDP MAVLink heartbeat broadcast now in SITL and will have it on the
    // WIFI radio, so people should not be in need any more of knowing their IP.
    // this can go once we are certain its not needed any more.
    #if 0
495
    bool prevAddr = false;
496 497 498 499 500 501 502 503 504 505 506 507
    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(")");
508
    #endif
509 510 511 512 513 514 515 516 517 518
    setWindowTitle(windowname);
}

/**
* @brief Create all actions associated to the main window
*
**/
void MainWindow::connectCommonActions()
{
    // Audio output
519 520 521
    _ui.actionMuteAudioOutput->setChecked(qgcApp()->toolbox()->audioOutput()->isMuted());
    connect(qgcApp()->toolbox()->audioOutput(), SIGNAL(mutedChanged(bool)), _ui.actionMuteAudioOutput, SLOT(setChecked(bool)));
    connect(_ui.actionMuteAudioOutput, SIGNAL(triggered(bool)), qgcApp()->toolbox()->audioOutput(), SLOT(mute(bool)));
522 523

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

526
    // Views actions
Don Gagne's avatar
Don Gagne committed
527 528 529 530 531 532
    connect(_ui.actionFlight,   &QAction::triggered,    qgcApp(),   &QGCApplication::showFlyView);
    connect(_ui.actionPlan,     &QAction::triggered,    qgcApp(),   &QGCApplication::showPlanView);
    connect(_ui.actionSetup,    &QAction::triggered,    qgcApp(),   &QGCApplication::showSetupView);
    connect(_ui.actionFlight,   &QAction::triggered,    this,       &MainWindow::handleActiveViewActionState);
    connect(_ui.actionPlan,     &QAction::triggered,    this,       &MainWindow::handleActiveViewActionState);
    connect(_ui.actionSetup,    &QAction::triggered,    this,       &MainWindow::handleActiveViewActionState);
533 534

    // Connect internal actions
535
    connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::vehicleAdded, this, &MainWindow::_vehicleAdded);
536 537
}

538 539 540 541 542 543 544 545 546
void MainWindow::handleActiveViewActionState(bool triggered)
{
    Q_UNUSED(triggered);
    QAction *triggeredAction = qobject_cast<QAction*>(sender());
    _ui.actionFlight->setChecked(triggeredAction == _ui.actionFlight);
    _ui.actionPlan->setChecked(triggeredAction == _ui.actionPlan);
    _ui.actionSetup->setChecked(triggeredAction == _ui.actionSetup);
}

Don Gagne's avatar
Don Gagne committed
547
void MainWindow::_openUrl(const QString& url, const QString& errorMessage)
548
{
Don Gagne's avatar
Don Gagne committed
549
    if(!QDesktopServices::openUrl(QUrl(url))) {
550
        qgcApp()->showMessage(QString("Could not open information in browser: %1").arg(errorMessage));
551 552 553
    }
}

554
#ifndef __mobile__
555 556
void MainWindow::showSettings()
{
557
    SettingsDialog settings(this);
Don Gagne's avatar
Don Gagne committed
558
    settings.exec();
559
}
560 561
#endif

562
void MainWindow::_vehicleAdded(Vehicle* vehicle)
563
{
564
    connect(vehicle->uas(), SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)), this, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)));
565 566
}

567 568
/// Stores the state of the toolbar, status bar and widgets associated with the current view
void MainWindow::_storeCurrentViewState(void)
569
{
Don Gagne's avatar
Don Gagne committed
570
#ifndef __mobile__
571 572
    foreach(QGCDockWidget* dockWidget, _mapName2DockWidget) {
        dockWidget->saveSettings();
573
    }
Don Gagne's avatar
Don Gagne committed
574
#endif
575

dogmaphobic's avatar
dogmaphobic committed
576
    settings.setValue(_getWindowGeometryKey(), saveGeometry());
577 578
}

579 580 581 582 583 584 585 586 587
/// @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);
}

588
#ifdef QGC_MOUSE_ENABLED_LINUX
589 590 591
bool MainWindow::x11Event(XEvent *event)
{
    emit x11EventOccured(event);
592
    return false;
593
}
594
#endif // QGC_MOUSE_ENABLED_LINUX
595 596 597 598 599 600 601

#ifdef UNITTEST_BUILD
void MainWindow::_showQmlTestWidget(void)
{
    new QmlTestWidget();
}
#endif
602 603 604 605 606

#ifndef __mobile__
void MainWindow::_loadVisibleWidgetsSettings(void)
{
    QSettings settings;
607

608
    QString widgets = settings.value(_visibleWidgetsKey).toString();
609

610 611
    if (!widgets.isEmpty()) {
        QStringList nameList = widgets.split(",");
612

613
        foreach (const QString &name, nameList) {
614 615 616 617 618 619 620 621 622
            _showDockWidget(name, true);
        }
    }
}

void MainWindow::_storeVisibleWidgetsSettings(void)
{
    QString widgetNames;
    bool firstWidget = true;
623

624
    foreach (const QString &name, _mapName2DockWidget.keys()) {
625 626 627 628 629 630
        if (_mapName2DockWidget[name]->isVisible()) {
            if (!firstWidget) {
                widgetNames += ",";
            } else {
                firstWidget = false;
            }
631

632 633 634
            widgetNames += name;
        }
    }
635

636
    QSettings settings;
637

638 639 640
    settings.setValue(_visibleWidgetsKey, widgetNames);
}
#endif
641

Don Gagne's avatar
Don Gagne committed
642
QObject* MainWindow::rootQmlObject(void)
643
{
Don Gagne's avatar
Don Gagne committed
644
    return _mainQmlWidgetHolder->getRootObject();
645
}