MainWindow.cc 54.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*=====================================================================

QGroundControl Open Source Ground Control Station

(c) 2009 - 2011 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

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

pixhawk's avatar
pixhawk committed
22 23 24 25
======================================================================*/

/**
 * @file
26
 *   @brief Implementation of class MainWindow
27
 *   @author Lorenz Meier <mail@qgroundcontrol.org>
pixhawk's avatar
pixhawk committed
28 29 30 31 32 33 34 35 36
 */

#include <QSettings>
#include <QDockWidget>
#include <QNetworkInterface>
#include <QMessageBox>
#include <QDebug>
#include <QTimer>
#include <QHostInfo>
lm's avatar
lm committed
37
#include <QSplashScreen>
pixhawk's avatar
pixhawk committed
38

39
#include "QGC.h"
pixhawk's avatar
pixhawk committed
40 41 42 43 44
#include "MAVLinkSimulationLink.h"
#include "SerialLink.h"
#include "UDPLink.h"
#include "MAVLinkProtocol.h"
#include "CommConfigurationWindow.h"
45
#include "QGCWaypointListMulti.h"
pixhawk's avatar
pixhawk committed
46 47
#include "MainWindow.h"
#include "JoystickWidget.h"
pixhawk's avatar
pixhawk committed
48
#include "GAudioOutput.h"
49
#include "QGCToolWidget.h"
50
#include "QGCMAVLinkLogPlayer.h"
51
#include "QGCSettingsWidget.h"
52
#include "QGCMapTool.h"
LM's avatar
LM committed
53
#include "MAVLinkDecoder.h"
54

55
#ifdef QGC_OSG_ENABLED
56
#include "Q3DWidgetFactory.h"
57
#endif
pixhawk's avatar
pixhawk committed
58

lm's avatar
lm committed
59 60 61 62
// FIXME Move
#include "PxQuadMAV.h"
#include "SlugsMAV.h"

pixhawk's avatar
pixhawk committed
63

64
#include "LogCompressor.h"
pixhawk's avatar
pixhawk committed
65

lm's avatar
lm committed
66
MainWindow* MainWindow::instance(QSplashScreen* screen)
67
{
68
    static MainWindow* _instance = 0;
69 70
    if(_instance == 0)
    {
71
        _instance = new MainWindow();
lm's avatar
lm committed
72
        if (screen) connect(_instance, SIGNAL(initStatusChanged(QString)), screen, SLOT(showMessage(QString)));
73

74
        /* Set the application as parent to ensure that this object
75
                 * will be destroyed when the main application exits */
76 77 78
        //_instance->setParent(qApp);
    }
    return _instance;
79 80
}

pixhawk's avatar
pixhawk committed
81 82 83 84 85 86 87
/**
* Create new mainwindow. The constructor instantiates all parts of the user
* interface. It does NOT show the mainwindow. To display it, call the show()
* method.
*
* @see QMainWindow::show()
**/
88
MainWindow::MainWindow(QWidget *parent):
89 90
    QMainWindow(parent),
    currentView(VIEW_UNCONNECTED),
91
    currentStyle(QGC_MAINWINDOW_STYLE_INDOOR),
92 93
    aboutToCloseFlag(false),
    changingViewsFlag(false),
94
    centerStackActionGroup(this),
95
    styleFileName(QCoreApplication::applicationDirPath() + "/style-indoor.css"),
96
    autoReconnect(false),
97
    lowPowerMode(false)
pixhawk's avatar
pixhawk committed
98
{
lm's avatar
lm committed
99 100
    hide();
    emit initStatusChanged("Loading UI Settings..");
101
    loadSettings();
102 103
    if (!settings.contains("CURRENT_VIEW"))
    {
104 105
        // Set this view as default view
        settings.setValue("CURRENT_VIEW", currentView);
106 107 108
    }
    else
    {
109 110 111
        // LOAD THE LAST VIEW
        VIEW_SECTIONS currentViewCandidate = (VIEW_SECTIONS) settings.value("CURRENT_VIEW", currentView).toInt();
        if (currentViewCandidate != VIEW_ENGINEER &&
112
                currentViewCandidate != VIEW_OPERATOR &&
113 114 115
                currentViewCandidate != VIEW_PILOT &&
                currentViewCandidate != VIEW_FULL)
        {
116
            currentView = currentViewCandidate;
117
        }
118 119
    }

120 121
    settings.sync();

lm's avatar
lm committed
122
    emit initStatusChanged("Loading Style.");
123
    loadStyle(currentStyle);
124

lm's avatar
lm committed
125 126
    emit initStatusChanged("Setting up user interface.");

pixhawk's avatar
pixhawk committed
127 128
    // Setup user interface
    ui.setupUi(this);
lm's avatar
lm committed
129
    hide();
pixhawk's avatar
pixhawk committed
130

131 132 133 134 135 136
    // Set dock options
    setDockOptions(AnimatedDocks | AllowTabbedDocks | AllowNestedDocks);
    statusBar()->setSizeGripEnabled(true);

    configureWindowName();

137 138 139
    // Setup corners
    setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);

140 141 142
    // Setup UI state machines
    centerStackActionGroup.setExclusive(true);

143 144
    centerStack = new QStackedWidget(this);
    setCentralWidget(centerStack);
145

lm's avatar
lm committed
146 147 148 149 150 151 152 153
    // Load Toolbar
    toolBar = new QGCToolBar(this);
    this->addToolBar(toolBar);
    // Add actions
    toolBar->addPerspectiveChangeAction(ui.actionOperatorsView);
    toolBar->addPerspectiveChangeAction(ui.actionEngineersView);
    toolBar->addPerspectiveChangeAction(ui.actionPilotsView);

lm's avatar
lm committed
154 155
    emit initStatusChanged("Building common widgets.");

156 157
    buildCommonWidgets();
    connectCommonWidgets();
158

lm's avatar
lm committed
159 160
    emit initStatusChanged("Building common actions.");

161
    // Create actions
162
    connectCommonActions();
163

164
    // Populate link menu
lm's avatar
lm committed
165
    emit initStatusChanged("Populating link menu");
166
    QList<LinkInterface*> links = LinkManager::instance()->getLinks();
167 168
    foreach(LinkInterface* link, links)
    {
169 170
        this->addLink(link);
    }
171

172 173
    connect(LinkManager::instance(), SIGNAL(newLink(LinkInterface*)), this, SLOT(addLink(LinkInterface*)));

174
    // Connect user interface devices
lm's avatar
lm committed
175
    emit initStatusChanged("Initializing joystick interface.");
176 177
    joystickWidget = 0;
    joystick = new JoystickInput();
178

179
    // Connect link
180 181
    if (autoReconnect)
    {
182 183 184 185 186 187
        SerialLink* link = new SerialLink();
        // Add to registry
        LinkManager::instance()->add(link);
        LinkManager::instance()->addProtocol(link, mavlink);
        link->connect();
    }
188

189 190 191
    // Set low power mode
    enableLowPowerMode(lowPowerMode);

192 193
    // Initialize window state
    windowStateVal = windowState();
194

lm's avatar
lm committed
195 196
    emit initStatusChanged("Restoring last view state.");

197 198 199
    // Restore the window setup
    loadViewState();

lm's avatar
lm committed
200
    emit initStatusChanged("Restoring last window size.");
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
    // Restore the window position and size
    if (settings.contains(getWindowGeometryKey()))
    {
        // Restore the window geometry
        restoreGeometry(settings.value(getWindowGeometryKey()).toByteArray());
        show();
    }
    else
    {
        // Adjust the size
        const int screenWidth = QApplication::desktop()->width();
        const int screenHeight = QApplication::desktop()->height();

        if (screenWidth < 1200)
        {
            showFullScreen();
        }
        else
        {
            resize(screenWidth*0.67f, qMin(screenHeight, (int)(screenWidth*0.67f*0.67f)));
            show();
        }

    }
225 226 227

    connect(&windowNameUpdateTimer, SIGNAL(timeout()), this, SLOT(configureWindowName()));
    windowNameUpdateTimer.start(15000);
lm's avatar
lm committed
228 229
    emit initStatusChanged("Done.");
    show();
pixhawk's avatar
pixhawk committed
230 231
}

pixhawk's avatar
pixhawk committed
232
MainWindow::~MainWindow()
pixhawk's avatar
pixhawk committed
233
{
234
    delete mavlink;
235
    delete joystick;
lm's avatar
lm committed
236

237 238 239 240 241 242
    // Get and delete all dockwidgets and contained
    // widgets
    QObjectList childList( this->children() );

    QObjectList::iterator i;
    QDockWidget* dockWidget;
243 244
    for (i = childList.begin(); i != childList.end(); ++i)
    {
245
        dockWidget = dynamic_cast<QDockWidget*>(*i);
246 247
        if (dockWidget)
        {
248 249 250 251 252
            // Remove dock widget from main window
            removeDockWidget(dockWidget);
            delete dockWidget->widget();
            delete dockWidget;
        }
LM's avatar
LM committed
253 254 255 256
        else
        {
            delete dynamic_cast<QObject*>(*i);
        }
257 258
    }

LM's avatar
LM committed
259
    // Delete all UAS objects
260 261
}

lm's avatar
lm committed
262 263
void MainWindow::resizeEvent(QResizeEvent * event)
{
264 265
    if (height() < 800)
    {
lm's avatar
lm committed
266
        ui.statusBar->setVisible(false);
267 268 269
    }
    else
    {
lm's avatar
lm committed
270
        ui.statusBar->setVisible(true);
271
        ui.statusBar->setSizeGripEnabled(true);
lm's avatar
lm committed
272
    }
273 274 275 276 277 278 279 280 281 282 283

    if (width() > 1200)
    {
        toolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    }
    else
    {
        toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
    }

    QMainWindow::resizeEvent(event);
lm's avatar
lm committed
284 285
}

286 287
QString MainWindow::getWindowStateKey()
{
288
    return QString::number(currentView)+"_windowstate";
289 290 291 292
}

QString MainWindow::getWindowGeometryKey()
{
293 294
    //return QString::number(currentView)+"_geometry";
    return "_geometry";
295 296
}

lm's avatar
lm committed
297 298
void MainWindow::buildCustomWidget()
{
lm's avatar
lm committed
299 300
    // Create custom widgets
    QList<QGCToolWidget*> widgets = QGCToolWidget::createWidgetsFromSettings(this);
lm's avatar
lm committed
301

lm's avatar
lm committed
302 303 304 305
    if (widgets.size() > 0)
    {
        ui.menuTools->addSeparator();
    }
lm's avatar
lm committed
306

lm's avatar
lm committed
307 308 309 310 311 312
    for(int i = 0; i < widgets.size(); ++i)
    {
        // Check if this widget already has a parent, do not create it in this case
        QGCToolWidget* tool = widgets.at(i);
        QDockWidget* dock = dynamic_cast<QDockWidget*>(tool->parentWidget());
        if (!dock)
313
        {
lm's avatar
lm committed
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
            QDockWidget* dock = new QDockWidget(tool->windowTitle(), this);
            dock->setObjectName(tool->objectName()+"_DOCK");
            dock->setWidget(tool);
            connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater()));
            QAction* showAction = new QAction(widgets.at(i)->windowTitle(), this);
            showAction->setCheckable(true);
            connect(showAction, SIGNAL(triggered(bool)), dock, SLOT(setVisible(bool)));
            connect(dock, SIGNAL(visibilityChanged(bool)), showAction, SLOT(setChecked(bool)));
            widgets.at(i)->setMainMenuAction(showAction);
            ui.menuTools->addAction(showAction);

            // Load dock widget location (default is bottom)
            Qt::DockWidgetArea location = static_cast <Qt::DockWidgetArea>(tool->getDockWidgetArea(currentView));

            addDockWidget(location, dock);
lm's avatar
lm committed
329 330 331 332
        }
    }
}

333 334 335 336
void MainWindow::buildCommonWidgets()
{
    //TODO:  move protocol outside UI
    mavlink     = new MAVLinkProtocol();
337
    connect(mavlink, SIGNAL(protocolStatusMessage(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection);
LM's avatar
LM committed
338 339
    // Add generic MAVLink decoder
    mavlinkDecoder = new MAVLinkDecoder(mavlink, this);
340 341

    // Dock widgets
342 343
    if (!controlDockWidget)
    {
344
        controlDockWidget = new QDockWidget(tr("Control"), this);
345
        controlDockWidget->setObjectName("UNMANNED_SYSTEM_CONTROL_DOCKWIDGET");
346
        controlDockWidget->setWidget( new UASControlWidget(this) );
347
        addTool(controlDockWidget, tr("Control"), Qt::LeftDockWidgetArea);
348
    }
349

350 351
    if (!listDockWidget)
    {
352 353
        listDockWidget = new QDockWidget(tr("Unmanned Systems"), this);
        listDockWidget->setWidget( new UASListWidget(this) );
354
        listDockWidget->setObjectName("UNMANNED_SYSTEMS_LIST_DOCKWIDGET");
355
        addTool(listDockWidget, tr("Unmanned Systems"), Qt::RightDockWidgetArea);
356
    }
357

358 359
    if (!waypointsDockWidget)
    {
360
        waypointsDockWidget = new QDockWidget(tr("Mission Plan"), this);
361
        waypointsDockWidget->setWidget( new QGCWaypointListMulti(this) );
362
        waypointsDockWidget->setObjectName("WAYPOINT_LIST_DOCKWIDGET");
363
        addTool(waypointsDockWidget, tr("Mission Plan"), Qt::BottomDockWidgetArea);
364
    }
365

366 367
    if (!infoDockWidget)
    {
368 369
        infoDockWidget = new QDockWidget(tr("Status Details"), this);
        infoDockWidget->setWidget( new UASInfoWidget(this) );
pixhawk's avatar
pixhawk committed
370
        infoDockWidget->setObjectName("UAS_STATUS_DETAILS_DOCKWIDGET");
371
        addTool(infoDockWidget, tr("Status Details"), Qt::RightDockWidgetArea);
372
    }
373

374 375
    if (!debugConsoleDockWidget)
    {
376 377
        debugConsoleDockWidget = new QDockWidget(tr("Communication Console"), this);
        debugConsoleDockWidget->setWidget( new DebugConsole(this) );
378
        debugConsoleDockWidget->setObjectName("COMMUNICATION_DEBUG_CONSOLE_DOCKWIDGET");
LM's avatar
LM committed
379 380 381 382

        DebugConsole *debugConsole = dynamic_cast<DebugConsole*>(debugConsoleDockWidget->widget());
        connect(mavlinkDecoder, SIGNAL(textMessageReceived(int, int, int, const QString)), debugConsole, SLOT(receiveTextMessage(int, int, int, const QString)));

383
        addTool(debugConsoleDockWidget, tr("Communication Console"), Qt::BottomDockWidgetArea);
384
    }
385

386 387
    if (!logPlayerDockWidget)
    {
388
        logPlayerDockWidget = new QDockWidget(tr("MAVLink Log Player"), this);
lm's avatar
lm committed
389 390 391
        logPlayer = new QGCMAVLinkLogPlayer(mavlink, this);
        toolBar->setLogPlayer(logPlayer);
        logPlayerDockWidget->setWidget(logPlayer);
392
        logPlayerDockWidget->setObjectName("MAVLINK_LOG_PLAYER_DOCKWIDGET");
393
        addTool(logPlayerDockWidget, tr("MAVLink Log Replay"), Qt::RightDockWidgetArea);
394 395
    }

lm's avatar
lm committed
396 397 398 399 400
    if (!mavlinkInspectorWidget)
    {
        mavlinkInspectorWidget = new QDockWidget(tr("MAVLink Message Inspector"), this);
        mavlinkInspectorWidget->setWidget( new QGCMAVLinkInspector(mavlink, this) );
        mavlinkInspectorWidget->setObjectName("MAVLINK_INSPECTOR_DOCKWIDGET");
lm's avatar
lm committed
401
        addTool(mavlinkInspectorWidget, tr("MAVLink Inspector"), Qt::RightDockWidgetArea);
lm's avatar
lm committed
402 403
    }

pixhawk's avatar
pixhawk committed
404 405
    //FIXME: memory of acceptList will never be freed again
    QStringList* acceptList = new QStringList();
pixhawk's avatar
pixhawk committed
406 407 408
    acceptList->append("-105,roll deg,deg,+105,s");
    acceptList->append("-105,pitch deg,deg,+105,s");
    acceptList->append("-105,heading deg,deg,+105,s");
409

pixhawk's avatar
pixhawk committed
410 411
    //FIXME: memory of acceptList2 will never be freed again
    QStringList* acceptList2 = new QStringList();
lm's avatar
lm committed
412
    acceptList2->append("0,abs pressure,hPa,65500");
413

lm's avatar
lm committed
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
    if (!parametersDockWidget) {
        parametersDockWidget = new QDockWidget(tr("Calibration and Onboard Parameters"), this);
        parametersDockWidget->setWidget( new ParameterInterface(this) );
        parametersDockWidget->setObjectName("PARAMETER_INTERFACE_DOCKWIDGET");
        addTool(parametersDockWidget, tr("Calibration and Parameters"), Qt::RightDockWidgetArea);
    }

    if (!hsiDockWidget) {
        hsiDockWidget = new QDockWidget(tr("Horizontal Situation Indicator"), this);
        hsiDockWidget->setWidget( new HSIDisplay(this) );
        hsiDockWidget->setObjectName("HORIZONTAL_SITUATION_INDICATOR_DOCK_WIDGET");
        addTool(hsiDockWidget, tr("Horizontal Situation"), Qt::BottomDockWidgetArea);
    }

    if (!headDown1DockWidget) {
        headDown1DockWidget = new QDockWidget(tr("Flight Display"), this);
430 431 432
        HDDisplay* hdDisplay = new HDDisplay(acceptList, "Flight Display", this);
        hdDisplay->addSource(mavlinkDecoder);
        headDown1DockWidget->setWidget(hdDisplay);
lm's avatar
lm committed
433 434 435 436 437 438
        headDown1DockWidget->setObjectName("HEAD_DOWN_DISPLAY_1_DOCK_WIDGET");
        addTool(headDown1DockWidget, tr("Flight Display"), Qt::RightDockWidgetArea);
    }

    if (!headDown2DockWidget) {
        headDown2DockWidget = new QDockWidget(tr("Actuator Status"), this);
439 440 441
        HDDisplay* hdDisplay = new HDDisplay(acceptList2, "Actuator Status", this);
        hdDisplay->addSource(mavlinkDecoder);
        headDown2DockWidget->setWidget(hdDisplay);
lm's avatar
lm committed
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
        headDown2DockWidget->setObjectName("HEAD_DOWN_DISPLAY_2_DOCK_WIDGET");
        addTool(headDown2DockWidget, tr("Actuator Status"), Qt::RightDockWidgetArea);
    }

    if (!rcViewDockWidget) {
        rcViewDockWidget = new QDockWidget(tr("Radio Control"), this);
        rcViewDockWidget->setWidget( new QGCRemoteControlView(this) );
        rcViewDockWidget->setObjectName("RADIO_CONTROL_CHANNELS_DOCK_WIDGET");
        addTool(rcViewDockWidget, tr("Radio Control"), Qt::BottomDockWidgetArea);
    }

    if (!headUpDockWidget) {
        headUpDockWidget = new QDockWidget(tr("HUD"), this);
        headUpDockWidget->setWidget( new HUD(320, 240, this));
        headUpDockWidget->setObjectName("HEAD_UP_DISPLAY_DOCK_WIDGET");
        addTool(headUpDockWidget, tr("Head Up Display"), Qt::RightDockWidgetArea);
    }

    if (!video1DockWidget) {
        video1DockWidget = new QDockWidget(tr("Video Stream 1"), this);
        HUD* video1 =  new HUD(160, 120, this);
        video1->enableHUDInstruments(false);
        video1->enableVideo(true);
        // FIXME select video stream as well
        video1DockWidget->setWidget(video1);
        video1DockWidget->setObjectName("VIDEO_STREAM_1_DOCK_WIDGET");
        addTool(video1DockWidget, tr("Video Stream 1"), Qt::LeftDockWidgetArea);
    }

    if (!video2DockWidget) {
        video2DockWidget = new QDockWidget(tr("Video Stream 2"), this);
        HUD* video2 =  new HUD(160, 120, this);
        video2->enableHUDInstruments(false);
        video2->enableVideo(true);
        // FIXME select video stream as well
        video2DockWidget->setWidget(video2);
        video2DockWidget->setObjectName("VIDEO_STREAM_2_DOCK_WIDGET");
        addTool(video2DockWidget, tr("Video Stream 2"), Qt::LeftDockWidgetArea);
    }

    // Custom widgets, added last to all menus and layouts
    buildCustomWidget();

    // Center widgets
    if (!mapWidget)
    {
        mapWidget = new QGCMapTool(this);
        addCentralWidget(mapWidget, "Maps");
    }

    if (!protocolWidget)
    {
        protocolWidget    = new XMLCommProtocolWidget(this);
        addCentralWidget(protocolWidget, "Mavlink Generator");
    }

498
    if (!hudWidget) {
499
        hudWidget         = new HUD(320, 240, this);
500
        addCentralWidget(hudWidget, tr("Head Up Display"));
lm's avatar
lm committed
501 502
    }

503
    if (!dataplotWidget) {
504
        dataplotWidget    = new QGCDataPlot2D(this);
505
        addCentralWidget(dataplotWidget, tr("Logfile Plot"));
506
    }
507

508
#ifdef QGC_OSG_ENABLED
509
    if (!_3DWidget) {
510
        _3DWidget         = Q3DWidgetFactory::get("PIXHAWK");
511
        addCentralWidget(_3DWidget, tr("Local 3D"));
512
    }
513
#endif
514

515
#if (defined _MSC_VER) | (defined Q_OS_MAC)
516
    if (!gEarthWidget) {
517
        gEarthWidget = new QGCGoogleEarthView(this);
518
        addCentralWidget(gEarthWidget, tr("Google Earth"));
519
    }
520
#endif
lm's avatar
lm committed
521
}
522

523
void MainWindow::addTool(QDockWidget* widget, const QString& title, Qt::DockWidgetArea area)
lm's avatar
lm committed
524
{
525
    QAction* tempAction = ui.menuTools->addAction(title);
526

527
    tempAction->setCheckable(true);
528 529 530 531
    QVariant var;
    var.setValue((QWidget*)widget);
    tempAction->setData(var);
    connect(tempAction,SIGNAL(triggered(bool)),this, SLOT(showTool(bool)));
LM's avatar
LM committed
532 533
    connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool)));
    tempAction->setChecked(widget->isVisible());
534
    addDockWidget(area, widget);
535 536 537
}


538
void MainWindow::showTool(bool show)
lm's avatar
lm committed
539
{
540 541 542
    QAction* act = qobject_cast<QAction *>(sender());
    QWidget* widget = qVariantValue<QWidget *>(act->data());
    widget->setVisible(show);
543
}
544

545
void MainWindow::addCentralWidget(QWidget* widget, const QString& title)
lm's avatar
lm committed
546
{
547 548 549 550
    // Check if this widget already has been added
    if (centerStack->indexOf(widget) == -1)
    {
        centerStack->addWidget(widget);
551

552
        QAction* tempAction = ui.menuMain->addAction(title);
553

554 555 556 557 558 559
        tempAction->setCheckable(true);
        QVariant var;
        var.setValue((QWidget*)widget);
        tempAction->setData(var);
        centerStackActionGroup.addAction(tempAction);
        connect(tempAction,SIGNAL(triggered()),this, SLOT(showCentralWidget()));
LM's avatar
LM committed
560 561
        connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool)));
        tempAction->setChecked(widget->isVisible());
562 563 564 565
    }
}


566
void MainWindow::showCentralWidget()
567
{
568 569 570
    QAction* act = qobject_cast<QAction *>(sender());
    QWidget* widget = qVariantValue<QWidget *>(act->data());
    centerStack->setCurrentWidget(widget);
571 572
}

573 574
void MainWindow::closeEvent(QCloseEvent *event)
{
pixhawk's avatar
pixhawk committed
575
    if (isVisible()) storeViewState();
576
    storeSettings();
577
    aboutToCloseFlag = true;
578
    mavlink->storeSettings();
579
    UASManager::instance()->storeSettings();
580 581
    QMainWindow::closeEvent(event);
}
582 583 584 585

/**
 * Connect the signals and slots of the common window widgets
 */
586
void MainWindow::connectCommonWidgets()
587
{
588
    if (infoDockWidget && infoDockWidget->widget()) {
pixhawk's avatar
pixhawk committed
589 590 591
        connect(mavlink, SIGNAL(receiveLossChanged(int, float)),
                infoDockWidget->widget(), SLOT(updateSendLoss(int, float)));
    }
592
}
593

594 595
void MainWindow::createCustomWidget()
{
596 597
    QDockWidget* dock = new QDockWidget("Unnamed Tool", this);
    QGCToolWidget* tool = new QGCToolWidget("Unnamed Tool", dock);
lm's avatar
lm committed
598

LM's avatar
LM committed
599 600
    if (QGCToolWidget::instances()->size() < 2)
    {
lm's avatar
lm committed
601 602 603 604 605
        // This is the first widget
        ui.menuTools->addSeparator();
    }

    connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater()));
606
    dock->setWidget(tool);
607

608 609 610 611 612 613 614 615 616 617 618 619 620 621
    QAction* showAction = new QAction(tool->getTitle(), this);
    showAction->setCheckable(true);
    connect(dock, SIGNAL(visibilityChanged(bool)), showAction, SLOT(setChecked(bool)));
    connect(showAction, SIGNAL(triggered(bool)), dock, SLOT(setVisible(bool)));
    tool->setMainMenuAction(showAction);
    ui.menuTools->addAction(showAction);
    this->addDockWidget(Qt::BottomDockWidgetArea, dock);
    dock->setVisible(true);
}

void MainWindow::loadCustomWidget()
{
    QString widgetFileExtension(".qgw");
    QString fileName = QFileDialog::getOpenFileName(this, tr("Specify Widget File Name"), QDesktopServices::storageLocation(QDesktopServices::DesktopLocation), tr("QGroundControl Widget (*%1);;").arg(widgetFileExtension));
622
    if (fileName != "") loadCustomWidget(fileName);
LM's avatar
LM committed
623
}
624

LM's avatar
LM committed
625 626 627 628
void MainWindow::loadCustomWidget(const QString& fileName, bool singleinstance)
{
    QGCToolWidget* tool = new QGCToolWidget("", this);
    if (tool->loadSettings(fileName, true) || !singleinstance)
LM's avatar
LM committed
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
    {
        // Add widget to UI
        QDockWidget* dock = new QDockWidget(tool->getTitle(), this);
        connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater()));
        dock->setWidget(tool);
        tool->setParent(dock);

        QAction* showAction = new QAction(tool->getTitle(), this);
        showAction->setCheckable(true);
        connect(dock, SIGNAL(visibilityChanged(bool)), showAction, SLOT(setChecked(bool)));
        connect(showAction, SIGNAL(triggered(bool)), dock, SLOT(setVisible(bool)));
        tool->setMainMenuAction(showAction);
        ui.menuTools->addAction(showAction);
        this->addDockWidget(Qt::BottomDockWidgetArea, dock);
        dock->setVisible(true);
    }
LM's avatar
LM committed
645 646 647
    else
    {
        return;
648
    }
649 650
}

LM's avatar
LM committed
651
void MainWindow::loadCustomWidgetsFromDefaults(const QString& systemType, const QString& autopilotType)
652
{
653
    QString defaultsDir = qApp->applicationDirPath() + "/files/" + autopilotType.toLower() + "/" + systemType.toLower() + "/widgets/";
654

LM's avatar
LM committed
655 656 657 658 659 660
    QDir widgets(defaultsDir);
    QStringList files = widgets.entryList();
    if (files.count() == 0)
    {
        qDebug() << "No default custom widgets for system " << systemType << "autopilot" << autopilotType << " found";
        qDebug() << "Tried with path: " << defaultsDir;
lm's avatar
lm committed
661
        showStatusMessage(tr("Did not find any custom widgets in %1").arg(defaultsDir));
662
    }
663

LM's avatar
LM committed
664 665
    // Load all custom widgets found in the AP folder
    for(int i = 0; i < files.count(); ++i)
lm's avatar
lm committed
666
    {
LM's avatar
LM committed
667 668 669 670 671
        QString file = files[i];
        if (file.endsWith(".qgw"))
        {
            // Will only be loaded if not already a custom widget with
            // the same name is present
672
            loadCustomWidget(defaultsDir+"/"+file, true);
lm's avatar
lm committed
673
            showStatusMessage(tr("Loaded custom widget %1").arg(defaultsDir+"/"+file));
LM's avatar
LM committed
674
        }
lm's avatar
lm committed
675
    }
676 677
}

678 679 680 681 682 683
void MainWindow::loadSettings()
{
    QSettings settings;
    settings.beginGroup("QGC_MAINWINDOW");
    autoReconnect = settings.value("AUTO_RECONNECT", autoReconnect).toBool();
    currentStyle = (QGC_MAINWINDOW_STYLE)settings.value("CURRENT_STYLE", currentStyle).toInt();
684
    lowPowerMode = settings.value("LOW_POWER_MODE", lowPowerMode).toBool();
685 686 687 688 689 690 691 692 693 694
    settings.endGroup();
}

void MainWindow::storeSettings()
{
    QSettings settings;
    settings.beginGroup("QGC_MAINWINDOW");
    settings.setValue("AUTO_RECONNECT", autoReconnect);
    settings.setValue("CURRENT_STYLE", currentStyle);
    settings.endGroup();
pixhawk's avatar
pixhawk committed
695 696 697 698 699 700 701 702 703 704 705
    if (!aboutToCloseFlag && isVisible())
    {
        settings.setValue(getWindowGeometryKey(), saveGeometry());
        // Save the last current view in any case
        settings.setValue("CURRENT_VIEW", currentView);
        // 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(QGC::applicationVersion()));
        // Save the current view only if a UAS is connected
        if (UASManager::instance()->getUASList().length() > 0) settings.setValue("CURRENT_VIEW_WITH_UAS_CONNECTED", currentView);
        // Save the current power mode
    }
706
    settings.setValue("LOW_POWER_MODE", lowPowerMode);
707 708 709
    settings.sync();
}

710 711
void MainWindow::configureWindowName()
{
pixhawk's avatar
pixhawk committed
712 713 714
    QList<QHostAddress> hostAddresses = QNetworkInterface::allAddresses();
    QString windowname = qApp->applicationName() + " " + qApp->applicationVersion();
    bool prevAddr = false;
715

pixhawk's avatar
pixhawk committed
716
    windowname.append(" (" + QHostInfo::localHostName() + ": ");
717

718 719
    for (int i = 0; i < hostAddresses.size(); i++)
    {
pixhawk's avatar
pixhawk committed
720
        // Exclude loopback IPv4 and all IPv6 addresses
721 722
        if (hostAddresses.at(i) != QHostAddress("127.0.0.1") && !hostAddresses.at(i).toString().contains(":"))
        {
pixhawk's avatar
pixhawk committed
723 724 725 726 727
            if(prevAddr) windowname.append("/");
            windowname.append(hostAddresses.at(i).toString());
            prevAddr = true;
        }
    }
728

pixhawk's avatar
pixhawk committed
729
    windowname.append(")");
730

pixhawk's avatar
pixhawk committed
731
    setWindowTitle(windowname);
732 733

#ifndef Q_WS_MAC
pixhawk's avatar
pixhawk committed
734
    //qApp->setWindowIcon(QIcon(":/core/images/qtcreator_logo_128.png"));
735 736 737
#endif
}

pixhawk's avatar
pixhawk committed
738
void MainWindow::startVideoCapture()
pixhawk's avatar
pixhawk committed
739 740 741 742 743
{
    QString format = "bmp";
    QString initialPath = QDir::currentPath() + tr("/untitled.") + format;

    QString screenFileName = QFileDialog::getSaveFileName(this, tr("Save As"),
lm's avatar
lm committed
744 745 746 747
                                                          initialPath,
                                                          tr("%1 Files (*.%2);;All Files (*)")
                                                          .arg(format.toUpper())
                                                          .arg(format));
pixhawk's avatar
pixhawk committed
748 749
    delete videoTimer;
    videoTimer = new QTimer(this);
750 751 752
    //videoTimer->setInterval(40);
    //connect(videoTimer, SIGNAL(timeout()), this, SLOT(saveScreen()));
    //videoTimer->stop();
pixhawk's avatar
pixhawk committed
753 754
}

pixhawk's avatar
pixhawk committed
755
void MainWindow::stopVideoCapture()
pixhawk's avatar
pixhawk committed
756 757 758 759 760 761
{
    videoTimer->stop();

    // TODO Convert raw images to PNG
}

pixhawk's avatar
pixhawk committed
762
void MainWindow::saveScreen()
pixhawk's avatar
pixhawk committed
763 764 765 766
{
    QPixmap window = QPixmap::grabWindow(this->winId());
    QString format = "bmp";

767 768
    if (!screenFileName.isEmpty())
    {
pixhawk's avatar
pixhawk committed
769 770 771 772
        window.save(screenFileName, format.toAscii());
    }
}

773
void MainWindow::enableAutoReconnect(bool enabled)
pixhawk's avatar
pixhawk committed
774
{
775 776
    autoReconnect = enabled;
}
777

778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
void MainWindow::loadNativeStyle()
{
    loadStyle(QGC_MAINWINDOW_STYLE_NATIVE);
}

void MainWindow::loadIndoorStyle()
{
    loadStyle(QGC_MAINWINDOW_STYLE_INDOOR);
}

void MainWindow::loadOutdoorStyle()
{
    loadStyle(QGC_MAINWINDOW_STYLE_OUTDOOR);
}

void MainWindow::loadStyle(QGC_MAINWINDOW_STYLE style)
{
795 796 797 798 799 800 801 802 803
    switch (style) {
    case QGC_MAINWINDOW_STYLE_NATIVE: {
        // Native mode means setting no style
        // so if we were already in native mode
        // take no action
        // Only if a style was set, remove it.
        if (style != currentStyle) {
            qApp->setStyleSheet("");
            showInfoMessage(tr("Please restart QGroundControl"), tr("Please restart QGroundControl to switch to fully native look and feel. Currently you have loaded Qt's plastique style."));
804
        }
805 806
    }
    break;
807 808 809 810 811 812 813 814 815 816 817 818 819 820
    case QGC_MAINWINDOW_STYLE_INDOOR:
        qApp->setStyle("plastique");
        styleFileName = ":/images/style-mission.css";
        reloadStylesheet();
        break;
    case QGC_MAINWINDOW_STYLE_OUTDOOR:
        qApp->setStyle("plastique");
        styleFileName = ":/images/style-outdoor.css";
        reloadStylesheet();
        break;
    }
    currentStyle = style;
}

821
void MainWindow::selectStylesheet()
pixhawk's avatar
pixhawk committed
822
{
823
    // Let user select style sheet
824
    styleFileName = QFileDialog::getOpenFileName(this, tr("Specify stylesheet"), styleFileName, tr("CSS Stylesheet (*.css);;"));
825

826
    if (!styleFileName.endsWith(".css")) {
827 828 829 830 831 832 833 834 835 836
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Information);
        msgBox.setText(tr("QGroundControl did lot load a new style"));
        msgBox.setInformativeText(tr("No suitable .css file selected. Please select a valid .css file."));
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
        return;
    }

pixhawk's avatar
pixhawk committed
837
    // Load style sheet
838 839 840 841 842 843 844
    reloadStylesheet();
}

void MainWindow::reloadStylesheet()
{
    // Load style sheet
    QFile* styleSheet = new QFile(styleFileName);
845
    if (!styleSheet->exists()) {
846 847
        styleSheet = new QFile(":/images/style-mission.css");
    }
848
    if (styleSheet->open(QIODevice::ReadOnly | QIODevice::Text)) {
849 850
        QString style = QString(styleSheet->readAll());
        style.replace("ICONDIR", QCoreApplication::applicationDirPath()+ "/images/");
pixhawk's avatar
pixhawk committed
851
        qApp->setStyleSheet(style);
852
    } else {
853 854 855
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Information);
        msgBox.setText(tr("QGroundControl did lot load a new style"));
856
        msgBox.setInformativeText(tr("Stylesheet file %1 was not readable").arg(styleFileName));
857 858 859
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
pixhawk's avatar
pixhawk committed
860
    }
861
    delete styleSheet;
pixhawk's avatar
pixhawk committed
862 863
}

864 865 866 867 868 869
/**
 * The status message will be overwritten if a new message is posted to this function
 *
 * @param status message text
 * @param timeout how long the status should be displayed
 */
pixhawk's avatar
pixhawk committed
870
void MainWindow::showStatusMessage(const QString& status, int timeout)
pixhawk's avatar
pixhawk committed
871
{
lm's avatar
lm committed
872
    statusBar()->showMessage(status, timeout);
pixhawk's avatar
pixhawk committed
873 874
}

875 876 877 878 879 880
/**
 * The status message will be overwritten if a new message is posted to this function.
 * it will be automatically hidden after 5 seconds.
 *
 * @param status message text
 */
881
void MainWindow::showStatusMessage(const QString& status)
pixhawk's avatar
pixhawk committed
882
{
lm's avatar
lm committed
883
    statusBar()->showMessage(status, 20000);
884 885 886 887
}

void MainWindow::showCriticalMessage(const QString& title, const QString& message)
{
888
    QMessageBox msgBox(this);
889 890 891 892 893 894
    msgBox.setIcon(QMessageBox::Critical);
    msgBox.setText(title);
    msgBox.setInformativeText(message);
    msgBox.setStandardButtons(QMessageBox::Ok);
    msgBox.setDefaultButton(QMessageBox::Ok);
    msgBox.exec();
pixhawk's avatar
pixhawk committed
895 896
}

lm's avatar
lm committed
897 898
void MainWindow::showInfoMessage(const QString& title, const QString& message)
{
899
    QMessageBox msgBox(this);
lm's avatar
lm committed
900 901 902 903 904 905 906 907
    msgBox.setIcon(QMessageBox::Information);
    msgBox.setText(title);
    msgBox.setInformativeText(message);
    msgBox.setStandardButtons(QMessageBox::Ok);
    msgBox.setDefaultButton(QMessageBox::Ok);
    msgBox.exec();
}

pixhawk's avatar
pixhawk committed
908 909 910 911
/**
* @brief Create all actions associated to the main window
*
**/
912
void MainWindow::connectCommonActions()
pixhawk's avatar
pixhawk committed
913
{
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936
    // Bind together the perspective actions
    QActionGroup* perspectives = new QActionGroup(ui.menuPerspectives);
    perspectives->addAction(ui.actionEngineersView);
    perspectives->addAction(ui.actionMavlinkView);
    perspectives->addAction(ui.actionPilotsView);
    perspectives->addAction(ui.actionOperatorsView);
    perspectives->addAction(ui.actionUnconnectedView);
    perspectives->setExclusive(true);

    // Mark the right one as selected
    if (currentView == VIEW_ENGINEER) ui.actionEngineersView->setChecked(true);
    if (currentView == VIEW_MAVLINK) ui.actionMavlinkView->setChecked(true);
    if (currentView == VIEW_PILOT) ui.actionPilotsView->setChecked(true);
    if (currentView == VIEW_OPERATOR) ui.actionOperatorsView->setChecked(true);
    if (currentView == VIEW_UNCONNECTED) ui.actionUnconnectedView->setChecked(true);

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

pixhawk's avatar
pixhawk committed
937 938 939 940 941
    // Connect actions from ui
    connect(ui.actionAdd_Link, SIGNAL(triggered()), this, SLOT(addLink()));

    // Connect internal actions
    connect(UASManager::instance(), SIGNAL(UASCreated(UASInterface*)), this, SLOT(UASCreated(UASInterface*)));
942
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
pixhawk's avatar
pixhawk committed
943

944
    // Unmanned System controls
pixhawk's avatar
pixhawk committed
945 946 947 948
    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()));
949
    connect(ui.actionShutdownMAV, SIGNAL(triggered()), UASManager::instance(), SLOT(shutdownActiveUAS()));
pixhawk's avatar
pixhawk committed
950 951
    connect(ui.actionConfiguration, SIGNAL(triggered()), UASManager::instance(), SLOT(configureActiveUAS()));

952
    // Views actions
953
    connect(ui.actionPilotsView, SIGNAL(triggered()), this, SLOT(loadPilotView()));
954
    connect(ui.actionEngineersView, SIGNAL(triggered()), this, SLOT(loadEngineerView()));
955
    connect(ui.actionOperatorsView, SIGNAL(triggered()), this, SLOT(loadOperatorView()));
956
    connect(ui.actionUnconnectedView, SIGNAL(triggered()), this, SLOT(loadUnconnectedView()));
957

958
    connect(ui.actionMavlinkView, SIGNAL(triggered()), this, SLOT(loadMAVLinkView()));
959 960
    connect(ui.actionReloadStylesheet, SIGNAL(triggered()), this, SLOT(reloadStylesheet()));
    connect(ui.actionSelectStylesheet, SIGNAL(triggered()), this, SLOT(selectStylesheet()));
961

962
    // Help Actions
963
    connect(ui.actionOnline_Documentation, SIGNAL(triggered()), this, SLOT(showHelp()));
964
    connect(ui.actionDeveloper_Credits, SIGNAL(triggered()), this, SLOT(showCredits()));
965
    connect(ui.actionProject_Roadmap_2, SIGNAL(triggered()), this, SLOT(showRoadMap()));
966 967 968

    // Custom widget actions
    connect(ui.actionNewCustomWidget, SIGNAL(triggered()), this, SLOT(createCustomWidget()));
969
    connect(ui.actionLoadCustomWidgetFile, SIGNAL(triggered()), this, SLOT(loadCustomWidget()));
970 971 972

    // Audio output
    ui.actionMuteAudioOutput->setChecked(GAudioOutput::instance()->isMuted());
973
    connect(GAudioOutput::instance(), SIGNAL(mutedChanged(bool)), ui.actionMuteAudioOutput, SLOT(setChecked(bool)));
974
    connect(ui.actionMuteAudioOutput, SIGNAL(triggered(bool)), GAudioOutput::instance(), SLOT(mute(bool)));
975 976 977 978 979 980 981

    // User interaction
    // NOTE: Joystick thread is not started and
    // configuration widget is not instantiated
    // unless it is actually used
    // so no ressources spend on this.
    ui.actionJoystickSettings->setVisible(true);
982 983 984

    // Configuration
    // Joystick
985
    connect(ui.actionJoystickSettings, SIGNAL(triggered()), this, SLOT(configure()));
986 987
    // Application Settings
    connect(ui.actionSettings, SIGNAL(triggered()), this, SLOT(showSettings()));
988 989
}

990 991
void MainWindow::showHelp()
{
lm's avatar
lm committed
992
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/users/start"))) {
993 994 995 996 997 998 999 1000 1001 1002 1003 1004
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Critical);
        msgBox.setText("Could not open help in browser");
        msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/user_guide in a browser.");
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
    }
}

void MainWindow::showCredits()
{
1005
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/credits"))) {
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Critical);
        msgBox.setText("Could not open credits in browser");
        msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/credits in a browser.");
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
    }
}

void MainWindow::showRoadMap()
{
lm's avatar
lm committed
1018
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/dev/roadmap"))) {
1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Critical);
        msgBox.setText("Could not open roadmap in browser");
        msgBox.setInformativeText("To get to the online help, please open http://qgroundcontrol.org/roadmap in a browser.");
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
    }
}

pixhawk's avatar
pixhawk committed
1029
void MainWindow::configure()
pixhawk's avatar
pixhawk committed
1030
{
1031 1032
    if (!joystickWidget) {
        if (!joystick->isRunning()) {
1033 1034 1035 1036 1037
            joystick->start();
        }
        joystickWidget = new JoystickWidget(joystick);
    }
    joystickWidget->show();
pixhawk's avatar
pixhawk committed
1038 1039
}

1040 1041 1042 1043 1044 1045
void MainWindow::showSettings()
{
    QGCSettingsWidget* settings = new QGCSettingsWidget(this);
    settings->show();
}

pixhawk's avatar
pixhawk committed
1046
void MainWindow::addLink()
pixhawk's avatar
pixhawk committed
1047 1048 1049 1050
{
    SerialLink* link = new SerialLink();
    // TODO This should be only done in the dialog itself

1051
    LinkManager::instance()->add(link);
pixhawk's avatar
pixhawk committed
1052 1053
    LinkManager::instance()->addProtocol(link, mavlink);

1054 1055
    // Go fishing for this link's configuration window
    QList<QAction*> actions = ui.menuNetwork->actions();
pixhawk's avatar
pixhawk committed
1056

oberion's avatar
oberion committed
1057 1058
	const int32_t& linkIndex(LinkManager::instance()->getLinks().indexOf(link));
	const int32_t& linkID(LinkManager::instance()->getLinks()[linkIndex]->getId());
oberion's avatar
oberion committed
1059

1060
    foreach (QAction* act, actions) {
oberion's avatar
oberion committed
1061
        if (act->data().toInt() == linkID) { // LinkManager::instance()->getLinks().indexOf(link)
1062 1063 1064 1065
            act->trigger();
            break;
        }
    }
pixhawk's avatar
pixhawk committed
1066 1067
}

pixhawk's avatar
pixhawk committed
1068 1069
void MainWindow::addLink(LinkInterface *link)
{
1070 1071 1072 1073
    // IMPORTANT! KEEP THESE TWO LINES
    // THEY MAKE SURE THE LINK IS PROPERLY REGISTERED
    // BEFORE LINKING THE UI AGAINST IT
    // Register (does nothing if already registered)
1074
    LinkManager::instance()->add(link);
1075
    LinkManager::instance()->addProtocol(link, mavlink);
1076

1077 1078
    // Go fishing for this link's configuration window
    QList<QAction*> actions = ui.menuNetwork->actions();
pixhawk's avatar
pixhawk committed
1079

oberion's avatar
oberion committed
1080
    bool found(false);
1081

oberion's avatar
oberion committed
1082 1083
	const int32_t& linkIndex(LinkManager::instance()->getLinks().indexOf(link));
	const int32_t& linkID(LinkManager::instance()->getLinks()[linkIndex]->getId());
1084

1085
    foreach (QAction* act, actions) {
oberion's avatar
oberion committed
1086
        if (act->data().toInt() == linkID) { // LinkManager::instance()->getLinks().indexOf(link)
1087 1088 1089 1090
            found = true;
        }
    }

oberion's avatar
oberion committed
1091
    //UDPLink* udp = dynamic_cast<UDPLink*>(link);
1092

oberion's avatar
oberion committed
1093
    if (!found) {  //  || udp
1094 1095 1096 1097 1098 1099 1100 1101
        CommConfigurationWindow* commWidget = new CommConfigurationWindow(link, mavlink, this);
        QAction* action = commWidget->getAction();
        ui.menuNetwork->addAction(action);

        // Error handling
        connect(link, SIGNAL(communicationError(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection);
        // Special case for simulationlink
        MAVLinkSimulationLink* sim = dynamic_cast<MAVLinkSimulationLink*>(link);
1102 1103
        if (sim)
        {
1104 1105 1106
            //connect(sim, SIGNAL(valueChanged(int,QString,double,quint64)), linechart, SLOT(appendData(int,QString,double,quint64)));
            connect(ui.actionSimulate, SIGNAL(triggered(bool)), sim, SLOT(connectLink(bool)));
        }
pixhawk's avatar
pixhawk committed
1107 1108 1109
    }
}

1110 1111 1112 1113 1114 1115 1116
void MainWindow::setActiveUAS(UASInterface* uas)
{
    // Enable and rename menu
    ui.menuUnmanned_System->setTitle(uas->getUASName());
    if (!ui.menuUnmanned_System->isEnabled()) ui.menuUnmanned_System->setEnabled(true);
}

1117 1118 1119
void MainWindow::UASSpecsChanged(int uas)
{
    UASInterface* activeUAS = UASManager::instance()->getActiveUAS();
1120 1121 1122 1123
    if (activeUAS)
    {
        if (activeUAS->getUASID() == uas)
        {
1124 1125 1126 1127 1128
            ui.menuUnmanned_System->setTitle(activeUAS->getUASName());
        }
    }
}

pixhawk's avatar
pixhawk committed
1129
void MainWindow::UASCreated(UASInterface* uas)
pixhawk's avatar
pixhawk committed
1130
{
1131

pixhawk's avatar
pixhawk committed
1132 1133
    // Connect the UAS to the full user interface

lm's avatar
lm committed
1134 1135
    if (uas != NULL)
    {
1136
        // The pilot, operator and engineer views were not available on startup, enable them now
1137
        ui.actionPilotsView->setEnabled(true);
1138 1139
        ui.actionOperatorsView->setEnabled(true);
        ui.actionEngineersView->setEnabled(true);
1140 1141 1142 1143 1144 1145
        // 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);
1146

1147 1148
        QIcon icon;
        // Set matching icon
1149
        switch (uas->getSystemType()) {
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
        case 0:
            icon = QIcon(":/images/mavs/generic.svg");
            break;
        case 1:
            icon = QIcon(":/images/mavs/fixed-wing.svg");
            break;
        case 2:
            icon = QIcon(":/images/mavs/quadrotor.svg");
            break;
        case 3:
            icon = QIcon(":/images/mavs/coaxial.svg");
            break;
        case 4:
            icon = QIcon(":/images/mavs/helicopter.svg");
            break;
        case 5:
            icon = QIcon(":/images/mavs/groundstation.svg");
            break;
        default:
            icon = QIcon(":/images/mavs/unknown.svg");
            break;
        }

1173 1174 1175
        QAction* uasAction = new QAction(icon, tr("Select %1 for control").arg(uas->getUASName()), ui.menuConnected_Systems);
        connect(uas, SIGNAL(systemRemoved()), uasAction, SLOT(deleteLater()));
        connect(uasAction, SIGNAL(triggered()), uas, SLOT(setSelected()));
1176
        connect(uas, SIGNAL(systemSpecsChanged(int)), this, SLOT(UASSpecsChanged(int)));
1177 1178

        ui.menuConnected_Systems->addAction(uasAction);
1179 1180

        // FIXME Should be not inside the mainwindow
1181 1182
        if (debugConsoleDockWidget)
        {
pixhawk's avatar
pixhawk committed
1183
            DebugConsole *debugConsole = dynamic_cast<DebugConsole*>(debugConsoleDockWidget->widget());
1184 1185
            if (debugConsole)
            {
pixhawk's avatar
pixhawk committed
1186 1187 1188
                connect(uas, SIGNAL(textMessageReceived(int,int,int,QString)),
                        debugConsole, SLOT(receiveTextMessage(int,int,int,QString)));
            }
1189
        }
1190 1191

        // Health / System status indicator
1192 1193
        if (infoDockWidget)
        {
pixhawk's avatar
pixhawk committed
1194
            UASInfoWidget *infoWidget = dynamic_cast<UASInfoWidget*>(infoDockWidget->widget());
1195 1196
            if (infoWidget)
            {
pixhawk's avatar
pixhawk committed
1197 1198
                infoWidget->addUAS(uas);
            }
1199
        }
1200 1201

        // UAS List
lm's avatar
lm committed
1202 1203
        if (listDockWidget)
        {
pixhawk's avatar
pixhawk committed
1204
            UASListWidget *listWidget = dynamic_cast<UASListWidget*>(listDockWidget->widget());
lm's avatar
lm committed
1205 1206
            if (listWidget)
            {
pixhawk's avatar
pixhawk committed
1207 1208
                listWidget->addUAS(uas);
            }
1209
        }
1210

1211 1212
        // Line chart
        if (!linechartWidget)
lm's avatar
lm committed
1213
        {
1214
            // Center widgets
LM's avatar
LM committed
1215
            linechartWidget = new Linecharts(this);
1216
            linechartWidget->addSource(mavlinkDecoder);
1217
            addCentralWidget(linechartWidget, tr("Realtime Plot"));
1218
        }
1219

LM's avatar
LM committed
1220
        // Load default custom widgets for this autopilot type
lm's avatar
lm committed
1221
        loadCustomWidgetsFromDefaults(uas->getSystemTypeName(), uas->getAutopilotTypeName());
1222 1223


lm's avatar
lm committed
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
        if (uas->getAutopilotType() == MAV_AUTOPILOT_PIXHAWK)
        {
            // Dock widgets
            if (!detectionDockWidget)
            {
                detectionDockWidget = new QDockWidget(tr("Object Recognition"), this);
                detectionDockWidget->setWidget( new ObjectDetectionView("images/patterns", this) );
                detectionDockWidget->setObjectName("OBJECT_DETECTION_DOCK_WIDGET");
                addTool(detectionDockWidget, tr("Object Recognition"), Qt::RightDockWidgetArea);
            }

1235 1236
            if (!watchdogControlDockWidget)
            {
lm's avatar
lm committed
1237 1238 1239 1240 1241
                watchdogControlDockWidget = new QDockWidget(tr("Process Control"), this);
                watchdogControlDockWidget->setWidget( new WatchdogControl(this) );
                watchdogControlDockWidget->setObjectName("WATCHDOG_CONTROL_DOCKWIDGET");
                addTool(watchdogControlDockWidget, tr("Process Control"), Qt::BottomDockWidgetArea);
            }
1242 1243 1244 1245 1246 1247
        }

        // Change the view only if this is the first UAS

        // If this is the first connected UAS, it is both created as well as
        // the currently active UAS
1248 1249
        if (UASManager::instance()->getUASList().size() == 1)
        {
1250
            // Load last view if setting is present
1251 1252
            if (settings.contains("CURRENT_VIEW_WITH_UAS_CONNECTED"))
            {
1253
                int view = settings.value("CURRENT_VIEW_WITH_UAS_CONNECTED").toInt();
1254
                switch (view) {
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270
                case VIEW_ENGINEER:
                    loadEngineerView();
                    break;
                case VIEW_MAVLINK:
                    loadMAVLinkView();
                    break;
                case VIEW_PILOT:
                    loadPilotView();
                    break;
                case VIEW_UNCONNECTED:
                    loadUnconnectedView();
                    break;
                case VIEW_OPERATOR:
                default:
                    loadOperatorView();
                    break;
1271
                }
1272 1273 1274
            }
            else
            {
1275
                loadOperatorView();
1276
            }
1277
        }
1278

1279
    }
1280 1281

    if (!ui.menuConnected_Systems->isEnabled()) ui.menuConnected_Systems->setEnabled(true);
lm's avatar
lm committed
1282

1283 1284
    // Reload view state in case new widgets were added
    loadViewState();
pixhawk's avatar
pixhawk committed
1285 1286
}

1287
/**
1288
 * Stores the current view state
1289
 */
1290
void MainWindow::storeViewState()
1291
{
pixhawk's avatar
pixhawk committed
1292
    if (!aboutToCloseFlag)
lm's avatar
lm committed
1293
    {
pixhawk's avatar
pixhawk committed
1294 1295 1296 1297 1298 1299 1300 1301
        // Save current state
        settings.setValue(getWindowStateKey(), saveState(QGC::applicationVersion()));
        settings.setValue(getWindowStateKey()+"CENTER_WIDGET", centerStack->currentIndex());
        // Although we want save the state of the window, we do not want to change the top-leve state (minimized, maximized, etc)
        // therefore this state is stored here and restored after applying the rest of the settings in the new
        // perspective.
        windowStateVal = this->windowState();
        settings.setValue(getWindowGeometryKey(), saveGeometry());
1302
    }
1303
}
1304

1305 1306 1307
void MainWindow::loadViewState()
{
    // Restore center stack state
lm's avatar
lm committed
1308
    int index = settings.value(getWindowStateKey()+"CENTER_WIDGET", -1).toInt();
LM's avatar
LM committed
1309 1310 1311 1312 1313 1314
    // The offline plot view is usually the consequence of a logging run, always show the realtime view first
    if (centerStack->indexOf(dataplotWidget) == index)
    {
        // Rewrite to realtime plot
        index = centerStack->indexOf(linechartWidget);
    }
pixhawk's avatar
pixhawk committed
1315

lm's avatar
lm committed
1316 1317 1318 1319 1320 1321
    if (index != -1)
    {
        centerStack->setCurrentIndex(index);
    }
    else
    {
1322 1323 1324 1325
        // Hide custom widgets
        if (detectionDockWidget) detectionDockWidget->hide();
        if (watchdogControlDockWidget) watchdogControlDockWidget->hide();

lm's avatar
lm committed
1326 1327 1328 1329 1330
        // Load defaults
        switch (currentView)
        {
        case VIEW_ENGINEER:
            centerStack->setCurrentWidget(linechartWidget);
lm's avatar
lm committed
1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345
            controlDockWidget->hide();
            listDockWidget->hide();
            waypointsDockWidget->hide();
            infoDockWidget->hide();
            debugConsoleDockWidget->show();
            logPlayerDockWidget->show();
            mavlinkInspectorWidget->show();
            parametersDockWidget->show();
            hsiDockWidget->hide();
            headDown1DockWidget->hide();
            headDown2DockWidget->hide();
            rcViewDockWidget->hide();
            headUpDockWidget->hide();
            video1DockWidget->hide();
            video2DockWidget->hide();
lm's avatar
lm committed
1346 1347 1348
            break;
        case VIEW_PILOT:
            centerStack->setCurrentWidget(hudWidget);
lm's avatar
lm committed
1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
            controlDockWidget->hide();
            listDockWidget->hide();
            waypointsDockWidget->hide();
            infoDockWidget->hide();
            debugConsoleDockWidget->hide();
            logPlayerDockWidget->hide();
            mavlinkInspectorWidget->hide();
            parametersDockWidget->hide();
            hsiDockWidget->show();
            headDown1DockWidget->show();
            headDown2DockWidget->show();
            rcViewDockWidget->hide();
            headUpDockWidget->hide();
1362
            video1DockWidget->hide();
lm's avatar
lm committed
1363
            video2DockWidget->hide();
lm's avatar
lm committed
1364 1365 1366
            break;
        case VIEW_MAVLINK:
            centerStack->setCurrentWidget(protocolWidget);
lm's avatar
lm committed
1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381
            controlDockWidget->hide();
            listDockWidget->hide();
            waypointsDockWidget->hide();
            infoDockWidget->hide();
            debugConsoleDockWidget->hide();
            logPlayerDockWidget->hide();
            mavlinkInspectorWidget->hide();
            parametersDockWidget->hide();
            hsiDockWidget->hide();
            headDown1DockWidget->hide();
            headDown2DockWidget->hide();
            rcViewDockWidget->hide();
            headUpDockWidget->hide();
            video1DockWidget->hide();
            video2DockWidget->hide();
lm's avatar
lm committed
1382 1383
            break;
        case VIEW_OPERATOR:
1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400
            centerStack->setCurrentWidget(mapWidget);
            controlDockWidget->hide();
            listDockWidget->show();
            waypointsDockWidget->show();
            infoDockWidget->hide();
            debugConsoleDockWidget->show();
            logPlayerDockWidget->show();
            parametersDockWidget->hide();
            hsiDockWidget->show();
            headDown1DockWidget->hide();
            headDown2DockWidget->hide();
            rcViewDockWidget->hide();
            headUpDockWidget->show();
            video1DockWidget->hide();
            video2DockWidget->hide();
            mavlinkInspectorWidget->hide();
            break;
lm's avatar
lm committed
1401 1402 1403 1404
        case VIEW_UNCONNECTED:
        case VIEW_FULL:
        default:
            centerStack->setCurrentWidget(mapWidget);
1405
            controlDockWidget->hide();
lm's avatar
lm committed
1406
            listDockWidget->show();
1407 1408
            waypointsDockWidget->hide();
            infoDockWidget->hide();
lm's avatar
lm committed
1409 1410 1411
            debugConsoleDockWidget->show();
            logPlayerDockWidget->show();
            parametersDockWidget->hide();
1412
            hsiDockWidget->hide();
lm's avatar
lm committed
1413 1414 1415 1416 1417 1418
            headDown1DockWidget->hide();
            headDown2DockWidget->hide();
            rcViewDockWidget->hide();
            headUpDockWidget->show();
            video1DockWidget->hide();
            video2DockWidget->hide();
1419
            mavlinkInspectorWidget->show();
lm's avatar
lm committed
1420
            break;
pixhawk's avatar
pixhawk committed
1421 1422 1423
        }
    }

1424 1425 1426 1427
    // Restore the widget positions and size
    if (settings.contains(getWindowStateKey()))
    {
        restoreState(settings.value(getWindowStateKey()).toByteArray(), QGC::applicationVersion());
pixhawk's avatar
pixhawk committed
1428 1429 1430
    }
}

1431
void MainWindow::loadEngineerView()
lm's avatar
lm committed
1432
{
1433 1434
    if (currentView != VIEW_ENGINEER)
    {
1435
        storeViewState();
1436 1437
        currentView = VIEW_ENGINEER;
        ui.actionEngineersView->setChecked(true);
1438
        loadViewState();
1439
    }
lm's avatar
lm committed
1440 1441
}

1442
void MainWindow::loadOperatorView()
lm's avatar
lm committed
1443
{
1444 1445
    if (currentView != VIEW_OPERATOR)
    {
1446
        storeViewState();
1447 1448
        currentView = VIEW_OPERATOR;
        ui.actionOperatorsView->setChecked(true);
1449
        loadViewState();
1450 1451
    }
}
lm's avatar
lm committed
1452

1453 1454
void MainWindow::loadUnconnectedView()
{
1455 1456 1457
    if (currentView != VIEW_UNCONNECTED)
    {
        storeViewState();
1458 1459
        currentView = VIEW_UNCONNECTED;
        ui.actionUnconnectedView->setChecked(true);
1460
        loadViewState();
1461
    }
lm's avatar
lm committed
1462 1463
}

1464
void MainWindow::loadPilotView()
1465
{
1466 1467 1468
    if (currentView != VIEW_PILOT)
    {
        storeViewState();
1469 1470
        currentView = VIEW_PILOT;
        ui.actionPilotsView->setChecked(true);
1471
        loadViewState();
1472
    }
1473 1474
}

1475
void MainWindow::loadMAVLinkView()
1476
{
1477 1478 1479
    if (currentView != VIEW_MAVLINK)
    {
        storeViewState();
1480 1481
        currentView = VIEW_MAVLINK;
        ui.actionMavlinkView->setChecked(true);
1482
        loadViewState();
1483
    }
1484 1485
}

1486 1487
void MainWindow::loadDataView(QString fileName)
{
1488
    // Plot is now selected, now load data from file
1489 1490 1491 1492 1493 1494 1495 1496
    if (dataplotWidget)
    {
        dataplotWidget->loadFile(fileName);
    }
    QStackedWidget *centerStack = dynamic_cast<QStackedWidget*>(centralWidget());
    if (centerStack)
    {
        centerStack->setCurrentWidget(dataplotWidget);
1497
        dataplotWidget->loadFile(fileName);
1498
    }
1499 1500 1501
}


oberion's avatar
oberion committed
1502 1503
QList<QAction*> MainWindow::listLinkMenuActions(void)
{
lm's avatar
lm committed
1504
    return ui.menuNetwork->actions();
1505
}
oberion's avatar
oberion committed
1506
/*
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608
void MainWindow::buildSenseSoarWidgets()
{
	if (!linechartWidget) 
	{
        // Center widgets
        linechartWidget   = new Linecharts(this);
        addToCentralWidgetsMenu(linechartWidget, tr("Realtime Plot"), SLOT(showCentralWidget()), CENTRAL_LINECHART);
    }

    if (!hudWidget) 
	{
        hudWidget         = new HUD(320, 240, this);
        addToCentralWidgetsMenu(hudWidget, tr("Head Up Display"), SLOT(showCentralWidget()), CENTRAL_HUD);
    }

    if (!dataplotWidget) {
        dataplotWidget    = new QGCDataPlot2D(this);
        addToCentralWidgetsMenu(dataplotWidget, "Logfile Plot", SLOT(showCentralWidget()), CENTRAL_DATA_PLOT);
    }

#ifdef QGC_OSG_ENABLED
    if (!_3DWidget) {
        _3DWidget         = Q3DWidgetFactory::get("PIXHAWK");
        addToCentralWidgetsMenu(_3DWidget, tr("Local 3D"), SLOT(showCentralWidget()), CENTRAL_3D_LOCAL);
    }
#endif

#ifdef QGC_OSGEARTH_ENABLED
    if (!_3DMapWidget) {
        _3DMapWidget = Q3DWidgetFactory::get("MAP3D");
        addToCentralWidgetsMenu(_3DMapWidget, tr("OSG Earth 3D"), SLOT(showCentralWidget()), CENTRAL_OSGEARTH);
    }
#endif

#if (defined _MSC_VER) | (defined Q_OS_MAC)
    if (!gEarthWidget) {
        gEarthWidget = new QGCGoogleEarthView(this);
        addToCentralWidgetsMenu(gEarthWidget, tr("Google Earth"), SLOT(showCentralWidget()), CENTRAL_GOOGLE_EARTH);
    }

#endif

    // Dock widgets

    if (!parametersDockWidget) {
        parametersDockWidget = new QDockWidget(tr("Calibration and Onboard Parameters"), this);
        parametersDockWidget->setWidget( new ParameterInterface(this) );
        parametersDockWidget->setObjectName("PARAMETER_INTERFACE_DOCKWIDGET");
        addToToolsMenu (parametersDockWidget, tr("Calibration and Parameters"), SLOT(showToolWidget(bool)), MENU_PARAMETERS, Qt::RightDockWidgetArea);
    }

    if (!hsiDockWidget) {
        hsiDockWidget = new QDockWidget(tr("Horizontal Situation Indicator"), this);
        hsiDockWidget->setWidget( new HSIDisplay(this) );
        hsiDockWidget->setObjectName("HORIZONTAL_SITUATION_INDICATOR_DOCK_WIDGET");
        addToToolsMenu (hsiDockWidget, tr("Horizontal Situation"), SLOT(showToolWidget(bool)), MENU_HSI, Qt::BottomDockWidgetArea);
    }
	
    if (!rcViewDockWidget) {
        rcViewDockWidget = new QDockWidget(tr("Radio Control"), this);
        rcViewDockWidget->setWidget( new QGCRemoteControlView(this) );
        rcViewDockWidget->setObjectName("RADIO_CONTROL_CHANNELS_DOCK_WIDGET");
        addToToolsMenu (rcViewDockWidget, tr("Radio Control"), SLOT(showToolWidget(bool)), MENU_RC_VIEW, Qt::BottomDockWidgetArea);
    }

    if (!headUpDockWidget) {
        headUpDockWidget = new QDockWidget(tr("HUD"), this);
        headUpDockWidget->setWidget( new HUD(320, 240, this));
        headUpDockWidget->setObjectName("HEAD_UP_DISPLAY_DOCK_WIDGET");
        addToToolsMenu (headUpDockWidget, tr("Head Up Display"), SLOT(showToolWidget(bool)), MENU_HUD, Qt::RightDockWidgetArea);
    }
}

void MainWindow::connectSenseSoarWidgets()
{
}

void MainWindow::arrangeSenseSoarCenterStack()
{
	if (!centerStack) {
        qDebug() << "Center Stack not Created!";
        return;
    }


    if (linechartWidget && (centerStack->indexOf(linechartWidget) == -1)) centerStack->addWidget(linechartWidget);

#ifdef QGC_OSG_ENABLED
    if (_3DWidget && (centerStack->indexOf(_3DWidget) == -1)) centerStack->addWidget(_3DWidget);
#endif
#ifdef QGC_OSGEARTH_ENABLED
    if (_3DMapWidget && (centerStack->indexOf(_3DMapWidget) == -1)) centerStack->addWidget(_3DMapWidget);
#endif
#if (defined _MSC_VER) | (defined Q_OS_MAC)
    if (gEarthWidget && (centerStack->indexOf(gEarthWidget) == -1)) centerStack->addWidget(gEarthWidget);
#endif
    if (hudWidget && (centerStack->indexOf(hudWidget) == -1)) centerStack->addWidget(hudWidget);
    if (dataplotWidget && (centerStack->indexOf(dataplotWidget) == -1)) centerStack->addWidget(dataplotWidget);
}

void MainWindow::connectSenseSoarActions()
{
1609
}
1610
*/