MainWindow.cc 23.3 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

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 46
#include "QGC.h"
#include "MAVLinkProtocol.h"
#include "MainWindow.h"
#include "GAudioOutput.h"
#include "QGCMAVLinkLogPlayer.h"
Don Gagne's avatar
Don Gagne committed
47
#include "SettingsDialog.h"
48
#include "MAVLinkDecoder.h"
Don Gagne's avatar
Don Gagne committed
49
#include "QGCApplication.h"
50
#include "QGCFileDialog.h"
Don Gagne's avatar
Don Gagne committed
51
#include "QGCMessageBox.h"
52
#include "MultiVehicleManager.h"
53
#include "HomePositionManager.h"
54
#include "LogCompressor.h"
55
#include "UAS.h"
56 57

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

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

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

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


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

87
#ifndef __mobile__
88 89 90 91 92 93 94
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
95
const char* MainWindow::_analyzeDockWidgetName =            "Analyze";
96 97

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

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

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

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

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

Don Gagne's avatar
Don Gagne committed
121 122 123
/// @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
124
MainWindow::MainWindow()
dogmaphobic's avatar
dogmaphobic committed
125 126
    : _autoReconnect(false)
    , _lowPowerMode(false)
127
    , _showStatusBar(false)
128
    , _mainQmlWidgetHolder(NULL)
129
{
Don Gagne's avatar
Don Gagne committed
130 131
    Q_ASSERT(_instance == NULL);
    _instance = this;
132

133 134 135 136
    // 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
137
    // Setup user interface
138
    loadSettings();
139
    emit initStatusChanged(tr("Setting up user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
140

dogmaphobic's avatar
dogmaphobic committed
141 142
    _ui.setupUi(this);
    // Make sure tool bar elements all fit before changing minimum width
dogmaphobic's avatar
dogmaphobic committed
143
    setMinimumWidth(1008);
dogmaphobic's avatar
dogmaphobic committed
144
    configureWindowName();
145

146 147
    // Setup central widget with a layout to hold the views
    _centralLayout = new QVBoxLayout();
148
    _centralLayout->setContentsMargins(0, 0, 0, 0);
149
    centralWidget()->setLayout(_centralLayout);
150 151 152 153 154 155 156 157

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

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

158
    // Set dock options
159
    setDockOptions(0);
160
    // Setup corners
161
    setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
162

dogmaphobic's avatar
dogmaphobic committed
163 164 165
    // On Mobile devices, we don't want any main menus at all.
#ifdef __mobile__
    menuBar()->setNativeMenuBar(false);
dogmaphobic's avatar
dogmaphobic committed
166
#endif
dogmaphobic's avatar
dogmaphobic committed
167

168 169 170
#ifdef UNITTEST_BUILD
    QAction* qmlTestAction = new QAction("Test QML palette and controls", NULL);
    connect(qmlTestAction, &QAction::triggered, this, &MainWindow::_showQmlTestWidget);
171
    _ui.menuWidgets->addAction(qmlTestAction);
172
#endif
173

dogmaphobic's avatar
dogmaphobic committed
174
    // Status Bar
175
    setStatusBar(new QStatusBar(this));
176
    statusBar()->setSizeGripEnabled(true);
177

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

193
#if QGC_MOUSE_ENABLED_LINUX
194
    emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
195 196

    mouse = new Mouse6dofInput(this);
197
    connect(this, SIGNAL(x11EventOccured(XEvent*)), mouse, SLOT(handleX11Event(XEvent*)));
198
#endif //QGC_MOUSE_ENABLED_LINUX
199

200 201 202 203
    // 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);

204
    // Connect link
dogmaphobic's avatar
dogmaphobic committed
205
    if (_autoReconnect)
206
    {
207
        restoreLastUsedConnection();
208 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 241 242
    // Make sure the proper fullscreen/normal menu item is checked properly.
    if (isFullScreen())
    {
dogmaphobic's avatar
dogmaphobic committed
243 244
        _ui.actionFullscreen->setChecked(true);
        _ui.actionNormal->setChecked(false);
245 246 247
    }
    else
    {
dogmaphobic's avatar
dogmaphobic committed
248 249
        _ui.actionFullscreen->setChecked(false);
        _ui.actionNormal->setChecked(true);
250 251 252
    }

    // And that they will stay checked properly after user input
253 254
    connect(_ui.actionFullscreen, &QAction::triggered, this, &MainWindow::fullScreenActionItemCallback);
    connect(_ui.actionNormal,     &QAction::triggered, this, &MainWindow::normalActionItemCallback);
255 256
#endif

257
    connect(_ui.actionStatusBar,  &QAction::triggered, this, &MainWindow::showStatusBarCallback);
258

259 260
    // 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
261
    _ui.actionSetup->setShortcut(QApplication::translate("MainWindow", "Meta+1", 0));
262 263
    _ui.actionPlan->setShortcut(QApplication::translate("MainWindow", "Meta+2", 0));
    _ui.actionFlight->setShortcut(QApplication::translate("MainWindow", "Meta+3", 0));
dogmaphobic's avatar
dogmaphobic committed
264
    _ui.actionFullscreen->setShortcut(QApplication::translate("MainWindow", "Meta+Return", 0));
265
#else
dogmaphobic's avatar
dogmaphobic committed
266
    _ui.actionSetup->setShortcut(QApplication::translate("MainWindow", "Ctrl+1", 0));
Gus Grubba's avatar
Gus Grubba committed
267
    _ui.actionPlan->setShortcut(QApplication::translate("MainWindow", "Ctrl+2", 0));
268
    _ui.actionFlight->setShortcut(QApplication::translate("MainWindow", "Ctrl+3", 0));
dogmaphobic's avatar
dogmaphobic committed
269
    _ui.actionFullscreen->setShortcut(QApplication::translate("MainWindow", "Ctrl+Return", 0));
270 271
#endif

272 273
    _ui.actionFlight->setChecked(true);

274 275
    connect(&windowNameUpdateTimer, SIGNAL(timeout()), this, SLOT(configureWindowName()));
    windowNameUpdateTimer.start(15000);
276
    emit initStatusChanged(tr("Done"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
277 278

    if (!qgcApp()->runningUnitTests()) {
279 280
        _ui.actionStatusBar->setChecked(_showStatusBar);
        showStatusBarCallback(_showStatusBar);
dogmaphobic's avatar
dogmaphobic committed
281
#ifdef __mobile__
282 283
        menuBar()->hide();
#endif
284
        show();
dogmaphobic's avatar
dogmaphobic committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
#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
300
    }
301 302 303 304
    
#ifndef __mobile__
    _loadVisibleWidgetsSettings();
#endif
305 306 307 308
}

MainWindow::~MainWindow()
{
Don Gagne's avatar
Don Gagne committed
309
    _instance = NULL;
310 311
}

dogmaphobic's avatar
dogmaphobic committed
312
QString MainWindow::_getWindowGeometryKey()
313 314 315 316
{
    return "_geometry";
}

317
#ifndef __mobile__
318
void MainWindow::_buildCommonWidgets(void)
319 320
{
    // Add generic MAVLink decoder
dogmaphobic's avatar
dogmaphobic committed
321
    // TODO: This is never deleted
322
    mavlinkDecoder = new MAVLinkDecoder(MAVLinkProtocol::instance(), this);
John Tapsell's avatar
John Tapsell committed
323 324
    connect(mavlinkDecoder, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)),
                      this, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)));
325

326
    // Log player
dogmaphobic's avatar
dogmaphobic committed
327
    // TODO: Make this optional with a preferences setting or under a "View" menu
Don Gagne's avatar
Don Gagne committed
328
    logPlayer = new QGCMAVLinkLogPlayer(statusBar());
329
    statusBar()->addPermanentWidget(logPlayer);
330

331 332 333 334 335 336 337 338
    static const char* rgDockWidgetNames[] = {
        _mavlinkDockWidgetName,
        _customCommandWidgetName,
        _filesDockWidgetName,
        _uasStatusDetailsDockWidgetName,
        _pfdDockWidgetName,
        _uasInfoViewDockWidgetName,
        _hilDockWidgetName,
Don Gagne's avatar
Don Gagne committed
339
        _analyzeDockWidgetName,
340
    };
341
    static const size_t cDockWidgetNames = sizeof(rgDockWidgetNames) / sizeof(rgDockWidgetNames[0]);
342

343 344 345 346 347 348 349 350 351 352 353
    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;
354
    }
355
}
356

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

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

405 406
void MainWindow::_hideAllDockWidgets(void)
{
407
    foreach(QGCDockWidget* dockWidget, _mapName2DockWidget) {
408 409 410 411 412 413 414 415
        dockWidget->setVisible(false);
    }
}

void MainWindow::_showDockWidgetAction(bool show)
{
    QAction* action = dynamic_cast<QAction*>(QObject::sender());
    Q_ASSERT(action);
416
    _showDockWidget(action->text(), show);
417 418 419
}
#endif

420
void MainWindow::fullScreenActionItemCallback(bool)
421
{
dogmaphobic's avatar
dogmaphobic committed
422
    _ui.actionNormal->setChecked(false);
423 424
}

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

430 431 432 433 434 435
void MainWindow::showStatusBarCallback(bool checked)
{
    _showStatusBar = checked;
    checked ? statusBar()->show() : statusBar()->hide();
}

436 437
void MainWindow::closeEvent(QCloseEvent *event)
{
438
    // Disallow window close if there are active connections
439
    if (LinkManager::instance()->anyConnectedLinks()) {
dogmaphobic's avatar
dogmaphobic committed
440 441 442 443 444 445
        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);
446 447 448 449 450 451 452 453 454 455
		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;
456 457
    }

458 459
    // This will process any remaining flight log save dialogs
    qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);
460
    
461
    // Should not be any active connections
462
    Q_ASSERT(!LinkManager::instance()->anyConnectedLinks());
463 464 465 466 467 468 469 470

    // 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;

471
    _storeCurrentViewState();
472
    storeSettings();
473
    event->accept();
474 475 476 477
}

void MainWindow::loadSettings()
{
478
    // Why the screaming?
479
    QSettings settings;
480
    settings.beginGroup(MAIN_SETTINGS_GROUP);
481 482 483
    _autoReconnect  = settings.value("AUTO_RECONNECT",      _autoReconnect).toBool();
    _lowPowerMode   = settings.value("LOW_POWER_MODE",      _lowPowerMode).toBool();
    _showStatusBar  = settings.value("SHOW_STATUSBAR",      _showStatusBar).toBool();
484 485 486 487 488 489
    settings.endGroup();
}

void MainWindow::storeSettings()
{
    QSettings settings;
490
    settings.beginGroup(MAIN_SETTINGS_GROUP);
491 492 493
    settings.setValue("AUTO_RECONNECT",     _autoReconnect);
    settings.setValue("LOW_POWER_MODE",     _lowPowerMode);
    settings.setValue("SHOW_STATUSBAR",     _showStatusBar);
494
    settings.endGroup();
dogmaphobic's avatar
dogmaphobic committed
495
    settings.setValue(_getWindowGeometryKey(), saveGeometry());
496 497 498 499
    
#ifndef __mobile__
    _storeVisibleWidgetsSettings();
#endif
500 501 502 503 504 505
}

void MainWindow::configureWindowName()
{
    QList<QHostAddress> hostAddresses = QNetworkInterface::allAddresses();
    QString windowname = qApp->applicationName() + " " + qApp->applicationVersion();
506 507 508 509 510

    // 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
511
    bool prevAddr = false;
512 513 514 515 516 517 518 519 520 521 522 523
    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(")");
524
    #endif
525 526 527 528 529
    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
}

592 593
void MainWindow::manageLinks()
{
dogmaphobic's avatar
dogmaphobic committed
594
    SettingsDialog settings(this, SettingsDialog::ShowCommLinks);
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
    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)) {
618
        QString connection = settings.value(key).toString();
619
        // Create a link for it
620
        LinkManager::instance()->createConnectedLink(connection);
621 622
    }
}
Don Gagne's avatar
Don Gagne committed
623

624 625 626 627 628
void MainWindow::_linkStateChange(LinkInterface*)
{
    emit repaintCanvas();
}

629
#ifdef QGC_MOUSE_ENABLED_LINUX
630 631 632
bool MainWindow::x11Event(XEvent *event)
{
    emit x11EventOccured(event);
633
    return false;
634
}
635
#endif // QGC_MOUSE_ENABLED_LINUX
636 637 638 639 640 641 642

#ifdef UNITTEST_BUILD
void MainWindow::_showQmlTestWidget(void)
{
    new QmlTestWidget();
}
#endif
643 644 645 646 647 648 649 650 651 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

#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