MainWindow.cc 49.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 35 36 37 38

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>
#include <QGCHilLink.h>
#include <QGCHilConfiguration.h>
#include <QGCHilFlightGearConfiguration.h>
39
#include <QQuickView>
40 41
#include <QDesktopWidget>

42 43 44 45 46 47 48 49 50 51
#include "QGC.h"
#include "MAVLinkSimulationLink.h"
#include "SerialLink.h"
#include "MAVLinkProtocol.h"
#include "QGCWaypointListMulti.h"
#include "MainWindow.h"
#include "JoystickWidget.h"
#include "GAudioOutput.h"
#include "QGCToolWidget.h"
#include "QGCMAVLinkLogPlayer.h"
Don Gagne's avatar
Don Gagne committed
52
#include "SettingsDialog.h"
53 54 55 56
#include "QGCMapTool.h"
#include "MAVLinkDecoder.h"
#include "QGCMAVLinkMessageSender.h"
#include "QGCRGBDView.h"
57
#include "UASQuickView.h"
58 59
#include "QGCDataPlot2D.h"
#include "Linecharts.h"
60 61
#include "QGCTabbedInfoView.h"
#include "UASRawStatusView.h"
62
#include "PrimaryFlightDisplay.h"
63
#include "SetupView.h"
64 65
#include "SerialSettingsDialog.h"
#include "terminalconsole.h"
66
#include "QGCUASFileViewMulti.h"
Don Gagne's avatar
Don Gagne committed
67
#include "QGCApplication.h"
68
#include "QGCFileDialog.h"
Don Gagne's avatar
Don Gagne committed
69
#include "QGCMessageBox.h"
70
#include "QGCDockWidget.h"
71

72 73 74 75
#ifdef UNITTEST_BUILD
#include "QmlControls/QmlTestWidget.h"
#endif

76 77 78 79 80 81
#ifdef QGC_OSG_ENABLED
#include "Q3DWidgetFactory.h"
#endif

#include "LogCompressor.h"

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

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
const char* MainWindow::_uasControlDockWidgetName = "UNMANNED_SYSTEM_CONTROL_DOCKWIDGET";
const char* MainWindow::_uasListDockWidgetName = "UNMANNED_SYSTEM_LIST_DOCKWIDGET";
const char* MainWindow::_waypointsDockWidgetName = "WAYPOINT_LIST_DOCKWIDGET";
const char* MainWindow::_mavlinkDockWidgetName = "MAVLINK_INSPECTOR_DOCKWIDGET";
const char* MainWindow::_parametersDockWidgetName = "PARAMETER_INTERFACE_DOCKWIDGET";
const char* MainWindow::_filesDockWidgetName = "FILE_VIEW_DOCKWIDGET";
const char* MainWindow::_uasStatusDetailsDockWidgetName = "UAS_STATUS_DETAILS_DOCKWIDGET";
const char* MainWindow::_mapViewDockWidgetName = "MAP_VIEW_DOCKWIDGET";
const char* MainWindow::_hsiDockWidgetName = "HORIZONTAL_SITUATION_INDICATOR_DOCKWIDGET";
const char* MainWindow::_hdd1DockWidgetName = "HEAD_DOWN_DISPLAY_1_DOCKWIDGET";
const char* MainWindow::_hdd2DockWidgetName = "HEAD_DOWN_DISPLAY_2_DOCKWIDGET";
const char* MainWindow::_pfdDockWidgetName = "PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET";
const char* MainWindow::_hudDockWidgetName = "HEAD_UP_DISPLAY_DOCKWIDGET";
const char* MainWindow::_uasInfoViewDockWidgetName = "UAS_INFO_INFOVIEW_DOCKWIDGET";
const char* MainWindow::_debugConsoleDockWidgetName = "COMMUNICATION_CONSOLE_DOCKWIDGET";

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

107
    new MainWindow(splashScreen);
108

Don Gagne's avatar
Don Gagne committed
109 110 111
    // _instance is set in constructor
    Q_ASSERT(_instance);

112 113 114
    return _instance;
}

Don Gagne's avatar
Don Gagne committed
115
MainWindow* MainWindow::instance(void)
116
{
Don Gagne's avatar
Don Gagne committed
117
    return _instance;
118 119
}

120 121
void MainWindow::deleteInstance(void)
{
Don Gagne's avatar
Don Gagne committed
122
    delete this;
123 124
}

Don Gagne's avatar
Don Gagne committed
125 126 127
/// @brief Private constructor for MainWindow. MainWindow singleton is only ever created
///         by MainWindow::_create method. Hence no other code should have access to
///         constructor.
128
MainWindow::MainWindow(QSplashScreen* splashScreen) :
129 130
    centerStackActionGroup(new QActionGroup(this)),
    autoReconnect(false),
131
    simulationLink(NULL),
132
    lowPowerMode(false),
133 134
    _currentView(VIEW_FLIGHT),
    _currentViewWidget(NULL),
Don Gagne's avatar
Don Gagne committed
135
    _splashScreen(splashScreen)
136
{
Don Gagne's avatar
Don Gagne committed
137 138
    Q_ASSERT(_instance == NULL);
    _instance = this;
139

140 141 142
    if (splashScreen) {
        connect(this, &MainWindow::initStatusChanged, splashScreen, &QSplashScreen::showMessage);
    }
143

144
    loadSettings();
145

146
    // Select the proper view. Default to the flight view or load the last one used if it's supported.
147 148 149 150 151 152 153 154
    VIEW_SECTIONS currentViewCandidate = (VIEW_SECTIONS) settings.value("CURRENT_VIEW", _currentView).toInt();
    switch (currentViewCandidate) {
        case VIEW_ENGINEER:
        case VIEW_MISSION:
        case VIEW_FLIGHT:
        case VIEW_SIMULATION:
        case VIEW_SETUP:
        case VIEW_TERMINAL:
155
#ifdef QGC_OSG_ENABLED
156
        case VIEW_LOCAL3D:
157 158
#endif
#ifdef QGC_GOOGLE_EARTH_ENABLED
159
        case VIEW_GOOGLEEARTH:
160
#endif
161 162
            _currentView = currentViewCandidate;
            break;
163

164 165 166
        default:
            // Leave _currentView to the default
            break;
167
    }
168

169 170
    // Put it back, which will set it to a valid value
    settings.setValue("CURRENT_VIEW", _currentView);
171

172
    emit initStatusChanged(tr("Setting up user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
173 174 175

    // Setup user interface
    ui.setupUi(this);
176

177 178 179
    // Setup central widget with a layout to hold the views
    _centralLayout = new QVBoxLayout();
    centralWidget()->setLayout(_centralLayout);
180

181 182 183 184 185 186
    // Set dock options
    setDockOptions(AnimatedDocks | AllowTabbedDocks | AllowNestedDocks);

    configureWindowName();

    // Setup corners
187
    setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
188

189 190 191 192 193
    // Qt 4 on Ubuntu does place the native menubar correctly so on Linux we revert back to in-window menu bar.
    // TODO: Check that this is still necessary on Qt5 on Ubuntu
#ifdef Q_OS_LINUX
    menuBar()->setNativeMenuBar(false);
#endif
194 195 196 197 198 199
    
#ifdef UNITTEST_BUILD
    QAction* qmlTestAction = new QAction("Test QML palette and controls", NULL);
    connect(qmlTestAction, &QAction::triggered, this, &MainWindow::_showQmlTestWidget);
    ui.menuTools->addAction(qmlTestAction);
#endif
200

201
    // Setup UI state machines
202
    centerStackActionGroup->setExclusive(true);
203 204

    // Load Toolbar
Don Gagne's avatar
Don Gagne committed
205 206
    toolBar = new QGCToolBar(this);
    this->addToolBar(toolBar);
207

Bryant's avatar
Bryant committed
208
    // Add the perspectives to the toolbar
Don Gagne's avatar
Don Gagne committed
209
    QList<QAction*> actions;
210
    actions << ui.actionSetup;
Don Gagne's avatar
Don Gagne committed
211 212
    actions << ui.actionMissionView;
    actions << ui.actionFlightView;
213
    actions << ui.actionEngineersView;
Don Gagne's avatar
Don Gagne committed
214
    toolBar->setPerspectiveChangeActions(actions);
215

Don Gagne's avatar
Don Gagne committed
216 217 218 219 220 221 222
    // Add actions for advanced users (displayed in dropdown under "advanced")
    QList<QAction*> advancedActions;
    advancedActions << ui.actionGoogleEarthView;
    advancedActions << ui.actionLocal3DView;
    advancedActions << ui.actionTerminalView;
    advancedActions << ui.actionSimulationView;
    toolBar->setPerspectiveChangeAdvancedActions(advancedActions);
223

224
    setStatusBar(new QStatusBar(this));
225
    statusBar()->setSizeGripEnabled(true);
226

227
    emit initStatusChanged(tr("Building common widgets."), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
228

229
    _buildCommonWidgets();
230

231
    emit initStatusChanged(tr("Building common actions"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
232 233 234 235 236

    // Create actions
    connectCommonActions();

    // Connect user interface devices
237
    emit initStatusChanged(tr("Initializing joystick interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
238 239
    joystick = new JoystickInput();

240
#ifdef QGC_MOUSE_ENABLED_WIN
241
    emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
242 243 244

    mouseInput = new Mouse3DInput(this);
    mouse = new Mouse6dofInput(mouseInput);
245
#endif //QGC_MOUSE_ENABLED_WIN
246

247
#if QGC_MOUSE_ENABLED_LINUX
248
    emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
249 250

    mouse = new Mouse6dofInput(this);
251
    connect(this, SIGNAL(x11EventOccured(XEvent*)), mouse, SLOT(handleX11Event(XEvent*)));
252
#endif //QGC_MOUSE_ENABLED_LINUX
253

254 255 256
    // Connect link
    if (autoReconnect)
    {
257
        restoreLastUsedConnection();
258 259 260 261 262
    }

    // Set low power mode
    enableLowPowerMode(lowPowerMode);

263
    emit initStatusChanged(tr("Restoring last view state"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
264 265

    // Restore the window setup
266
    _loadCurrentViewState();
267

268
    emit initStatusChanged(tr("Restoring last window size"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
269 270 271 272 273 274 275 276 277 278 279 280
    // Restore the window position and size
    if (settings.contains(getWindowGeometryKey()))
    {
        // Restore the window geometry
        restoreGeometry(settings.value(getWindowGeometryKey()).toByteArray());
    }
    else
    {
        // Adjust the size
        const int screenWidth = QApplication::desktop()->width();
        const int screenHeight = QApplication::desktop()->height();

Lorenz Meier's avatar
Lorenz Meier committed
281
        if (screenWidth < 1500)
282
        {
283
            resize(screenWidth, screenHeight - 80);
284 285 286 287 288 289 290 291
        }
        else
        {
            resize(screenWidth*0.67f, qMin(screenHeight, (int)(screenWidth*0.67f*0.67f)));
        }

    }

292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    // Make sure the proper fullscreen/normal menu item is checked properly.
    if (isFullScreen())
    {
        ui.actionFullscreen->setChecked(true);
        ui.actionNormal->setChecked(false);
    }
    else
    {
        ui.actionFullscreen->setChecked(false);
        ui.actionNormal->setChecked(true);
    }

    // And that they will stay checked properly after user input
    QObject::connect(ui.actionFullscreen, SIGNAL(triggered()), this, SLOT(fullScreenActionItemCallback()));
    QObject::connect(ui.actionNormal, SIGNAL(triggered()), this,SLOT(normalActionItemCallback()));


309 310
    // Set OS dependent keyboard shortcuts for the main window, non OS dependent shortcuts are set in MainWindow.ui
#ifdef Q_OS_MACX
311 312 313
    ui.actionSetup->setShortcut(QApplication::translate("MainWindow", "Meta+1", 0));
    ui.actionMissionView->setShortcut(QApplication::translate("MainWindow", "Meta+2", 0));
    ui.actionFlightView->setShortcut(QApplication::translate("MainWindow", "Meta+3", 0));
314 315 316 317 318
    ui.actionEngineersView->setShortcut(QApplication::translate("MainWindow", "Meta+4", 0));
    ui.actionGoogleEarthView->setShortcut(QApplication::translate("MainWindow", "Meta+5", 0));
    ui.actionLocal3DView->setShortcut(QApplication::translate("MainWindow", "Meta+6", 0));
    ui.actionTerminalView->setShortcut(QApplication::translate("MainWindow", "Meta+7", 0));
    ui.actionSimulationView->setShortcut(QApplication::translate("MainWindow", "Meta+8", 0));
319
    ui.actionFullscreen->setShortcut(QApplication::translate("MainWindow", "Meta+Return", 0));
320
#else
321 322 323
    ui.actionSetup->setShortcut(QApplication::translate("MainWindow", "Ctrl+1", 0));
    ui.actionMissionView->setShortcut(QApplication::translate("MainWindow", "Ctrl+2", 0));
    ui.actionFlightView->setShortcut(QApplication::translate("MainWindow", "Ctrl+3", 0));
324 325 326 327 328
    ui.actionEngineersView->setShortcut(QApplication::translate("MainWindow", "Ctrl+4", 0));
    ui.actionGoogleEarthView->setShortcut(QApplication::translate("MainWindow", "Ctrl+5", 0));
    ui.actionLocal3DView->setShortcut(QApplication::translate("MainWindow", "Ctrl+6", 0));
    ui.actionTerminalView->setShortcut(QApplication::translate("MainWindow", "Ctrl+7", 0));
    ui.actionSimulationView->setShortcut(QApplication::translate("MainWindow", "Ctrl+8", 0));
329
    ui.actionFullscreen->setShortcut(QApplication::translate("MainWindow", "Ctrl+Return", 0));
330 331
#endif

332 333
    connect(&windowNameUpdateTimer, SIGNAL(timeout()), this, SLOT(configureWindowName()));
    windowNameUpdateTimer.start(15000);
334
    emit initStatusChanged(tr("Done"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
335 336 337 338

    if (!qgcApp()->runningUnitTests()) {
        show();
    }
339 340 341 342
}

MainWindow::~MainWindow()
{
343 344 345 346 347
    if (simulationLink)
    {
        delete simulationLink;
        simulationLink = NULL;
    }
348 349
    if (joystick)
    {
350 351
        joystick->shutdown();
        joystick->wait(5000);
352 353 354 355 356
        delete joystick;
        joystick = NULL;
    }

    // Delete all UAS objects
357 358 359 360
    for (int i=0;i<commsWidgetList.size();i++)
    {
        commsWidgetList[i]->deleteLater();
    }
361

Don Gagne's avatar
Don Gagne committed
362
    _instance = NULL;
363 364 365 366 367 368 369 370 371
}

void MainWindow::resizeEvent(QResizeEvent * event)
{
    QMainWindow::resizeEvent(event);
}

QString MainWindow::getWindowStateKey()
{
372 373
    if (UASManager::instance()->getActiveUAS())
    {
374
        return QString::number(_currentView)+"_windowstate_" + UASManager::instance()->getActiveUAS()->getAutopilotTypeName();
375 376
    }
    else
377
        return QString::number(_currentView)+"_windowstate_";
378 379 380 381 382 383 384
}

QString MainWindow::getWindowGeometryKey()
{
    return "_geometry";
}

385
void MainWindow::_buildCustomWidgets(void)
386
{
387
    Q_ASSERT(_customWidgets.count() == 0);
388

389
    // Create custom widgets
390
    _customWidgets = QGCToolWidget::createWidgetsFromSettings(this);
391

392
    if (_customWidgets.size() > 0)
393 394 395
    {
        ui.menuTools->addSeparator();
    }
396

397
    foreach(QGCToolWidget* tool, _customWidgets) {
398 399
        // Check if this widget already has a parent, do not create it in this case
        QDockWidget* dock = dynamic_cast<QDockWidget*>(tool->parentWidget());
400

401 402
        if (!dock) {
            _createDockWidget(tool->getTitle(), tool->objectName(), Qt::BottomDockWidgetArea, tool);
403 404 405 406
        }
    }
}

407 408 409
void MainWindow::_createDockWidget(const QString& title, const QString& name, Qt::DockWidgetArea area, QWidget* innerWidget)
{
    Q_ASSERT(!_mapName2DockWidget.contains(name));
410

411 412 413 414
    QGCDockWidget* dockWidget = new QGCDockWidget(title, this);
    Q_CHECK_PTR(dockWidget);
    dockWidget->setObjectName(name);
    dockWidget->setVisible (false);
415

416 417 418 419 420 421
    if (innerWidget) {
        // Put inner widget inside QDockWidget
        innerWidget->setParent(dockWidget);
        dockWidget->setWidget(innerWidget);
        innerWidget->setVisible(true);
    }
422

423
    // Add to menu
424

425 426 427
    QAction* action = new QAction(title, NULL);
    action->setCheckable(true);
    action->setData(name);
428

429
    connect(action, &QAction::triggered, this, &MainWindow::_showDockWidgetAction);
430

431
    ui.menuTools->addAction(action);
432

433 434
    _mapName2DockWidget[name] = dockWidget;
    _mapDockWidget2Action[dockWidget] = action;
435

436 437 438 439
    addDockWidget(area, dockWidget);
}

void MainWindow::_buildCommonWidgets(void)
440 441
{
    // Add generic MAVLink decoder
442
    mavlinkDecoder = new MAVLinkDecoder(MAVLinkProtocol::instance(), this);
John Tapsell's avatar
John Tapsell committed
443 444
    connect(mavlinkDecoder, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)),
                      this, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)));
445

446
    // Log player
447
    logPlayer = new QGCMAVLinkLogPlayer(MAVLinkProtocol::instance(), statusBar());
448
    statusBar()->addPermanentWidget(logPlayer);
449

450 451 452
    // In order for Qt to save and restore state of widgets all widgets must be created ahead of time. We only create the QDockWidget
    // holders. We do not create the actual inner widget until it is needed. This saves memory and cpu from running widgets that are
    // never shown.
453

454 455 456 457 458
    struct DockWidgetInfo {
        const char* name;
        const char* title;
        Qt::DockWidgetArea area;
    };
459

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
    static const struct DockWidgetInfo rgDockWidgetInfo[] = {
        { _uasControlDockWidgetName,        "Control",                  Qt::LeftDockWidgetArea },
        { _uasListDockWidgetName,           "Unmanned Systems",         Qt::RightDockWidgetArea },
        { _waypointsDockWidgetName,         "Mission Plan",             Qt::BottomDockWidgetArea },
        { _mavlinkDockWidgetName,           "MAVLink Inspector",        Qt::RightDockWidgetArea },
        { _parametersDockWidgetName,        "Onboard Parameters",       Qt::RightDockWidgetArea },
        { _filesDockWidgetName,             "Onboard Files",            Qt::RightDockWidgetArea },
        { _uasStatusDetailsDockWidgetName,  "Status Details",           Qt::RightDockWidgetArea },
        { _mapViewDockWidgetName,           "Map view",                 Qt::RightDockWidgetArea },
        { _hsiDockWidgetName,               "Horizontal Situation",     Qt::BottomDockWidgetArea },
        { _hdd1DockWidgetName,              "Flight Display",           Qt::RightDockWidgetArea },
        { _hdd2DockWidgetName,              "Actuator Status",          Qt::RightDockWidgetArea },
        { _pfdDockWidgetName,               "Primary Flight Display",   Qt::RightDockWidgetArea },
        { _hudDockWidgetName,               "Video Downlink",           Qt::RightDockWidgetArea },
        { _uasInfoViewDockWidgetName,       "Info View",                Qt::LeftDockWidgetArea },
        { _debugConsoleDockWidgetName,      "Communications Console",   Qt::LeftDockWidgetArea }
    };
    static const size_t cDockWidgetInfo = sizeof(rgDockWidgetInfo) / sizeof(rgDockWidgetInfo[0]);
478

479 480
    for (size_t i=0; i<cDockWidgetInfo; i++) {
        const struct DockWidgetInfo* pDockInfo = &rgDockWidgetInfo[i];
481

482
        _createDockWidget(pDockInfo->title, pDockInfo->name, pDockInfo->area, NULL /* no inner widget yet */);
483 484
    }

485 486
    _buildCustomWidgets();
}
487

488 489 490 491 492
void MainWindow::_buildPlannerView(void)
{
    if (!_plannerView) {
        _plannerView = new QGCMapTool(this);
        _plannerView->setVisible(false);
493
    }
494
}
495

496 497 498 499 500 501
void MainWindow::_buildPilotView(void)
{
    if (!_pilotView) {
        _pilotView = new PrimaryFlightDisplay(this);
        _pilotView->setVisible(false);
    }
502 503
}

504
void MainWindow::_buildSetupView(void)
505
{
506 507 508 509
    if (!_setupView) {
        _setupView = new SetupView(this);
        _setupView->setVisible(false);
    }
510 511
}

512
void MainWindow::_buildEngineeringView(void)
513
{
514 515 516 517 518
    if (!_engineeringView) {
        _engineeringView = new QGCDataPlot2D(this);
        _engineeringView->setVisible(false);
    }
}
519

520 521 522 523 524 525
void MainWindow::_buildSimView(void)
{
    if (!_simView) {
        _simView = new QGCMapTool(this);
        _simView->setVisible(false);
    }
526
}
527

528
void MainWindow::_buildTerminalView(void)
529
{
530 531 532 533
    if (!_terminalView) {
        _terminalView = new TerminalConsole(this);
        _terminalView->setVisible(false);
    }
534 535
}

536
void MainWindow::_buildGoogleEarthView(void)
537
{
538 539 540 541 542 543
#ifdef QGC_GOOGLE_EARTH_ENABLED
    if (!_googleEarthView) {
        _googleEarthView = new QGCGoogleEarthView(this);
        _googleEarthView->setVisible(false);
    }
#endif
544 545
}

546
void MainWindow::_buildLocal3DView(void)
547
{
548 549 550 551 552 553
#ifdef QGC_OSG_ENABLED
    if (!_local3DView) {
        _local3DView = Q3DWidgetFactory::get("PIXHAWK", this);
        _local3DView->setVisible(false);
    }
#endif
554 555
}

556 557
/// Shows or hides the specified dock widget, creating if necessary
void MainWindow::_showDockWidget(const QString& name, bool show)
558
{
559 560
    if (!_mapName2DockWidget.contains(name)) {
        qWarning() << "Attempt to show unknown dock widget" << name;
561 562
        return;
    }
563

564 565 566
    // Create the inner widget if we need to
    if (!_mapName2DockWidget[name]->widget()) {
        _createInnerDockWidget(name);
567
    }
568 569 570 571

    Q_ASSERT(_mapName2DockWidget.contains(name));
    QDockWidget* dockWidget = _mapName2DockWidget[name];
    Q_ASSERT(dockWidget);
572

573
    dockWidget->setVisible(show);
574

575 576 577 578 579 580 581 582 583
    Q_ASSERT(_mapDockWidget2Action.contains(dockWidget));
    _mapDockWidget2Action[dockWidget]->setChecked(show);
}

/// Creates the specified inner dock widget and adds to the QDockWidget
void MainWindow::_createInnerDockWidget(const QString& widgetName)
{
    Q_ASSERT(_mapName2DockWidget.contains(widgetName)); // QDockWidget should already exist
    Q_ASSERT(!_mapName2DockWidget[widgetName]->widget());     // Inner widget should not
584

585
    QWidget* widget = NULL;
586

587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
    if (widgetName == _uasControlDockWidgetName) {
        widget = new UASControlWidget(this);
    } else if (widgetName == _uasListDockWidgetName) {
        widget = new UASListWidget(this);
    } else if (widgetName == _waypointsDockWidgetName) {
        widget = new QGCWaypointListMulti(this);
    } else if (widgetName == _mavlinkDockWidgetName) {
        widget = new QGCMAVLinkInspector(MAVLinkProtocol::instance(),this);
    } else if (widgetName == _parametersDockWidgetName) {
        widget = new ParameterInterface(this);
    } else if (widgetName == _filesDockWidgetName) {
        widget = new QGCUASFileViewMulti(this);
    } else if (widgetName == _uasStatusDetailsDockWidgetName) {
        widget = new UASInfoWidget(this);
    } else if (widgetName == _mapViewDockWidgetName) {
        widget = new QGCMapTool(this);
    } else if (widgetName == _hsiDockWidgetName) {
        widget = new HSIDisplay(this);
    } else if (widgetName == _hdd1DockWidgetName) {
606 607 608 609
        QStringList acceptList;
        acceptList.append("-3.3,ATTITUDE.roll,rad,+3.3,s");
        acceptList.append("-3.3,ATTITUDE.pitch,deg,+3.3,s");
        acceptList.append("-3.3,ATTITUDE.yaw,deg,+3.3,s");
610 611
        HDDisplay *hddisplay = new HDDisplay(acceptList,"Flight Display",this);
        hddisplay->addSource(mavlinkDecoder);
612

613 614
        widget = hddisplay;
    } else if (widgetName == _hdd2DockWidgetName) {
615 616 617
        QStringList acceptList;
        acceptList.append("0,RAW_PRESSURE.pres_abs,hPa,65500");
        HDDisplay *hddisplay = new HDDisplay(acceptList,"Actuator Status",this);
618
        hddisplay->addSource(mavlinkDecoder);
619

620 621 622 623 624 625 626 627 628 629 630
        widget = hddisplay;
    } else if (widgetName == _pfdDockWidgetName) {
        widget = new PrimaryFlightDisplay(this);
    } else if (widgetName == _hudDockWidgetName) {
        widget = new HUD(320,240,this);
    } else if (widgetName == _uasInfoViewDockWidgetName) {
        widget = new QGCTabbedInfoView(this);
    } else if (widgetName == _debugConsoleDockWidgetName) {
        widget = new DebugConsole(this);
    } else {
        qWarning() << "Attempt to create unknown Inner Dock Widget" << widgetName;
631
    }
632

633 634 635
    if (widget) {
        QDockWidget* dockWidget = _mapName2DockWidget[widgetName];
        Q_CHECK_PTR(dockWidget);
636

637 638
        widget->setParent(dockWidget);
        dockWidget->setWidget(widget);
639 640
    }
}
641

642
void MainWindow::_showHILConfigurationWidgets(void)
643
{
644
    UASInterface* uas = UASManager::instance()->getActiveUAS();
645

646 647 648
    if (!uas) {
        return;
    }
649

650 651
    UAS* mav = dynamic_cast<UAS*>(uas);
    Q_ASSERT(mav);
652

653
    int uasId = mav->getUASID();
654

655
    if (!_mapUasId2HilDockWidget.contains(uasId)) {
656

657 658 659 660 661
        // Create QDockWidget
        QGCDockWidget* dockWidget = new QGCDockWidget(tr("HIL Config %1").arg(uasId), this);
        Q_CHECK_PTR(dockWidget);
        dockWidget->setObjectName(tr("HIL_CONFIG_%1").arg(uasId));
        dockWidget->setVisible (false);
662

663 664
        // Create inner widget and set it
        QWidget* widget = new QGCHilConfiguration(mav, dockWidget);
665

666 667
        widget->setParent(dockWidget);
        dockWidget->setWidget(widget);
668

669
        _mapUasId2HilDockWidget[uasId] = dockWidget;
670

671 672
        addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
    }
673

674 675 676 677 678
    if (_currentView == VIEW_SIMULATION) {
        // HIL dock widgets only show up on simulation view
        foreach (QDockWidget* dockWidget, _mapUasId2HilDockWidget) {
            dockWidget->setVisible(true);
        }
679 680 681
    }
}

682
void MainWindow::fullScreenActionItemCallback()
683
{
684
    ui.actionNormal->setChecked(false);
685 686
}

687
void MainWindow::normalActionItemCallback()
688
{
689
    ui.actionFullscreen->setChecked(false);
690 691 692 693
}

void MainWindow::closeEvent(QCloseEvent *event)
{
694
    // Disallow window close if there are active connections
695

696 697 698 699 700 701 702
    bool foundConnections = false;
    foreach(LinkInterface* link, LinkManager::instance()->getLinks()) {
        if (link->isConnected()) {
            foundConnections = true;
            break;
        }
    }
703

704
    if (foundConnections) {
705 706 707 708 709 710 711 712 713 714 715 716
        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);
        if (button == QMessageBox::Yes) {
            foreach(LinkInterface* link, LinkManager::instance()->getLinks()) {
                LinkManager::instance()->disconnectLink(link);
            }
        } else {
            event->ignore();
            return;
        }
717 718
    }

719 720 721
    // This will process any remaining flight log save dialogs
    qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents);

722 723
    // Should not be any active connections
    foreach(LinkInterface* link, LinkManager::instance()->getLinks()) {
Don Gagne's avatar
Don Gagne committed
724
        Q_UNUSED(link);
725 726 727
        Q_ASSERT(!link->isConnected());
    }

728
    _storeCurrentViewState();
729
    storeSettings();
730
    UASManager::instance()->storeSettings();
731
    event->accept();
732 733
}

734
void MainWindow::_createNewCustomWidget(void)
735
{
736
    if (QGCToolWidget::instances()->isEmpty())
737 738 739 740
    {
        // This is the first widget
        ui.menuTools->addSeparator();
    }
741 742 743 744 745 746 747 748 749 750
    QString objectName;
    int customToolIndex = 0;
    //Find the next unique object name that we can use
    do {
        ++customToolIndex;
        objectName = QString("CUSTOM_TOOL_%1").arg(customToolIndex) + "DOCK";
    } while(QGCToolWidget::instances()->contains(objectName));

    QString title = tr("Custom Tool %1").arg(customToolIndex );

751
    QGCToolWidget* tool = new QGCToolWidget(objectName, title);
752 753
    tool->resize(100, 100);
    _createDockWidget(title, objectName, Qt::BottomDockWidgetArea, tool);
754

755
    _mapName2DockWidget[objectName]->setVisible(true);
756 757
}

758
void MainWindow::_loadCustomWidgetFromFile(void)
759
{
760 761 762
    QString fileName = QGCFileDialog::getOpenFileName(
        this, tr("Load Widget File"),
        QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
763
        tr("QGroundControl Widgets (*.qgw);;All Files (*)"));
764
    if (!fileName.isEmpty()) {
765 766 767
        QGCToolWidget* tool = new QGCToolWidget("", "", this);
        if (tool->loadSettings(fileName, true)) {
            QString objectName = tool->objectName() + "DOCK";
768

769 770
            _createDockWidget(tool->getTitle(), objectName, Qt::LeftDockWidgetArea, tool);
            _mapName2DockWidget[objectName]->widget()->setVisible(true);
771 772
        }
    }
773
    // TODO Add error dialog if widget could not be loaded
774 775 776 777
}

void MainWindow::loadSettings()
{
778
    // Why the screaming?
779
    QSettings settings;
780
    settings.beginGroup(MAIN_SETTINGS_GROUP);
781
    autoReconnect = settings.value("AUTO_RECONNECT", autoReconnect).toBool();
782
    lowPowerMode  = settings.value("LOW_POWER_MODE", lowPowerMode).toBool();
783 784 785 786 787 788
    settings.endGroup();
}

void MainWindow::storeSettings()
{
    QSettings settings;
789

790
    settings.beginGroup(MAIN_SETTINGS_GROUP);
791 792
    settings.setValue("AUTO_RECONNECT", autoReconnect);
    settings.setValue("LOW_POWER_MODE", lowPowerMode);
793 794 795 796 797
    settings.endGroup();

    settings.setValue(getWindowGeometryKey(), saveGeometry());

    // Save the last current view in any case
798
    settings.setValue("CURRENT_VIEW", _currentView);
799 800 801 802 803

    // Save the current window state, but only if a system is connected (else no real number of widgets would be present))
    if (UASManager::instance()->getUASList().length() > 0) settings.setValue(getWindowStateKey(), saveState());

    // Save the current UAS view if a UAS is connected
804
    if (UASManager::instance()->getUASList().length() > 0) settings.setValue("CURRENT_VIEW_WITH_UAS_CONNECTED", _currentView);
805 806

    // And save any custom weidgets
807
    QGCToolWidget::storeWidgetsToSettings(settings);
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
}

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::startVideoCapture()
{
836
    // TODO: What is this? What kind of "Video" is saved to bmp?
837
    QString format("bmp");
838
    QString initialPath = QDir::currentPath() + tr("/untitled.") + format;
839 840 841 842 843 844
    QString screenFileName = QGCFileDialog::getSaveFileName(
        this, tr("Save Video Capture"),
        initialPath,
        tr("%1 Files (*.%2);;All Files (*)")
        .arg(format.toUpper())
        .arg(format),
845
        format);
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
    delete videoTimer;
    videoTimer = new QTimer(this);
}

void MainWindow::stopVideoCapture()
{
    videoTimer->stop();

    // TODO Convert raw images to PNG
}

void MainWindow::saveScreen()
{
    QPixmap window = QPixmap::grabWindow(this->winId());
    QString format = "bmp";

    if (!screenFileName.isEmpty())
    {
864
        window.save(screenFileName, format.toLatin1());
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
    }
}

void MainWindow::enableAutoReconnect(bool enabled)
{
    autoReconnect = enabled;
}

/**
* @brief Create all actions associated to the main window
*
**/
void MainWindow::connectCommonActions()
{
    // Bind together the perspective actions
    QActionGroup* perspectives = new QActionGroup(ui.menuPerspectives);
    perspectives->addAction(ui.actionEngineersView);
882
    perspectives->addAction(ui.actionFlightView);
883
    perspectives->addAction(ui.actionSimulationView);
884
    perspectives->addAction(ui.actionMissionView);
885
    perspectives->addAction(ui.actionSetup);
886
    perspectives->addAction(ui.actionTerminalView);
Lorenz Meier's avatar
Lorenz Meier committed
887 888
    perspectives->addAction(ui.actionGoogleEarthView);
    perspectives->addAction(ui.actionLocal3DView);
889 890
    perspectives->setExclusive(true);

891
    /* Hide the actions that are not relevant */
892 893 894 895 896 897 898
#ifndef QGC_GOOGLE_EARTH_ENABLED
    ui.actionGoogleEarthView->setVisible(false);
#endif
#ifndef QGC_OSG_ENABLED
    ui.actionLocal3DView->setVisible(false);
#endif

899
    // Mark the right one as selected
900
    if (_currentView == VIEW_ENGINEER)
901 902 903 904
    {
        ui.actionEngineersView->setChecked(true);
        ui.actionEngineersView->activate(QAction::Trigger);
    }
905
    if (_currentView == VIEW_FLIGHT)
906 907 908 909
    {
        ui.actionFlightView->setChecked(true);
        ui.actionFlightView->activate(QAction::Trigger);
    }
910
    if (_currentView == VIEW_SIMULATION)
911
    {
912 913
        ui.actionSimulationView->setChecked(true);
        ui.actionSimulationView->activate(QAction::Trigger);
914
    }
915
    if (_currentView == VIEW_MISSION)
916 917 918 919
    {
        ui.actionMissionView->setChecked(true);
        ui.actionMissionView->activate(QAction::Trigger);
    }
920
    if (_currentView == VIEW_SETUP)
921
    {
922 923
        ui.actionSetup->setChecked(true);
        ui.actionSetup->activate(QAction::Trigger);
924
    }
925
    if (_currentView == VIEW_TERMINAL)
926 927 928 929
    {
        ui.actionTerminalView->setChecked(true);
        ui.actionTerminalView->activate(QAction::Trigger);
    }
930
    if (_currentView == VIEW_GOOGLEEARTH)
931 932 933 934
    {
        ui.actionGoogleEarthView->setChecked(true);
        ui.actionGoogleEarthView->activate(QAction::Trigger);
    }
935
    if (_currentView == VIEW_LOCAL3D)
936 937 938 939
    {
        ui.actionLocal3DView->setChecked(true);
        ui.actionLocal3DView->activate(QAction::Trigger);
    }
940 941 942 943 944 945 946 947 948

    // The UAS actions are not enabled without connection to system
    ui.actionLiftoff->setEnabled(false);
    ui.actionLand->setEnabled(false);
    ui.actionEmergency_Kill->setEnabled(false);
    ui.actionEmergency_Land->setEnabled(false);
    ui.actionShutdownMAV->setEnabled(false);

    // Connect actions from ui
949
    connect(ui.actionAdd_Link, SIGNAL(triggered()), this, SLOT(manageLinks()));
950 951 952 953 954 955 956 957 958 959 960 961 962

    // Connect internal actions
    connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(UASCreated(UASInterface*)));
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));

    // Unmanned System controls
    connect(ui.actionLiftoff, SIGNAL(triggered()), UASManager::instance(), SLOT(launchActiveUAS()));
    connect(ui.actionLand, SIGNAL(triggered()), UASManager::instance(), SLOT(returnActiveUAS()));
    connect(ui.actionEmergency_Land, SIGNAL(triggered()), UASManager::instance(), SLOT(stopActiveUAS()));
    connect(ui.actionEmergency_Kill, SIGNAL(triggered()), UASManager::instance(), SLOT(killActiveUAS()));
    connect(ui.actionShutdownMAV, SIGNAL(triggered()), UASManager::instance(), SLOT(shutdownActiveUAS()));

    // Views actions
963
    connect(ui.actionFlightView, SIGNAL(triggered()), this, SLOT(loadPilotView()));
964
    connect(ui.actionSimulationView, SIGNAL(triggered()), this, SLOT(loadSimulationView()));
965
    connect(ui.actionEngineersView, SIGNAL(triggered()), this, SLOT(loadEngineerView()));
966
    connect(ui.actionMissionView, SIGNAL(triggered()), this, SLOT(loadOperatorView()));
967
    connect(ui.actionSetup,SIGNAL(triggered()),this,SLOT(loadSetupView()));
968 969
    connect(ui.actionGoogleEarthView, SIGNAL(triggered()), this, SLOT(loadGoogleEarthView()));
    connect(ui.actionLocal3DView, SIGNAL(triggered()), this, SLOT(loadLocal3DView()));
970
    connect(ui.actionTerminalView,SIGNAL(triggered()),this,SLOT(loadTerminalView()));
971 972 973 974

    // Help Actions
    connect(ui.actionOnline_Documentation, SIGNAL(triggered()), this, SLOT(showHelp()));
    connect(ui.actionDeveloper_Credits, SIGNAL(triggered()), this, SLOT(showCredits()));
975
    connect(ui.actionProject_Roadmap, SIGNAL(triggered()), this, SLOT(showRoadMap()));
976 977

    // Custom widget actions
978 979
    connect(ui.actionNewCustomWidget, SIGNAL(triggered()), this, SLOT(_createNewCustomWidget()));
    connect(ui.actionLoadCustomWidgetFile, SIGNAL(triggered()), this, SLOT(_loadCustomWidgetFromFile()));
980 981 982 983 984 985 986 987

    // Audio output
    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)));

    // Application Settings
    connect(ui.actionSettings, SIGNAL(triggered()), this, SLOT(showSettings()));
988 989

    connect(ui.actionSimulate, SIGNAL(triggered(bool)), this, SLOT(simulateLink(bool)));
990 991
}

Don Gagne's avatar
Don Gagne committed
992
void MainWindow::_openUrl(const QString& url, const QString& errorMessage)
993
{
Don Gagne's avatar
Don Gagne committed
994 995 996 997
    if(!QDesktopServices::openUrl(QUrl(url))) {
        QMessageBox::critical(this,
                              tr("Could not open information in browser"),
                              errorMessage);
998 999 1000
    }
}

Don Gagne's avatar
Don Gagne committed
1001 1002 1003 1004 1005 1006
void MainWindow::showHelp()
{
    _openUrl("http://qgroundcontrol.org/users/start",
             tr("To get to the online help, please open http://qgroundcontrol.org/user_guide in a browser."));
}

1007 1008
void MainWindow::showCredits()
{
Don Gagne's avatar
Don Gagne committed
1009 1010
    _openUrl("http://qgroundcontrol.org/credits",
             tr("To get to the credits, please open http://qgroundcontrol.org/credits in a browser."));
1011 1012 1013 1014
}

void MainWindow::showRoadMap()
{
Don Gagne's avatar
Don Gagne committed
1015 1016
    _openUrl("http://qgroundcontrol.org/dev/roadmap",
             tr("To get to the online help, please open http://qgroundcontrol.org/roadmap in a browser."));
1017 1018 1019 1020
}

void MainWindow::showSettings()
{
Don Gagne's avatar
Don Gagne committed
1021 1022
    SettingsDialog settings(joystick, this);
    settings.exec();
1023 1024
}

1025
void MainWindow::simulateLink(bool simulate) {
Don Gagne's avatar
Don Gagne committed
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
    if (simulate) {
        if (!simulationLink) {
            simulationLink = new MAVLinkSimulationLink(":/demo-log.txt");
            Q_CHECK_PTR(simulationLink);
        }
        LinkManager::instance()->connectLink(simulationLink);
    } else {
        Q_ASSERT(simulationLink);
        LinkManager::instance()->disconnectLink(simulationLink);
    }
1036 1037
}

1038 1039
void MainWindow::commsWidgetDestroyed(QObject *obj)
{
1040 1041 1042
    // Do not dynamic cast or de-reference QObject, since object is either in destructor or may have already
    // been destroyed.

1043 1044 1045 1046 1047
    if (commsWidgetList.contains(obj))
    {
        commsWidgetList.removeOne(obj);
    }
}
1048 1049 1050

void MainWindow::setActiveUAS(UASInterface* uas)
{
1051
    Q_UNUSED(uas);
1052 1053
    if (settings.contains(getWindowStateKey()))
    {
1054
        restoreState(settings.value(getWindowStateKey()).toByteArray());
1055 1056
    }

1057 1058 1059 1060
}

void MainWindow::UASSpecsChanged(int uas)
{
1061 1062
    Q_UNUSED(uas);
    // TODO: Update UAS properties if its specs change
1063 1064 1065 1066
}

void MainWindow::UASCreated(UASInterface* uas)
{
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
    // The UAS actions are not enabled without connection to system
    ui.actionLiftoff->setEnabled(true);
    ui.actionLand->setEnabled(true);
    ui.actionEmergency_Kill->setEnabled(true);
    ui.actionEmergency_Land->setEnabled(true);
    ui.actionShutdownMAV->setEnabled(true);

    QIcon icon;
    // Set matching icon
    switch (uas->getSystemType())
    {
    case MAV_TYPE_GENERIC:
        icon = QIcon(":files/images/mavs/generic.svg");
        break;
    case MAV_TYPE_FIXED_WING:
        icon = QIcon(":files/images/mavs/fixed-wing.svg");
        break;
    case MAV_TYPE_QUADROTOR:
        icon = QIcon(":files/images/mavs/quadrotor.svg");
        break;
    case MAV_TYPE_COAXIAL:
        icon = QIcon(":files/images/mavs/coaxial.svg");
        break;
    case MAV_TYPE_HELICOPTER:
        icon = QIcon(":files/images/mavs/helicopter.svg");
        break;
    case MAV_TYPE_ANTENNA_TRACKER:
        icon = QIcon(":files/images/mavs/antenna-tracker.svg");
        break;
    case MAV_TYPE_GCS:
        icon = QIcon(":files/images/mavs/groundstation.svg");
        break;
    case MAV_TYPE_AIRSHIP:
        icon = QIcon(":files/images/mavs/airship.svg");
        break;
    case MAV_TYPE_FREE_BALLOON:
        icon = QIcon(":files/images/mavs/free-balloon.svg");
        break;
    case MAV_TYPE_ROCKET:
        icon = QIcon(":files/images/mavs/rocket.svg");
        break;
    case MAV_TYPE_GROUND_ROVER:
        icon = QIcon(":files/images/mavs/ground-rover.svg");
        break;
    case MAV_TYPE_SURFACE_BOAT:
        icon = QIcon(":files/images/mavs/surface-boat.svg");
        break;
    case MAV_TYPE_SUBMARINE:
        icon = QIcon(":files/images/mavs/submarine.svg");
        break;
    case MAV_TYPE_HEXAROTOR:
        icon = QIcon(":files/images/mavs/hexarotor.svg");
        break;
    case MAV_TYPE_OCTOROTOR:
        icon = QIcon(":files/images/mavs/octorotor.svg");
        break;
    case MAV_TYPE_TRICOPTER:
        icon = QIcon(":files/images/mavs/tricopter.svg");
        break;
    case MAV_TYPE_FLAPPING_WING:
        icon = QIcon(":files/images/mavs/flapping-wing.svg");
        break;
    case MAV_TYPE_KITE:
        icon = QIcon(":files/images/mavs/kite.svg");
        break;
    default:
        icon = QIcon(":files/images/mavs/unknown.svg");
        break;
    }
1136

1137
    connect(uas, SIGNAL(systemSpecsChanged(int)), this, SLOT(UASSpecsChanged(int)));
John Tapsell's avatar
John Tapsell committed
1138
    connect(uas, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)), this, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)));
1139
    connect(uas, SIGNAL(misconfigurationDetected(UASInterface*)), this, SLOT(handleMisconfiguration(UASInterface*)));
1140

1141
    // HIL
1142
    _showHILConfigurationWidgets();
1143

1144 1145 1146
    if (!linechartWidget)
    {
        linechartWidget = new Linecharts(this);
1147
        linechartWidget->setVisible(false);
1148
    }
1149

1150
    linechartWidget->addSource(mavlinkDecoder);
1151
    if (_engineeringView != linechartWidget)
1152
    {
1153
        _engineeringView = linechartWidget;
1154
    }
1155 1156

    // Reload view state in case new widgets were added
1157
    _loadCurrentViewState();
1158 1159 1160 1161
}

void MainWindow::UASDeleted(UASInterface* uas)
{
1162
    Q_UNUSED(uas);
1163
    // TODO: Update the UI when a UAS is deleted
1164 1165
}

1166 1167
/// Stores the state of the toolbar, status bar and widgets associated with the current view
void MainWindow::_storeCurrentViewState(void)
1168
{
1169 1170
    // HIL dock widgets are dynamic and are not part of the saved state
    _hideAllHilDockWidgets();
1171

1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
    // Save list of visible widgets

    bool firstWidget = true;
    QString widgetNames = "";
    foreach(QDockWidget* dockWidget, _mapName2DockWidget) {
        if (dockWidget->isVisible()) {
            if (!firstWidget) {
                widgetNames += ",";
            }
            widgetNames += dockWidget->objectName();
            firstWidget = false;
        }
1184
    }
1185

1186 1187
    settings.setValue(getWindowStateKey() + "WIDGETS", widgetNames);
    settings.setValue(getWindowStateKey(), saveState());
1188
    settings.setValue(getWindowGeometryKey(), saveGeometry());
1189 1190
}

1191 1192
/// Restores the state of the toolbar, status bar and widgets associated with the current view
void MainWindow::_loadCurrentViewState(void)
1193
{
Don Gagne's avatar
Don Gagne committed
1194
    QWidget* centerView = NULL;
1195
    QString defaultWidgets;
1196

1197
    switch (_currentView) {
1198
        case VIEW_SETUP:
1199 1200
            _buildSetupView();
            centerView = _setupView;
1201
            break;
1202

1203
        case VIEW_ENGINEER:
1204 1205 1206
            _buildEngineeringView();
            centerView = _engineeringView;
            defaultWidgets = "MAVLINK_INSPECTOR_DOCKWIDGET,PARAMETER_INTERFACE_DOCKWIDGET,FILE_VIEW_DOCKWIDGET,HEAD_UP_DISPLAY_DOCKWIDGET";
1207
            break;
1208

1209
        case VIEW_FLIGHT:
1210 1211 1212
            _buildPilotView();
            centerView = _pilotView;
            defaultWidgets = "COMMUNICATION_CONSOLE_DOCKWIDGET,UAS_INFO_INFOVIEW_DOCKWIDGET";
1213
            break;
1214

1215
        case VIEW_MISSION:
1216 1217 1218
            _buildPlannerView();
            centerView = _plannerView;
            defaultWidgets = "UNMANNED_SYSTEM_LIST_DOCKWIDGET,WAYPOINT_LIST_DOCKWIDGET";
1219
            break;
1220

1221
        case VIEW_SIMULATION:
1222 1223 1224
            _buildSimView();
            centerView = _simView;
            defaultWidgets = "UNMANNED_SYSTEM_CONTROL_DOCKWIDGET,WAYPOINT_LIST_DOCKWIDGET,PARAMETER_INTERFACE_DOCKWIDGET,PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET";
1225
            break;
1226

1227
        case VIEW_TERMINAL:
1228 1229
            _buildTerminalView();
            centerView = _terminalView;
1230
            break;
1231

1232
        case VIEW_GOOGLEEARTH:
1233 1234
            _buildGoogleEarthView();
            centerView = _googleEarthView;
1235
            break;
1236

1237
        case VIEW_LOCAL3D:
1238 1239
            _buildLocal3DView();
            centerView = _local3DView;
1240
            break;
1241
    }
1242

1243 1244 1245 1246 1247 1248 1249 1250
    // Remove old view
    if (_currentViewWidget) {
        _currentViewWidget->setVisible(false);
        Q_ASSERT(_centralLayout->count() == 1);
        QLayoutItem *child = _centralLayout->takeAt(0);
        Q_ASSERT(child);
        delete child;
    }
1251

1252 1253 1254 1255 1256 1257
    // Add the new one
    Q_ASSERT(centerView);
    Q_ASSERT(_centralLayout->count() == 0);
    _currentViewWidget = centerView;
    _centralLayout->addWidget(_currentViewWidget);
    _currentViewWidget->setVisible(true);
1258

1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
    // Hide all widgets from previous view
    _hideAllDockWidgets();

    // Restore the widgets for the new view
    QString widgetNames = settings.value(getWindowStateKey() + "WIDGETS", defaultWidgets).toString();
    if (!widgetNames.isEmpty()) {
        QStringList split = widgetNames.split(",");
        foreach (QString widgetName, split) {
            Q_ASSERT(!widgetName.isEmpty());
            _showDockWidget(widgetName, true);
1269 1270 1271
        }
    }

1272 1273
    if (settings.contains(getWindowStateKey())) {
        restoreState(settings.value(getWindowStateKey()).toByteArray());
1274
    }
1275

1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
    // HIL dock widget are dynamic and don't take part in the saved window state, so this
    // need to happen after we restore state
    _showHILConfigurationWidgets();
}

void MainWindow::_hideAllHilDockWidgets(void)
{
    foreach(QDockWidget* dockWidget, _mapUasId2HilDockWidget) {
        dockWidget->setVisible(false);
    }
}

void MainWindow::_hideAllDockWidgets(void)
{
    foreach(QDockWidget* dockWidget, _mapName2DockWidget) {
        dockWidget->setVisible(false);
1292
    }
1293

1294
    _hideAllHilDockWidgets();
1295
}
1296 1297

void MainWindow::_showDockWidgetAction(bool show)
1298
{
1299 1300
    QAction* action = dynamic_cast<QAction*>(QObject::sender());
    Q_ASSERT(action);
1301

1302
    _showDockWidget(action->data().toString(), show);
1303
}
1304

1305

1306 1307 1308
void MainWindow::handleMisconfiguration(UASInterface* uas)
{
    static QTime lastTime;
1309

1310 1311 1312 1313 1314 1315 1316 1317 1318
    // We have to debounce this signal
    if (!lastTime.isValid()) {
        lastTime.start();
    } else {
        if (lastTime.elapsed() < 10000) {
            lastTime.start();
            return;
        }
    }
1319

1320
    // Ask user if he wants to handle this now
Don Gagne's avatar
Don Gagne committed
1321 1322 1323 1324 1325
    QMessageBox::StandardButton button = QGCMessageBox::question(tr("Missing or Invalid Onboard Configuration"),
                                                                    tr("The onboard system configuration is missing or incomplete. Do you want to resolve this now?"),
                                                                    QMessageBox::Ok | QMessageBox::Cancel,
                                                                    QMessageBox::Ok);
    if (button == QMessageBox::Ok) {
1326 1327 1328 1329
        // He wants to handle it, make sure this system is selected
        UASManager::instance()->setActiveUAS(uas);

        // Flick to config view
1330
        loadSetupView();
1331 1332 1333
    }
}

1334 1335
void MainWindow::loadEngineerView()
{
1336
    if (_currentView != VIEW_ENGINEER)
1337
    {
1338 1339
        _storeCurrentViewState();
        _currentView = VIEW_ENGINEER;
1340
        ui.actionEngineersView->setChecked(true);
1341
        _loadCurrentViewState();
1342 1343 1344 1345 1346
    }
}

void MainWindow::loadOperatorView()
{
1347
    if (_currentView != VIEW_MISSION)
1348
    {
1349 1350
        _storeCurrentViewState();
        _currentView = VIEW_MISSION;
1351
        ui.actionMissionView->setChecked(true);
1352
        _loadCurrentViewState();
1353 1354
    }
}
1355
void MainWindow::loadSetupView()
1356
{
1357
    if (_currentView != VIEW_SETUP)
1358
    {
1359 1360
        _storeCurrentViewState();
        _currentView = VIEW_SETUP;
1361
        ui.actionSetup->setChecked(true);
1362
        _loadCurrentViewState();
1363 1364 1365
    }
}

1366 1367
void MainWindow::loadTerminalView()
{
1368
    if (_currentView != VIEW_TERMINAL)
1369
    {
1370 1371
        _storeCurrentViewState();
        _currentView = VIEW_TERMINAL;
1372
        ui.actionTerminalView->setChecked(true);
1373
        _loadCurrentViewState();
1374 1375 1376
    }
}

1377 1378
void MainWindow::loadGoogleEarthView()
{
1379
    if (_currentView != VIEW_GOOGLEEARTH)
1380
    {
1381 1382
        _storeCurrentViewState();
        _currentView = VIEW_GOOGLEEARTH;
1383
        ui.actionGoogleEarthView->setChecked(true);
1384
        _loadCurrentViewState();
1385 1386 1387 1388 1389
    }
}

void MainWindow::loadLocal3DView()
{
1390
    if (_currentView != VIEW_LOCAL3D)
1391
    {
1392 1393
        _storeCurrentViewState();
        _currentView = VIEW_LOCAL3D;
1394
        ui.actionLocal3DView->setChecked(true);
1395
        _loadCurrentViewState();
1396 1397
    }
}
1398

1399 1400
void MainWindow::loadPilotView()
{
1401
    if (_currentView != VIEW_FLIGHT)
1402
    {
1403 1404
        _storeCurrentViewState();
        _currentView = VIEW_FLIGHT;
1405
        ui.actionFlightView->setChecked(true);
1406
        _loadCurrentViewState();
1407 1408 1409
    }
}

1410 1411
void MainWindow::loadSimulationView()
{
1412
    if (_currentView != VIEW_SIMULATION)
1413
    {
1414 1415
        _storeCurrentViewState();
        _currentView = VIEW_SIMULATION;
1416
        ui.actionSimulationView->setChecked(true);
1417
        _loadCurrentViewState();
1418 1419 1420
    }
}

Don Gagne's avatar
Don Gagne committed
1421
/// @brief Hides the spash screen if it is currently being shown
1422
void MainWindow::hideSplashScreen(void)
Don Gagne's avatar
Don Gagne committed
1423 1424 1425 1426 1427 1428 1429
{
    if (_splashScreen) {
        _splashScreen->hide();
        _splashScreen = NULL;
    }
}

1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465
void MainWindow::manageLinks()
{
    SettingsDialog settings(joystick, this, SettingsDialog::ShowCommLinks);
    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";
    QString connection;
    if(settings.contains(key)) {
        connection = settings.value(connection).toString();
        // Create a link for it
        LinkInterface* link = LinkManager::instance()->createLink(connection);
        if(link) {
            // Connect it
            LinkManager::instance()->connectLink(link);
        }
    }
}
Don Gagne's avatar
Don Gagne committed
1466

1467
#ifdef QGC_MOUSE_ENABLED_LINUX
1468 1469 1470
bool MainWindow::x11Event(XEvent *event)
{
    emit x11EventOccured(event);
1471
    return false;
1472
}
1473
#endif // QGC_MOUSE_ENABLED_LINUX
1474 1475 1476 1477 1478 1479 1480 1481

#ifdef UNITTEST_BUILD
void MainWindow::_showQmlTestWidget(void)
{
    new QmlTestWidget();
}
#endif