MainWindow.cc 48.1 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 37
 */

#include <QSettings>
#include <QDockWidget>
#include <QNetworkInterface>
#include <QMessageBox>
#include <QDebug>
#include <QTimer>
#include <QHostInfo>

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

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

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

pixhawk's avatar
pixhawk committed
61

62
#include "LogCompressor.h"
pixhawk's avatar
pixhawk committed
63

64 65
MainWindow* MainWindow::instance()
{
66
    static MainWindow* _instance = 0;
67 68
    if(_instance == 0)
    {
69
        _instance = new MainWindow();
70

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

pixhawk's avatar
pixhawk committed
78 79 80 81 82 83 84
/**
* 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()
**/
85
MainWindow::MainWindow(QWidget *parent):
86 87 88 89 90 91
    QMainWindow(parent),
    currentView(VIEW_UNCONNECTED),
    aboutToCloseFlag(false),
    changingViewsFlag(false),
    styleFileName(QCoreApplication::applicationDirPath() + "/style-indoor.css"),
    autoReconnect(false),
92
    currentStyle(QGC_MAINWINDOW_STYLE_INDOOR),
93 94
    lowPowerMode(false),
    centerStackActionGroup(this)
pixhawk's avatar
pixhawk committed
95
{
96
    loadSettings();
97 98
    if (!settings.contains("CURRENT_VIEW"))
    {
99 100
        // Set this view as default view
        settings.setValue("CURRENT_VIEW", currentView);
101 102 103
    }
    else
    {
104 105 106
        // LOAD THE LAST VIEW
        VIEW_SECTIONS currentViewCandidate = (VIEW_SECTIONS) settings.value("CURRENT_VIEW", currentView).toInt();
        if (currentViewCandidate != VIEW_ENGINEER &&
107
                currentViewCandidate != VIEW_OPERATOR &&
108 109 110
                currentViewCandidate != VIEW_PILOT &&
                currentViewCandidate != VIEW_FULL)
        {
111
            currentView = currentViewCandidate;
112
        }
113 114
    }

115 116
    settings.sync();

117 118 119
    // Setup UI state machines
    centerStackActionGroup.setExclusive(true);

pixhawk's avatar
pixhawk committed
120 121 122
    // Setup user interface
    ui.setupUi(this);

123 124
    centerStack = new QStackedWidget(this);
    setCentralWidget(centerStack);
125

126 127
    buildCommonWidgets();
    connectCommonWidgets();
128 129

    configureWindowName();
pixhawk's avatar
pixhawk committed
130

131
    loadStyle(currentStyle);
132

133
    // Create actions
134
    connectCommonActions();
135

136 137 138
    // Set dock options
    setDockOptions(AnimatedDocks | AllowTabbedDocks | AllowNestedDocks);

lm's avatar
lm committed
139 140
    statusBar()->setSizeGripEnabled(true);

141 142 143 144 145 146
    // Restore the window setup
    if (settings.contains(getWindowStateKey()))
    {
        loadViewState();
    }

147
    // Restore the window position and size
148 149
    if (settings.contains(getWindowGeometryKey()))
    {
150
        // Restore the window geometry
151
        restoreGeometry(settings.value(getWindowGeometryKey()).toByteArray());
152 153 154
    }
    else
    {
155 156 157
        // Adjust the size
        adjustSize();
    }
pixhawk's avatar
pixhawk committed
158

159 160
    // Populate link menu
    QList<LinkInterface*> links = LinkManager::instance()->getLinks();
161 162
    foreach(LinkInterface* link, links)
    {
163 164
        this->addLink(link);
    }
165

166 167
    connect(LinkManager::instance(), SIGNAL(newLink(LinkInterface*)), this, SLOT(addLink(LinkInterface*)));

lm's avatar
lm committed
168 169 170
    // Add generic MAVLink decoder
    mavlinkDecoder = new MAVLinkDecoder(mavlink, this);

171
    // Connect user interface devices
172 173
    joystickWidget = 0;
    joystick = new JoystickInput();
174

175 176 177 178 179 180 181 182
    // Load Toolbar
    toolBar = new QGCToolBar(this);
    this->addToolBar(toolBar);
    // Add actions
    toolBar->addPerspectiveChangeAction(ui.actionOperatorsView);
    toolBar->addPerspectiveChangeAction(ui.actionEngineersView);
    toolBar->addPerspectiveChangeAction(ui.actionPilotsView);

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

193 194 195
    // Set low power mode
    enableLowPowerMode(lowPowerMode);

196 197
    // Initialize window state
    windowStateVal = windowState();
198 199

    show();
pixhawk's avatar
pixhawk committed
200 201
}

pixhawk's avatar
pixhawk committed
202
MainWindow::~MainWindow()
pixhawk's avatar
pixhawk committed
203
{
204
    delete mavlink;
205
    delete joystick;
lm's avatar
lm committed
206

207 208 209 210 211 212
    // Get and delete all dockwidgets and contained
    // widgets
    QObjectList childList( this->children() );

    QObjectList::iterator i;
    QDockWidget* dockWidget;
213 214
    for (i = childList.begin(); i != childList.end(); ++i)
    {
215
        dockWidget = dynamic_cast<QDockWidget*>(*i);
216 217
        if (dockWidget)
        {
218 219 220 221 222
            // Remove dock widget from main window
            removeDockWidget(dockWidget);
            delete dockWidget->widget();
            delete dockWidget;
        }
LM's avatar
LM committed
223 224 225 226
        else
        {
            delete dynamic_cast<QObject*>(*i);
        }
227 228
    }

LM's avatar
LM committed
229
    // Delete all UAS objects
230 231
}

lm's avatar
lm committed
232 233 234
void MainWindow::resizeEvent(QResizeEvent * event)
{
    Q_UNUSED(event);
235 236
    if (height() < 800)
    {
lm's avatar
lm committed
237
        ui.statusBar->setVisible(false);
238 239 240
    }
    else
    {
lm's avatar
lm committed
241
        ui.statusBar->setVisible(true);
242
        ui.statusBar->setSizeGripEnabled(true);
lm's avatar
lm committed
243 244 245
    }
}

246 247
QString MainWindow::getWindowStateKey()
{
248
    return QString::number(currentView)+"_windowstate";
249 250 251 252
}

QString MainWindow::getWindowGeometryKey()
{
253 254
    //return QString::number(currentView)+"_geometry";
    return "_geometry";
255 256
}

lm's avatar
lm committed
257 258 259
void MainWindow::buildCustomWidget()
{
    // Show custom widgets only if UAS is connected
260 261
    if (UASManager::instance()->getActiveUAS() != NULL)
    {
lm's avatar
lm committed
262 263 264
        // Create custom widgets
        QList<QGCToolWidget*> widgets = QGCToolWidget::createWidgetsFromSettings(this);

265 266
        if (widgets.size() > 0)
        {
lm's avatar
lm committed
267 268 269
            ui.menuTools->addSeparator();
        }

270 271
        for(int i = 0; i < widgets.size(); ++i)
        {
lm's avatar
lm committed
272
            // Check if this widget already has a parent, do not create it in this case
273 274
            QGCToolWidget* tool = widgets.at(i);
            QDockWidget* dock = dynamic_cast<QDockWidget*>(tool->parentWidget());
275 276
            if (!dock)
            {
277 278 279 280
                QDockWidget* dock = new QDockWidget(tool->windowTitle(), this);
                dock->setObjectName(tool->objectName()+"_DOCK");
                dock->setWidget(tool);
                connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater()));
lm's avatar
lm committed
281
                QAction* showAction = new QAction(widgets.at(i)->windowTitle(), this);
282
                showAction->setCheckable(true);
lm's avatar
lm committed
283 284 285 286
                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);
287 288 289 290 291

                // 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
292 293 294 295 296
            }
        }
    }
}

297 298 299 300
void MainWindow::buildCommonWidgets()
{
    //TODO:  move protocol outside UI
    mavlink     = new MAVLinkProtocol();
301
    connect(mavlink, SIGNAL(protocolStatusMessage(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection);
302 303

    // Dock widgets
304 305
    if (!controlDockWidget)
    {
306
        controlDockWidget = new QDockWidget(tr("Control"), this);
307
        controlDockWidget->setObjectName("UNMANNED_SYSTEM_CONTROL_DOCKWIDGET");
308
        controlDockWidget->setWidget( new UASControlWidget(this) );
309
        addTool(controlDockWidget, tr("Control"), Qt::LeftDockWidgetArea);
310
    }
311

312 313
    if (!listDockWidget)
    {
314 315
        listDockWidget = new QDockWidget(tr("Unmanned Systems"), this);
        listDockWidget->setWidget( new UASListWidget(this) );
316
        listDockWidget->setObjectName("UNMANNED_SYSTEMS_LIST_DOCKWIDGET");
317
        addTool(listDockWidget, tr("Unmanned Systems"), Qt::RightDockWidgetArea);
318
    }
319

320 321
    if (!waypointsDockWidget)
    {
322
        waypointsDockWidget = new QDockWidget(tr("Mission Plan"), this);
323
        waypointsDockWidget->setWidget( new QGCWaypointListMulti(this) );
324
        waypointsDockWidget->setObjectName("WAYPOINT_LIST_DOCKWIDGET");
325
        addTool(waypointsDockWidget, tr("Mission Plan"), Qt::BottomDockWidgetArea);
326
    }
327

328 329
    if (!infoDockWidget)
    {
330 331
        infoDockWidget = new QDockWidget(tr("Status Details"), this);
        infoDockWidget->setWidget( new UASInfoWidget(this) );
pixhawk's avatar
pixhawk committed
332
        infoDockWidget->setObjectName("UAS_STATUS_DETAILS_DOCKWIDGET");
333
        addTool(infoDockWidget, tr("Status Details"), Qt::RightDockWidgetArea);
334
    }
335

336 337
    if (!debugConsoleDockWidget)
    {
338 339
        debugConsoleDockWidget = new QDockWidget(tr("Communication Console"), this);
        debugConsoleDockWidget->setWidget( new DebugConsole(this) );
340
        debugConsoleDockWidget->setObjectName("COMMUNICATION_DEBUG_CONSOLE_DOCKWIDGET");
341
        addTool(debugConsoleDockWidget, tr("Communication Console"), Qt::BottomDockWidgetArea);
342
    }
343

344 345
    if (!logPlayerDockWidget)
    {
346 347 348
        logPlayerDockWidget = new QDockWidget(tr("MAVLink Log Player"), this);
        logPlayerDockWidget->setWidget( new QGCMAVLinkLogPlayer(mavlink, this) );
        logPlayerDockWidget->setObjectName("MAVLINK_LOG_PLAYER_DOCKWIDGET");
349
        addTool(logPlayerDockWidget, tr("MAVLink Log Replay"), Qt::RightDockWidgetArea);
350 351
    }

lm's avatar
lm committed
352 353 354 355 356
    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
357
        addTool(mavlinkInspectorWidget, tr("MAVLink Inspector"), Qt::RightDockWidgetArea);
lm's avatar
lm committed
358 359
    }

pixhawk's avatar
pixhawk committed
360 361
    //FIXME: memory of acceptList will never be freed again
    QStringList* acceptList = new QStringList();
pixhawk's avatar
pixhawk committed
362 363 364
    acceptList->append("-105,roll deg,deg,+105,s");
    acceptList->append("-105,pitch deg,deg,+105,s");
    acceptList->append("-105,heading deg,deg,+105,s");
365

pixhawk's avatar
pixhawk committed
366 367 368
    acceptList->append("-60,rollspeed d/s,deg/s,+60,s");
    acceptList->append("-60,pitchspeed d/s,deg/s,+60,s");
    acceptList->append("-60,yawspeed d/s,deg/s,+60,s");
369
    acceptList->append("0,airspeed,m/s,30");
370 371
    acceptList->append("0,groundspeed,m/s,30");
    acceptList->append("0,climbrate,m/s,30");
372
    acceptList->append("0,throttle,%,100");
373

pixhawk's avatar
pixhawk committed
374 375
    //FIXME: memory of acceptList2 will never be freed again
    QStringList* acceptList2 = new QStringList();
376 377 378
    acceptList2->append("900,servo #6,us,2100,s");
    acceptList2->append("900,servo #7,us,2100,s");
    acceptList2->append("900,servo #8,us,2100,s");
lm's avatar
lm committed
379
    acceptList2->append("0,abs pressure,hPa,65500");
380

lm's avatar
lm committed
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
    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);
        headDown1DockWidget->setWidget( new HDDisplay(acceptList, "Flight Display", this) );
        headDown1DockWidget->setObjectName("HEAD_DOWN_DISPLAY_1_DOCK_WIDGET");
        addTool(headDown1DockWidget, tr("Flight Display"), Qt::RightDockWidgetArea);
    }

    if (!headDown2DockWidget) {
        headDown2DockWidget = new QDockWidget(tr("Actuator Status"), this);
        headDown2DockWidget->setWidget( new HDDisplay(acceptList2, "Actuator Status", this) );
        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");
    }

461
    if (!hudWidget) {
462
        hudWidget         = new HUD(320, 240, this);
463
        addCentralWidget(hudWidget, tr("Head Up Display"));
lm's avatar
lm committed
464 465
    }

466
    if (!dataplotWidget) {
467
        dataplotWidget    = new QGCDataPlot2D(this);
468
        addCentralWidget(dataplotWidget, tr("Logfile Plot"));
469
    }
470

471
#ifdef QGC_OSG_ENABLED
472
    if (!_3DWidget) {
473
        _3DWidget         = Q3DWidgetFactory::get("PIXHAWK");
474
        addCentralWidget(_3DWidget, tr("Local 3D"));
475
    }
476
#endif
477

478
#if (defined _MSC_VER) | (defined Q_OS_MAC)
479
    if (!gEarthWidget) {
480
        gEarthWidget = new QGCGoogleEarthView(this);
481
        addCentralWidget(gEarthWidget, tr("Google Earth"));
482
    }
483
#endif
lm's avatar
lm committed
484
}
485

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

490
    tempAction->setCheckable(true);
491 492 493 494
    QVariant var;
    var.setValue((QWidget*)widget);
    tempAction->setData(var);
    connect(tempAction,SIGNAL(triggered(bool)),this, SLOT(showTool(bool)));
LM's avatar
LM committed
495 496
    connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool)));
    tempAction->setChecked(widget->isVisible());
497
    addDockWidget(area, widget);
498 499 500
}


501
void MainWindow::showTool(bool show)
lm's avatar
lm committed
502
{
503 504 505
    QAction* act = qobject_cast<QAction *>(sender());
    QWidget* widget = qVariantValue<QWidget *>(act->data());
    widget->setVisible(show);
506
}
507

508
void MainWindow::addCentralWidget(QWidget* widget, const QString& title)
lm's avatar
lm committed
509
{
510 511 512 513
    // Check if this widget already has been added
    if (centerStack->indexOf(widget) == -1)
    {
        centerStack->addWidget(widget);
514

515
        QAction* tempAction = ui.menuMain->addAction(title);
516

517 518 519 520 521 522
        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
523 524
        connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool)));
        tempAction->setChecked(widget->isVisible());
525 526 527 528
    }
}


529
void MainWindow::showCentralWidget()
530
{
531 532 533
    QAction* act = qobject_cast<QAction *>(sender());
    QWidget* widget = qVariantValue<QWidget *>(act->data());
    centerStack->setCurrentWidget(widget);
534 535
}

536 537
void MainWindow::closeEvent(QCloseEvent *event)
{
pixhawk's avatar
pixhawk committed
538
    if (isVisible()) storeViewState();
539
    storeSettings();
540
    aboutToCloseFlag = true;
541
    mavlink->storeSettings();
542
    UASManager::instance()->storeSettings();
543 544
    QMainWindow::closeEvent(event);
}
545 546 547 548

/**
 * Connect the signals and slots of the common window widgets
 */
549
void MainWindow::connectCommonWidgets()
550
{
551
    if (infoDockWidget && infoDockWidget->widget()) {
pixhawk's avatar
pixhawk committed
552 553 554
        connect(mavlink, SIGNAL(receiveLossChanged(int, float)),
                infoDockWidget->widget(), SLOT(updateSendLoss(int, float)));
    }
555
}
556

557 558
void MainWindow::createCustomWidget()
{
559 560
    QDockWidget* dock = new QDockWidget("Unnamed Tool", this);
    QGCToolWidget* tool = new QGCToolWidget("Unnamed Tool", dock);
lm's avatar
lm committed
561

LM's avatar
LM committed
562 563
    if (QGCToolWidget::instances()->size() < 2)
    {
lm's avatar
lm committed
564 565 566 567 568
        // This is the first widget
        ui.menuTools->addSeparator();
    }

    connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater()));
569
    dock->setWidget(tool);
570

571 572 573 574 575 576 577 578 579 580 581 582 583 584
    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));
585
    if (fileName != "") loadCustomWidget(fileName);
LM's avatar
LM committed
586
}
587

LM's avatar
LM committed
588 589 590 591
void MainWindow::loadCustomWidget(const QString& fileName, bool singleinstance)
{
    QGCToolWidget* tool = new QGCToolWidget("", this);
    if (tool->loadSettings(fileName, true) || !singleinstance)
LM's avatar
LM committed
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
    {
        // 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
608 609 610
    else
    {
        return;
611
    }
612 613
}

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

LM's avatar
LM committed
618 619 620 621 622 623
    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
624
        showStatusMessage(tr("Did not find any custom widgets in %1").arg(defaultsDir));
625
    }
626

LM's avatar
LM committed
627 628
    // Load all custom widgets found in the AP folder
    for(int i = 0; i < files.count(); ++i)
lm's avatar
lm committed
629
    {
LM's avatar
LM committed
630 631 632 633 634
        QString file = files[i];
        if (file.endsWith(".qgw"))
        {
            // Will only be loaded if not already a custom widget with
            // the same name is present
635
            loadCustomWidget(defaultsDir+"/"+file, true);
lm's avatar
lm committed
636
            showStatusMessage(tr("Loaded custom widget %1").arg(defaultsDir+"/"+file));
LM's avatar
LM committed
637
        }
lm's avatar
lm committed
638
    }
639 640
}

641 642 643 644 645 646
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();
647
    lowPowerMode = settings.value("LOW_POWER_MODE", lowPowerMode).toBool();
648 649 650 651 652 653 654 655 656 657
    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
658 659 660 661 662 663 664 665 666 667 668
    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
    }
669
    settings.setValue("LOW_POWER_MODE", lowPowerMode);
670 671 672
    settings.sync();
}

673 674
void MainWindow::configureWindowName()
{
pixhawk's avatar
pixhawk committed
675 676 677
    QList<QHostAddress> hostAddresses = QNetworkInterface::allAddresses();
    QString windowname = qApp->applicationName() + " " + qApp->applicationVersion();
    bool prevAddr = false;
678

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

681 682
    for (int i = 0; i < hostAddresses.size(); i++)
    {
pixhawk's avatar
pixhawk committed
683
        // Exclude loopback IPv4 and all IPv6 addresses
684 685
        if (hostAddresses.at(i) != QHostAddress("127.0.0.1") && !hostAddresses.at(i).toString().contains(":"))
        {
pixhawk's avatar
pixhawk committed
686 687 688 689 690
            if(prevAddr) windowname.append("/");
            windowname.append(hostAddresses.at(i).toString());
            prevAddr = true;
        }
    }
691

pixhawk's avatar
pixhawk committed
692
    windowname.append(")");
693

pixhawk's avatar
pixhawk committed
694
    setWindowTitle(windowname);
695 696

#ifndef Q_WS_MAC
pixhawk's avatar
pixhawk committed
697
    //qApp->setWindowIcon(QIcon(":/core/images/qtcreator_logo_128.png"));
698 699 700
#endif
}

pixhawk's avatar
pixhawk committed
701
void MainWindow::startVideoCapture()
pixhawk's avatar
pixhawk committed
702 703 704 705 706
{
    QString format = "bmp";
    QString initialPath = QDir::currentPath() + tr("/untitled.") + format;

    QString screenFileName = QFileDialog::getSaveFileName(this, tr("Save As"),
lm's avatar
lm committed
707 708 709 710
                                                          initialPath,
                                                          tr("%1 Files (*.%2);;All Files (*)")
                                                          .arg(format.toUpper())
                                                          .arg(format));
pixhawk's avatar
pixhawk committed
711 712
    delete videoTimer;
    videoTimer = new QTimer(this);
713 714 715
    //videoTimer->setInterval(40);
    //connect(videoTimer, SIGNAL(timeout()), this, SLOT(saveScreen()));
    //videoTimer->stop();
pixhawk's avatar
pixhawk committed
716 717
}

pixhawk's avatar
pixhawk committed
718
void MainWindow::stopVideoCapture()
pixhawk's avatar
pixhawk committed
719 720 721 722 723 724
{
    videoTimer->stop();

    // TODO Convert raw images to PNG
}

pixhawk's avatar
pixhawk committed
725
void MainWindow::saveScreen()
pixhawk's avatar
pixhawk committed
726 727 728 729
{
    QPixmap window = QPixmap::grabWindow(this->winId());
    QString format = "bmp";

730 731
    if (!screenFileName.isEmpty())
    {
pixhawk's avatar
pixhawk committed
732 733 734 735
        window.save(screenFileName, format.toAscii());
    }
}

736
void MainWindow::enableAutoReconnect(bool enabled)
pixhawk's avatar
pixhawk committed
737
{
738 739
    autoReconnect = enabled;
}
740

741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757
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)
{
758 759 760 761 762 763 764 765 766
    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."));
767
        }
768 769
    }
    break;
770 771 772 773 774 775 776 777 778 779 780 781 782 783
    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;
}

784
void MainWindow::selectStylesheet()
pixhawk's avatar
pixhawk committed
785
{
786
    // Let user select style sheet
787
    styleFileName = QFileDialog::getOpenFileName(this, tr("Specify stylesheet"), styleFileName, tr("CSS Stylesheet (*.css);;"));
788

789
    if (!styleFileName.endsWith(".css")) {
790 791 792 793 794 795 796 797 798 799
        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
800
    // Load style sheet
801 802 803 804 805 806 807
    reloadStylesheet();
}

void MainWindow::reloadStylesheet()
{
    // Load style sheet
    QFile* styleSheet = new QFile(styleFileName);
808
    if (!styleSheet->exists()) {
809 810
        styleSheet = new QFile(":/images/style-mission.css");
    }
811
    if (styleSheet->open(QIODevice::ReadOnly | QIODevice::Text)) {
812 813
        QString style = QString(styleSheet->readAll());
        style.replace("ICONDIR", QCoreApplication::applicationDirPath()+ "/images/");
pixhawk's avatar
pixhawk committed
814
        qApp->setStyleSheet(style);
815
    } else {
816 817 818
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Information);
        msgBox.setText(tr("QGroundControl did lot load a new style"));
819
        msgBox.setInformativeText(tr("Stylesheet file %1 was not readable").arg(styleFileName));
820 821 822
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
pixhawk's avatar
pixhawk committed
823
    }
824
    delete styleSheet;
pixhawk's avatar
pixhawk committed
825 826
}

827 828 829 830 831 832
/**
 * 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
833
void MainWindow::showStatusMessage(const QString& status, int timeout)
pixhawk's avatar
pixhawk committed
834
{
lm's avatar
lm committed
835
    statusBar()->showMessage(status, timeout);
pixhawk's avatar
pixhawk committed
836 837
}

838 839 840 841 842 843
/**
 * 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
 */
844
void MainWindow::showStatusMessage(const QString& status)
pixhawk's avatar
pixhawk committed
845
{
lm's avatar
lm committed
846
    statusBar()->showMessage(status, 20000);
847 848 849 850
}

void MainWindow::showCriticalMessage(const QString& title, const QString& message)
{
851
    QMessageBox msgBox(this);
852 853 854 855 856 857
    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
858 859
}

lm's avatar
lm committed
860 861
void MainWindow::showInfoMessage(const QString& title, const QString& message)
{
862
    QMessageBox msgBox(this);
lm's avatar
lm committed
863 864 865 866 867 868 869 870
    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
871 872 873 874
/**
* @brief Create all actions associated to the main window
*
**/
875
void MainWindow::connectCommonActions()
pixhawk's avatar
pixhawk committed
876
{
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
    // 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
900 901 902 903 904
    // 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*)));
905
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
pixhawk's avatar
pixhawk committed
906

907
    // Unmanned System controls
pixhawk's avatar
pixhawk committed
908 909 910 911
    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()));
912
    connect(ui.actionShutdownMAV, SIGNAL(triggered()), UASManager::instance(), SLOT(shutdownActiveUAS()));
pixhawk's avatar
pixhawk committed
913 914
    connect(ui.actionConfiguration, SIGNAL(triggered()), UASManager::instance(), SLOT(configureActiveUAS()));

915
    // Views actions
916
    connect(ui.actionPilotsView, SIGNAL(triggered()), this, SLOT(loadPilotView()));
917
    connect(ui.actionEngineersView, SIGNAL(triggered()), this, SLOT(loadEngineerView()));
918
    connect(ui.actionOperatorsView, SIGNAL(triggered()), this, SLOT(loadOperatorView()));
919
    connect(ui.actionUnconnectedView, SIGNAL(triggered()), this, SLOT(loadUnconnectedView()));
920

921
    connect(ui.actionMavlinkView, SIGNAL(triggered()), this, SLOT(loadMAVLinkView()));
922 923
    connect(ui.actionReloadStylesheet, SIGNAL(triggered()), this, SLOT(reloadStylesheet()));
    connect(ui.actionSelectStylesheet, SIGNAL(triggered()), this, SLOT(selectStylesheet()));
924

925
    // Help Actions
926
    connect(ui.actionOnline_Documentation, SIGNAL(triggered()), this, SLOT(showHelp()));
927
    connect(ui.actionDeveloper_Credits, SIGNAL(triggered()), this, SLOT(showCredits()));
928
    connect(ui.actionProject_Roadmap_2, SIGNAL(triggered()), this, SLOT(showRoadMap()));
929 930 931

    // Custom widget actions
    connect(ui.actionNewCustomWidget, SIGNAL(triggered()), this, SLOT(createCustomWidget()));
932
    connect(ui.actionLoadCustomWidgetFile, SIGNAL(triggered()), this, SLOT(loadCustomWidget()));
933 934 935

    // Audio output
    ui.actionMuteAudioOutput->setChecked(GAudioOutput::instance()->isMuted());
936
    connect(GAudioOutput::instance(), SIGNAL(mutedChanged(bool)), ui.actionMuteAudioOutput, SLOT(setChecked(bool)));
937
    connect(ui.actionMuteAudioOutput, SIGNAL(triggered(bool)), GAudioOutput::instance(), SLOT(mute(bool)));
938 939 940 941 942 943 944

    // 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);
945 946 947

    // Configuration
    // Joystick
948
    connect(ui.actionJoystickSettings, SIGNAL(triggered()), this, SLOT(configure()));
949 950
    // Application Settings
    connect(ui.actionSettings, SIGNAL(triggered()), this, SLOT(showSettings()));
951 952
}

953 954
void MainWindow::showHelp()
{
lm's avatar
lm committed
955
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/users/start"))) {
956 957 958 959 960 961 962 963 964 965 966 967
        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()
{
968
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/credits"))) {
969 970 971 972 973 974 975 976 977 978 979 980
        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
981
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/dev/roadmap"))) {
982 983 984 985 986 987 988 989 990 991
        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
992
void MainWindow::configure()
pixhawk's avatar
pixhawk committed
993
{
994 995
    if (!joystickWidget) {
        if (!joystick->isRunning()) {
996 997 998 999 1000
            joystick->start();
        }
        joystickWidget = new JoystickWidget(joystick);
    }
    joystickWidget->show();
pixhawk's avatar
pixhawk committed
1001 1002
}

1003 1004 1005 1006 1007 1008
void MainWindow::showSettings()
{
    QGCSettingsWidget* settings = new QGCSettingsWidget(this);
    settings->show();
}

pixhawk's avatar
pixhawk committed
1009
void MainWindow::addLink()
pixhawk's avatar
pixhawk committed
1010 1011 1012 1013
{
    SerialLink* link = new SerialLink();
    // TODO This should be only done in the dialog itself

1014
    LinkManager::instance()->add(link);
pixhawk's avatar
pixhawk committed
1015 1016
    LinkManager::instance()->addProtocol(link, mavlink);

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

lm's avatar
lm committed
1020 1021 1022 1023
    foreach (QAction* act, actions)
    {
        if (act->data().toInt() == LinkManager::instance()->getLinks().indexOf(link))
        {
1024 1025 1026 1027
            act->trigger();
            break;
        }
    }
pixhawk's avatar
pixhawk committed
1028 1029
}

pixhawk's avatar
pixhawk committed
1030 1031
void MainWindow::addLink(LinkInterface *link)
{
1032 1033 1034 1035
    // 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)
1036
    LinkManager::instance()->add(link);
1037
    LinkManager::instance()->addProtocol(link, mavlink);
1038

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

1042
    bool found = false;
1043

1044 1045
    foreach (QAction* act, actions) {
        if (act->data().toInt() == LinkManager::instance()->getLinks().indexOf(link)) {
1046 1047 1048 1049 1050
            found = true;
        }
    }

    UDPLink* udp = dynamic_cast<UDPLink*>(link);
1051

1052
    if (!found || udp) {
1053 1054 1055 1056 1057 1058 1059 1060
        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);
1061
        if (sim) {
1062 1063 1064
            //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
1065 1066 1067
    }
}

1068 1069 1070 1071 1072 1073 1074
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);
}

1075 1076 1077
void MainWindow::UASSpecsChanged(int uas)
{
    UASInterface* activeUAS = UASManager::instance()->getActiveUAS();
1078 1079
    if (activeUAS) {
        if (activeUAS->getUASID() == uas) {
1080 1081 1082 1083 1084
            ui.menuUnmanned_System->setTitle(activeUAS->getUASName());
        }
    }
}

pixhawk's avatar
pixhawk committed
1085
void MainWindow::UASCreated(UASInterface* uas)
pixhawk's avatar
pixhawk committed
1086
{
1087

pixhawk's avatar
pixhawk committed
1088 1089
    // Connect the UAS to the full user interface

lm's avatar
lm committed
1090 1091
    if (uas != NULL)
    {
1092
        // The pilot, operator and engineer views were not available on startup, enable them now
1093
        ui.actionPilotsView->setEnabled(true);
1094 1095
        ui.actionOperatorsView->setEnabled(true);
        ui.actionEngineersView->setEnabled(true);
1096 1097 1098 1099 1100 1101
        // 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);
1102

1103 1104
        QIcon icon;
        // Set matching icon
1105
        switch (uas->getSystemType()) {
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
        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;
        }

1129 1130 1131
        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()));
1132
        connect(uas, SIGNAL(systemSpecsChanged(int)), this, SLOT(UASSpecsChanged(int)));
1133 1134

        ui.menuConnected_Systems->addAction(uasAction);
1135 1136

        // FIXME Should be not inside the mainwindow
1137
        if (debugConsoleDockWidget) {
pixhawk's avatar
pixhawk committed
1138
            DebugConsole *debugConsole = dynamic_cast<DebugConsole*>(debugConsoleDockWidget->widget());
1139
            if (debugConsole) {
pixhawk's avatar
pixhawk committed
1140 1141 1142
                connect(uas, SIGNAL(textMessageReceived(int,int,int,QString)),
                        debugConsole, SLOT(receiveTextMessage(int,int,int,QString)));
            }
1143
        }
1144 1145

        // Health / System status indicator
1146
        if (infoDockWidget) {
pixhawk's avatar
pixhawk committed
1147
            UASInfoWidget *infoWidget = dynamic_cast<UASInfoWidget*>(infoDockWidget->widget());
1148
            if (infoWidget) {
pixhawk's avatar
pixhawk committed
1149 1150
                infoWidget->addUAS(uas);
            }
1151
        }
1152 1153

        // UAS List
lm's avatar
lm committed
1154 1155
        if (listDockWidget)
        {
pixhawk's avatar
pixhawk committed
1156
            UASListWidget *listWidget = dynamic_cast<UASListWidget*>(listDockWidget->widget());
lm's avatar
lm committed
1157 1158
            if (listWidget)
            {
pixhawk's avatar
pixhawk committed
1159 1160
                listWidget->addUAS(uas);
            }
1161
        }
1162

1163 1164
        // Line chart
        if (!linechartWidget)
lm's avatar
lm committed
1165
        {
1166 1167 1168
            // Center widgets
            linechartWidget   = new Linecharts(this);
            addCentralWidget(linechartWidget, tr("Realtime Plot"));
1169
        }
1170

LM's avatar
LM committed
1171
        // Load default custom widgets for this autopilot type
lm's avatar
lm committed
1172
        loadCustomWidgetsFromDefaults(uas->getSystemTypeName(), uas->getAutopilotTypeName());
1173 1174


lm's avatar
lm committed
1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191
        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);
            }

            if (!watchdogControlDockWidget) {
                watchdogControlDockWidget = new QDockWidget(tr("Process Control"), this);
                watchdogControlDockWidget->setWidget( new WatchdogControl(this) );
                watchdogControlDockWidget->setObjectName("WATCHDOG_CONTROL_DOCKWIDGET");
                addTool(watchdogControlDockWidget, tr("Process Control"), Qt::BottomDockWidgetArea);
            }
1192 1193 1194 1195 1196 1197
        }

        // 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
1198 1199
        if (UASManager::instance()->getUASList().size() == 1)
        {
1200
            // Load last view if setting is present
1201 1202
            if (settings.contains("CURRENT_VIEW_WITH_UAS_CONNECTED"))
            {
1203
                int view = settings.value("CURRENT_VIEW_WITH_UAS_CONNECTED").toInt();
1204
                switch (view) {
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
                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;
1221
                }
1222 1223 1224
            }
            else
            {
1225
                loadOperatorView();
1226
            }
1227
        }
1228

1229
    }
1230 1231

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

1233
    // Restore the mainwindow size
LM's avatar
LM committed
1234 1235
    if (settings.contains(getWindowGeometryKey()))
    {