MainWindow.cc 54.2 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 91 92 93 94
    QMainWindow(parent),
    currentView(VIEW_UNCONNECTED),
    aboutToCloseFlag(false),
    changingViewsFlag(false),
    styleFileName(QCoreApplication::applicationDirPath() + "/style-indoor.css"),
    autoReconnect(false),
95
    currentStyle(QGC_MAINWINDOW_STYLE_INDOOR),
96
    centerStackActionGroup(this),
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
    // Set dock options
    setDockOptions(AnimatedDocks | AllowTabbedDocks | AllowNestedDocks);
    statusBar()->setSizeGripEnabled(true);
134

135
    configureWindowName();
136

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

140 141
    // Setup UI state machines
    centerStackActionGroup.setExclusive(true);
pixhawk's avatar
pixhawk committed
142

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

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

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

156 157
    buildCommonWidgets();
    connectCommonWidgets();
158

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

161
    // Create actions
162
    connectCommonActions();
pixhawk's avatar
pixhawk committed
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
    if (!mavlinkInspectorWidget)
397
    {
lm's avatar
lm committed
398 399 400
        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);
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

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

421
    if (!hsiDockWidget) {
422 423
        hsiDockWidget = new QDockWidget(tr("Horizontal Situation Indicator"), this);
        hsiDockWidget->setWidget( new HSIDisplay(this) );
424
        hsiDockWidget->setObjectName("HORIZONTAL_SITUATION_INDICATOR_DOCK_WIDGET");
lm's avatar
lm committed
425
        addTool(hsiDockWidget, tr("Horizontal Situation"), Qt::BottomDockWidgetArea);
426
    }
427

428
    if (!headDown1DockWidget) {
429 430
        headDown1DockWidget = new QDockWidget(tr("Flight Display"), this);
        headDown1DockWidget->setWidget( new HDDisplay(acceptList, "Flight Display", this) );
431
        headDown1DockWidget->setObjectName("HEAD_DOWN_DISPLAY_1_DOCK_WIDGET");
lm's avatar
lm committed
432
        addTool(headDown1DockWidget, tr("Flight Display"), Qt::RightDockWidgetArea);
433
    }
434

435
    if (!headDown2DockWidget) {
436 437
        headDown2DockWidget = new QDockWidget(tr("Actuator Status"), this);
        headDown2DockWidget->setWidget( new HDDisplay(acceptList2, "Actuator Status", this) );
438
        headDown2DockWidget->setObjectName("HEAD_DOWN_DISPLAY_2_DOCK_WIDGET");
lm's avatar
lm committed
439
        addTool(headDown2DockWidget, tr("Actuator Status"), Qt::RightDockWidgetArea);
440
    }
441

442
    if (!rcViewDockWidget) {
443 444
        rcViewDockWidget = new QDockWidget(tr("Radio Control"), this);
        rcViewDockWidget->setWidget( new QGCRemoteControlView(this) );
445
        rcViewDockWidget->setObjectName("RADIO_CONTROL_CHANNELS_DOCK_WIDGET");
lm's avatar
lm committed
446
        addTool(rcViewDockWidget, tr("Radio Control"), Qt::BottomDockWidgetArea);
447
    }
448

449
    if (!headUpDockWidget) {
450 451
        headUpDockWidget = new QDockWidget(tr("HUD"), this);
        headUpDockWidget->setWidget( new HUD(320, 240, this));
452
        headUpDockWidget->setObjectName("HEAD_UP_DISPLAY_DOCK_WIDGET");
lm's avatar
lm committed
453
        addTool(headUpDockWidget, tr("Head Up Display"), Qt::RightDockWidgetArea);
454
    }
455

456
    if (!video1DockWidget) {
pixhawk's avatar
pixhawk committed
457 458 459 460 461 462 463
        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");
lm's avatar
lm committed
464
        addTool(video1DockWidget, tr("Video Stream 1"), Qt::LeftDockWidgetArea);
pixhawk's avatar
pixhawk committed
465 466
    }

467
    if (!video2DockWidget) {
pixhawk's avatar
pixhawk committed
468 469 470 471 472 473 474
        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");
lm's avatar
lm committed
475
        addTool(video2DockWidget, tr("Video Stream 2"), Qt::LeftDockWidgetArea);
476
    }
477

lm's avatar
lm committed
478 479
    // Custom widgets, added last to all menus and layouts
    buildCustomWidget();
480

lm's avatar
lm committed
481 482 483 484 485
    // Center widgets
    if (!mapWidget)
    {
        mapWidget = new QGCMapTool(this);
        addCentralWidget(mapWidget, "Maps");
486 487
    }

lm's avatar
lm committed
488 489 490 491
    if (!protocolWidget)
    {
        protocolWidget    = new XMLCommProtocolWidget(this);
        addCentralWidget(protocolWidget, "Mavlink Generator");
492
    }
493

494
    if (!hudWidget) {
495
        hudWidget         = new HUD(320, 240, this);
496
        addCentralWidget(hudWidget, tr("Head Up Display"));
497
    }
498

499
    if (!dataplotWidget) {
500
        dataplotWidget    = new QGCDataPlot2D(this);
501
        addCentralWidget(dataplotWidget, tr("Logfile Plot"));
502
    }
503

504
#ifdef QGC_OSG_ENABLED
505
    if (!_3DWidget) {
506
        _3DWidget         = Q3DWidgetFactory::get("PIXHAWK");
507
        addCentralWidget(_3DWidget, tr("Local 3D"));
508
    }
509
#endif
510

511
#if (defined _MSC_VER) | (defined Q_OS_MAC)
512
    if (!gEarthWidget) {
513
        gEarthWidget = new QGCGoogleEarthView(this);
514
        addCentralWidget(gEarthWidget, tr("Google Earth"));
515
    }
516
#endif
517
}
518

519
void MainWindow::addTool(QDockWidget* widget, const QString& title, Qt::DockWidgetArea area)
520
{
521
    QAction* tempAction = ui.menuTools->addAction(title);
522

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


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

541
void MainWindow::addCentralWidget(QWidget* widget, const QString& title)
542
{
543 544 545 546
    // Check if this widget already has been added
    if (centerStack->indexOf(widget) == -1)
    {
        centerStack->addWidget(widget);
547

548
        QAction* tempAction = ui.menuMain->addAction(title);
549

550 551 552 553 554 555
        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
556 557
        connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool)));
        tempAction->setChecked(widget->isVisible());
558 559 560 561
    }
}


562
void MainWindow::showCentralWidget()
563
{
564 565 566
    QAction* act = qobject_cast<QAction *>(sender());
    QWidget* widget = qVariantValue<QWidget *>(act->data());
    centerStack->setCurrentWidget(widget);
567 568
}

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

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

590 591
void MainWindow::createCustomWidget()
{
592 593
    QDockWidget* dock = new QDockWidget("Unnamed Tool", this);
    QGCToolWidget* tool = new QGCToolWidget("Unnamed Tool", dock);
lm's avatar
lm committed
594

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

    connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater()));
602
    dock->setWidget(tool);
603

604 605 606 607 608 609 610 611 612 613 614 615 616 617
    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));
618
    if (fileName != "") loadCustomWidget(fileName);
619 620
}

LM's avatar
LM committed
621
void MainWindow::loadCustomWidget(const QString& fileName, bool singleinstance)
622
{
LM's avatar
LM committed
623 624
    QGCToolWidget* tool = new QGCToolWidget("", this);
    if (tool->loadSettings(fileName, true) || !singleinstance)
LM's avatar
LM committed
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
    {
        // 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
641 642
    else
    {
643 644
        return;
    }
645 646
}

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

LM's avatar
LM committed
651 652 653 654 655 656
    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
657
        showStatusMessage(tr("Did not find any custom widgets in %1").arg(defaultsDir));
658
    }
659

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

674 675 676 677 678 679
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();
680
    lowPowerMode = settings.value("LOW_POWER_MODE", lowPowerMode).toBool();
681 682 683 684 685 686 687 688 689 690
    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
691 692 693 694 695 696 697 698 699 700 701
    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
    }
702
    settings.setValue("LOW_POWER_MODE", lowPowerMode);
703 704 705
    settings.sync();
}

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

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

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

pixhawk's avatar
pixhawk committed
725
    windowname.append(")");
726

pixhawk's avatar
pixhawk committed
727
    setWindowTitle(windowname);
728 729

#ifndef Q_WS_MAC
pixhawk's avatar
pixhawk committed
730
    //qApp->setWindowIcon(QIcon(":/core/images/qtcreator_logo_128.png"));
731 732 733
#endif
}

pixhawk's avatar
pixhawk committed
734
void MainWindow::startVideoCapture()
pixhawk's avatar
pixhawk committed
735 736 737 738 739
{
    QString format = "bmp";
    QString initialPath = QDir::currentPath() + tr("/untitled.") + format;

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

pixhawk's avatar
pixhawk committed
751
void MainWindow::stopVideoCapture()
pixhawk's avatar
pixhawk committed
752 753 754 755 756 757
{
    videoTimer->stop();

    // TODO Convert raw images to PNG
}

pixhawk's avatar
pixhawk committed
758
void MainWindow::saveScreen()
pixhawk's avatar
pixhawk committed
759 760 761 762
{
    QPixmap window = QPixmap::grabWindow(this->winId());
    QString format = "bmp";

763 764
    if (!screenFileName.isEmpty())
    {
pixhawk's avatar
pixhawk committed
765 766 767 768
        window.save(screenFileName, format.toAscii());
    }
}

769
void MainWindow::enableAutoReconnect(bool enabled)
pixhawk's avatar
pixhawk committed
770
{
771 772
    autoReconnect = enabled;
}
773

774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
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)
{
791 792 793 794 795 796 797 798 799
    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."));
800
        }
801 802
    }
    break;
803 804 805 806 807 808 809 810 811 812 813 814 815 816
    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;
}

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

822
    if (!styleFileName.endsWith(".css")) {
823 824 825 826 827 828 829 830 831 832
        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
833
    // Load style sheet
834 835 836 837 838 839 840
    reloadStylesheet();
}

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

860 861 862 863 864 865
/**
 * 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
866
void MainWindow::showStatusMessage(const QString& status, int timeout)
pixhawk's avatar
pixhawk committed
867
{
lm's avatar
lm committed
868
    statusBar()->showMessage(status, timeout);
pixhawk's avatar
pixhawk committed
869 870
}

871 872 873 874 875 876
/**
 * 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
 */
877
void MainWindow::showStatusMessage(const QString& status)
pixhawk's avatar
pixhawk committed
878
{
lm's avatar
lm committed
879
    statusBar()->showMessage(status, 20000);
880 881 882 883
}

void MainWindow::showCriticalMessage(const QString& title, const QString& message)
{
884
    QMessageBox msgBox(this);
885 886 887 888 889 890
    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
891 892
}

lm's avatar
lm committed
893 894
void MainWindow::showInfoMessage(const QString& title, const QString& message)
{
895
    QMessageBox msgBox(this);
lm's avatar
lm committed
896 897 898 899 900 901 902 903
    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
904 905 906 907
/**
* @brief Create all actions associated to the main window
*
**/
908
void MainWindow::connectCommonActions()
pixhawk's avatar
pixhawk committed
909
{
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
    // 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
933 934 935 936 937
    // 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*)));
938
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
pixhawk's avatar
pixhawk committed
939

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

948
    // Views actions
949
    connect(ui.actionPilotsView, SIGNAL(triggered()), this, SLOT(loadPilotView()));
950
    connect(ui.actionEngineersView, SIGNAL(triggered()), this, SLOT(loadEngineerView()));
951
    connect(ui.actionOperatorsView, SIGNAL(triggered()), this, SLOT(loadOperatorView()));
952
    connect(ui.actionUnconnectedView, SIGNAL(triggered()), this, SLOT(loadUnconnectedView()));
953

954
    connect(ui.actionMavlinkView, SIGNAL(triggered()), this, SLOT(loadMAVLinkView()));
955 956
    connect(ui.actionReloadStylesheet, SIGNAL(triggered()), this, SLOT(reloadStylesheet()));
    connect(ui.actionSelectStylesheet, SIGNAL(triggered()), this, SLOT(selectStylesheet()));
957

958
    // Help Actions
959
    connect(ui.actionOnline_Documentation, SIGNAL(triggered()), this, SLOT(showHelp()));
960
    connect(ui.actionDeveloper_Credits, SIGNAL(triggered()), this, SLOT(showCredits()));
961
    connect(ui.actionProject_Roadmap_2, SIGNAL(triggered()), this, SLOT(showRoadMap()));
962 963 964

    // Custom widget actions
    connect(ui.actionNewCustomWidget, SIGNAL(triggered()), this, SLOT(createCustomWidget()));
965
    connect(ui.actionLoadCustomWidgetFile, SIGNAL(triggered()), this, SLOT(loadCustomWidget()));
966 967 968

    // Audio output
    ui.actionMuteAudioOutput->setChecked(GAudioOutput::instance()->isMuted());
969
    connect(GAudioOutput::instance(), SIGNAL(mutedChanged(bool)), ui.actionMuteAudioOutput, SLOT(setChecked(bool)));
970
    connect(ui.actionMuteAudioOutput, SIGNAL(triggered(bool)), GAudioOutput::instance(), SLOT(mute(bool)));
971 972 973 974 975 976 977

    // 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);
978 979 980

    // Configuration
    // Joystick
981
    connect(ui.actionJoystickSettings, SIGNAL(triggered()), this, SLOT(configure()));
982 983
    // Application Settings
    connect(ui.actionSettings, SIGNAL(triggered()), this, SLOT(showSettings()));
984 985
}

986 987
void MainWindow::showHelp()
{
lm's avatar
lm committed
988
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/users/start"))) {
989 990 991 992 993 994 995 996 997 998 999 1000
        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()
{
1001
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/credits"))) {
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
        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
1014
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/dev/roadmap"))) {
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
        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
1025
void MainWindow::configure()
pixhawk's avatar
pixhawk committed
1026
{
1027 1028
    if (!joystickWidget) {
        if (!joystick->isRunning()) {
1029 1030 1031 1032 1033
            joystick->start();
        }
        joystickWidget = new JoystickWidget(joystick);
    }
    joystickWidget->show();
pixhawk's avatar
pixhawk committed
1034 1035
}

1036 1037 1038 1039 1040 1041
void MainWindow::showSettings()
{
    QGCSettingsWidget* settings = new QGCSettingsWidget(this);
    settings->show();
}

pixhawk's avatar
pixhawk committed
1042
void MainWindow::addLink()
pixhawk's avatar
pixhawk committed
1043 1044 1045 1046
{
    SerialLink* link = new SerialLink();
    // TODO This should be only done in the dialog itself

1047
    LinkManager::instance()->add(link);
pixhawk's avatar
pixhawk committed
1048 1049
    LinkManager::instance()->addProtocol(link, mavlink);

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

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

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

pixhawk's avatar
pixhawk committed
1064 1065
void MainWindow::addLink(LinkInterface *link)
{
1066 1067 1068 1069
    // 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)
1070
    LinkManager::instance()->add(link);
1071
    LinkManager::instance()->addProtocol(link, mavlink);
1072

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

oberion's avatar
oberion committed
1076 1077
    bool found(false);

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

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

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

oberion's avatar
oberion committed
1089
    if (!found) {  //  || udp
1090 1091 1092 1093 1094 1095 1096 1097
        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);
1098 1099
        if (sim)
        {
1100 1101 1102
            //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
1103 1104 1105
    }
}

1106 1107 1108 1109 1110 1111 1112
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);
}

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

pixhawk's avatar
pixhawk committed
1125
void MainWindow::UASCreated(UASInterface* uas)
pixhawk's avatar
pixhawk committed
1126
{
1127

pixhawk's avatar
pixhawk committed
1128 1129
    // Connect the UAS to the full user interface

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

1143