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

117
    setDefaultSettingsForAp();
118

119 120
    settings.sync();

121 122 123
    // Setup UI state machines
    centerStackActionGroup.setExclusive(true);

pixhawk's avatar
pixhawk committed
124 125 126
    // Setup user interface
    ui.setupUi(this);

127 128
    centerStack = new QStackedWidget(this);
    setCentralWidget(centerStack);
129

130 131
    buildCommonWidgets();
    connectCommonWidgets();
132 133

    configureWindowName();
pixhawk's avatar
pixhawk committed
134

135
    loadStyle(currentStyle);
136

137
    // Create actions
138
    connectCommonActions();
139

140 141 142
    // Set dock options
    setDockOptions(AnimatedDocks | AllowTabbedDocks | AllowNestedDocks);

143
    // Load mavlink view as default widget set
144
    //loadMAVLinkView();
145

lm's avatar
lm committed
146 147
    statusBar()->setSizeGripEnabled(true);

148 149 150 151 152 153
    // Restore the window setup
    if (settings.contains(getWindowStateKey()))
    {
        loadViewState();
    }

154
    // Restore the window position and size
155 156
    if (settings.contains(getWindowGeometryKey()))
    {
157
        // Restore the window geometry
158
        restoreGeometry(settings.value(getWindowGeometryKey()).toByteArray());
159 160 161
    }
    else
    {
162 163 164
        // Adjust the size
        adjustSize();
    }
pixhawk's avatar
pixhawk committed
165

166 167
    // Populate link menu
    QList<LinkInterface*> links = LinkManager::instance()->getLinks();
168 169
    foreach(LinkInterface* link, links)
    {
170 171
        this->addLink(link);
    }
172

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

175
    // Connect user interface devices
176 177
    joystickWidget = 0;
    joystick = new JoystickInput();
178

lm's avatar
lm committed
179 180 181 182 183
    // Connect flighgear test link
    // FIXME MOVE INTO UAV OBJECT
    fgLink = new QGCFlightGearLink();
    fgLink->connectSimulation();

184 185 186 187 188 189 190 191
    // Load Toolbar
    toolBar = new QGCToolBar(this);
    this->addToolBar(toolBar);
    // Add actions
    toolBar->addPerspectiveChangeAction(ui.actionOperatorsView);
    toolBar->addPerspectiveChangeAction(ui.actionEngineersView);
    toolBar->addPerspectiveChangeAction(ui.actionPilotsView);

192
    // Connect link
193 194
    if (autoReconnect)
    {
195 196 197 198 199 200
        SerialLink* link = new SerialLink();
        // Add to registry
        LinkManager::instance()->add(link);
        LinkManager::instance()->addProtocol(link, mavlink);
        link->connect();
    }
201

202 203 204
    // Set low power mode
    enableLowPowerMode(lowPowerMode);

205 206
    // Initialize window state
    windowStateVal = windowState();
207 208

    show();
pixhawk's avatar
pixhawk committed
209 210
}

pixhawk's avatar
pixhawk committed
211
MainWindow::~MainWindow()
pixhawk's avatar
pixhawk committed
212
{
213
    delete mavlink;
214
    delete joystick;
lm's avatar
lm committed
215

216 217 218 219 220 221
    // Get and delete all dockwidgets and contained
    // widgets
    QObjectList childList( this->children() );

    QObjectList::iterator i;
    QDockWidget* dockWidget;
222 223
    for (i = childList.begin(); i != childList.end(); ++i)
    {
224
        dockWidget = dynamic_cast<QDockWidget*>(*i);
225 226
        if (dockWidget)
        {
227 228 229 230 231
            // Remove dock widget from main window
            removeDockWidget(dockWidget);
            delete dockWidget->widget();
            delete dockWidget;
        }
LM's avatar
LM committed
232 233 234 235
        else
        {
            delete dynamic_cast<QObject*>(*i);
        }
236
    }
LM's avatar
LM committed
237 238

    // Delete all UAS objects
pixhawk's avatar
pixhawk committed
239 240
}

241 242 243 244 245
/**
 * Set default settings for this AP type.
 */
void MainWindow::setDefaultSettingsForAp()
{
LM's avatar
LM committed
246
    // Check if the settings exist, instantiate defaults if necessary
247

248

249 250 251 252
//    // UNCONNECTED VIEW DEFAULT
////    QString centralKey = buildMenuKey(SUB_SECTION_CHECKED, CENTRAL_MAP, VIEW_UNCONNECTED);
//    if (!settings.contains(centralKey)) {
//        settings.setValue(centralKey,true);
253

254 255 256 257 258
//        // ENABLE UAS LIST
////        settings.setValue(buildMenuKey(SUB_SECTION_CHECKED,MainWindow::MENU_UAS_LIST, VIEW_UNCONNECTED), true);
//        // ENABLE COMMUNICATION CONSOLE
////        settings.setValue(buildMenuKey(SUB_SECTION_CHECKED,MainWindow::MENU_DEBUG_CONSOLE, VIEW_UNCONNECTED), true);
//    }
259

260 261 262 263 264 265 266 267 268 269 270 271
//    // OPERATOR VIEW DEFAULT
////    centralKey = buildMenuKey(SUB_SECTION_CHECKED, CENTRAL_MAP, VIEW_OPERATOR);
//    if (!settings.contains(centralKey)) {
////        settings.setValue(centralKey,true);

//        // ENABLE UAS LIST
////        settings.setValue(buildMenuKey(SUB_SECTION_CHECKED,MainWindow::MENU_UAS_LIST,VIEW_OPERATOR), true);
//        // ENABLE HUD TOOL WIDGET
////        settings.setValue(buildMenuKey(SUB_SECTION_CHECKED,MainWindow::MENU_HUD,VIEW_OPERATOR), true);
//        // ENABLE WAYPOINTS
////        settings.setValue(buildMenuKey(SUB_SECTION_CHECKED,MainWindow::MENU_WAYPOINTS,VIEW_OPERATOR), true);
//    }
272

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
//    // ENGINEER VIEW DEFAULT
//    centralKey = buildMenuKey(SUB_SECTION_CHECKED, CENTRAL_LINECHART, VIEW_ENGINEER);
//    if (!settings.contains(centralKey)) {
//        settings.setValue(centralKey,true);
//        // Enable Parameter widget
//        settings.setValue(buildMenuKey(SUB_SECTION_CHECKED,MainWindow::MENU_PARAMETERS,VIEW_ENGINEER), true);
//    }

//    // MAVLINK VIEW DEFAULT
//    centralKey = buildMenuKey(SUB_SECTION_CHECKED, CENTRAL_PROTOCOL, VIEW_MAVLINK);
//    if (!settings.contains(centralKey)) {
//        settings.setValue(centralKey,true);
//    }

//    // PILOT VIEW DEFAULT
//    centralKey = buildMenuKey(SUB_SECTION_CHECKED, CENTRAL_HUD, VIEW_PILOT);
//    if (!settings.contains(centralKey)) {
//        settings.setValue(centralKey,true);
//        // Enable Flight display
//        settings.setValue(buildMenuKey(SUB_SECTION_CHECKED,MainWindow::MENU_HDD_1,VIEW_PILOT), true);
//    }
294 295
}

lm's avatar
lm committed
296 297 298
void MainWindow::resizeEvent(QResizeEvent * event)
{
    Q_UNUSED(event);
299 300
    if (height() < 800)
    {
lm's avatar
lm committed
301
        ui.statusBar->setVisible(false);
302 303 304
    }
    else
    {
lm's avatar
lm committed
305
        ui.statusBar->setVisible(true);
306
        ui.statusBar->setSizeGripEnabled(true);
lm's avatar
lm committed
307 308 309
    }
}

310 311
QString MainWindow::getWindowStateKey()
{
312
    return QString::number(currentView)+"_windowstate";
313 314 315 316
}

QString MainWindow::getWindowGeometryKey()
{
317 318
    //return QString::number(currentView)+"_geometry";
    return "_geometry";
319 320
}

lm's avatar
lm committed
321 322 323
void MainWindow::buildCustomWidget()
{
    // Show custom widgets only if UAS is connected
324 325
    if (UASManager::instance()->getActiveUAS() != NULL)
    {
lm's avatar
lm committed
326 327 328
        // Create custom widgets
        QList<QGCToolWidget*> widgets = QGCToolWidget::createWidgetsFromSettings(this);

329 330
        if (widgets.size() > 0)
        {
lm's avatar
lm committed
331 332 333
            ui.menuTools->addSeparator();
        }

334 335
        for(int i = 0; i < widgets.size(); ++i)
        {
lm's avatar
lm committed
336
            // Check if this widget already has a parent, do not create it in this case
337 338
            QGCToolWidget* tool = widgets.at(i);
            QDockWidget* dock = dynamic_cast<QDockWidget*>(tool->parentWidget());
339 340
            if (!dock)
            {
341 342 343 344
                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
345
                QAction* showAction = new QAction(widgets.at(i)->windowTitle(), this);
346
                showAction->setCheckable(true);
lm's avatar
lm committed
347 348 349 350
                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);
351 352 353 354 355

                // 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
356 357 358 359 360
            }
        }
    }
}

361 362 363 364
void MainWindow::buildCommonWidgets()
{
    //TODO:  move protocol outside UI
    mavlink     = new MAVLinkProtocol();
365
    connect(mavlink, SIGNAL(protocolStatusMessage(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection);
366 367

    // Dock widgets
368 369
    if (!controlDockWidget)
    {
370
        controlDockWidget = new QDockWidget(tr("Control"), this);
371
        controlDockWidget->setObjectName("UNMANNED_SYSTEM_CONTROL_DOCKWIDGET");
372
        controlDockWidget->setWidget( new UASControlWidget(this) );
373
        addTool(controlDockWidget, tr("Control"), Qt::LeftDockWidgetArea);
374
    }
375

376 377
    if (!listDockWidget)
    {
378 379
        listDockWidget = new QDockWidget(tr("Unmanned Systems"), this);
        listDockWidget->setWidget( new UASListWidget(this) );
380
        listDockWidget->setObjectName("UNMANNED_SYSTEMS_LIST_DOCKWIDGET");
381
        addTool(listDockWidget, tr("Unmanned Systems"), Qt::RightDockWidgetArea);
382
    }
383

384 385
    if (!waypointsDockWidget)
    {
386
        waypointsDockWidget = new QDockWidget(tr("Mission Plan"), this);
387
        waypointsDockWidget->setWidget( new QGCWaypointListMulti(this) );
388
        waypointsDockWidget->setObjectName("WAYPOINT_LIST_DOCKWIDGET");
389
        addTool(waypointsDockWidget, tr("Mission Plan"), Qt::BottomDockWidgetArea);
390
    }
391

392 393
    if (!infoDockWidget)
    {
394 395
        infoDockWidget = new QDockWidget(tr("Status Details"), this);
        infoDockWidget->setWidget( new UASInfoWidget(this) );
pixhawk's avatar
pixhawk committed
396
        infoDockWidget->setObjectName("UAS_STATUS_DETAILS_DOCKWIDGET");
397
        addTool(infoDockWidget, tr("Status Details"), Qt::RightDockWidgetArea);
398
    }
399

400 401
    if (!debugConsoleDockWidget)
    {
402 403
        debugConsoleDockWidget = new QDockWidget(tr("Communication Console"), this);
        debugConsoleDockWidget->setWidget( new DebugConsole(this) );
404
        debugConsoleDockWidget->setObjectName("COMMUNICATION_DEBUG_CONSOLE_DOCKWIDGET");
405
        addTool(debugConsoleDockWidget, tr("Communication Console"), Qt::BottomDockWidgetArea);
406
    }
407

408 409
    if (!logPlayerDockWidget)
    {
410 411 412
        logPlayerDockWidget = new QDockWidget(tr("MAVLink Log Player"), this);
        logPlayerDockWidget->setWidget( new QGCMAVLinkLogPlayer(mavlink, this) );
        logPlayerDockWidget->setObjectName("MAVLINK_LOG_PLAYER_DOCKWIDGET");
413
        addTool(logPlayerDockWidget, tr("MAVLink Log Replay"), Qt::RightDockWidgetArea);
414 415
    }

416
    // Center widgets
417 418
    if (!mapWidget)
    {
419
        mapWidget = new QGCMapTool(this);
420
        addCentralWidget(mapWidget, "Maps");
421
    }
422

423 424
    if (!protocolWidget)
    {
425
        protocolWidget    = new XMLCommProtocolWidget(this);
426
        addCentralWidget(protocolWidget, "Mavlink Generator");
427
    }
lm's avatar
lm committed
428

429 430
    if (!dataplotWidget)
    {
lm's avatar
lm committed
431
        dataplotWidget    = new QGCDataPlot2D(this);
432
        addCentralWidget(dataplotWidget, "Logfile Plot");
lm's avatar
lm committed
433
    }
434

pixhawk's avatar
pixhawk committed
435 436
    //FIXME: memory of acceptList will never be freed again
    QStringList* acceptList = new QStringList();
pixhawk's avatar
pixhawk committed
437 438 439
    acceptList->append("-105,roll deg,deg,+105,s");
    acceptList->append("-105,pitch deg,deg,+105,s");
    acceptList->append("-105,heading deg,deg,+105,s");
440

pixhawk's avatar
pixhawk committed
441 442 443
    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");
444
    acceptList->append("0,airspeed,m/s,30");
445 446
    acceptList->append("0,groundspeed,m/s,30");
    acceptList->append("0,climbrate,m/s,30");
447
    acceptList->append("0,throttle,%,100");
448

pixhawk's avatar
pixhawk committed
449 450
    //FIXME: memory of acceptList2 will never be freed again
    QStringList* acceptList2 = new QStringList();
451 452 453 454 455 456 457 458
    acceptList2->append("900,servo #1,us,2100,s");
    acceptList2->append("900,servo #2,us,2100,s");
    acceptList2->append("900,servo #3,us,2100,s");
    acceptList2->append("900,servo #4,us,2100,s");
    acceptList2->append("900,servo #5,us,2100,s");
    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
459
    acceptList2->append("0,abs pressure,hPa,65500");
460 461 462
    //acceptList2->append("-2048,accel. x,raw,2048,s");
    //acceptList2->append("-2048,accel. y,raw,2048,s");
    //acceptList2->append("-2048,accel. z,raw,2048,s");
463

464
    if (!hudWidget) {
465
        hudWidget         = new HUD(320, 240, this);
466
        addCentralWidget(hudWidget, tr("Head Up Display"));
467
    }
468

469
    if (!dataplotWidget) {
470
        dataplotWidget    = new QGCDataPlot2D(this);
471
        addCentralWidget(dataplotWidget, tr("Logfile Plot"));
472
    }
473

474
#ifdef QGC_OSG_ENABLED
475
    if (!_3DWidget) {
476
        _3DWidget         = Q3DWidgetFactory::get("PIXHAWK");
477
        addCentralWidget(_3DWidget, tr("Local 3D"));
478
    }
479
#endif
lm's avatar
lm committed
480

481
#if (defined _MSC_VER) | (defined Q_OS_MAC)
482
    if (!gEarthWidget) {
483
        gEarthWidget = new QGCGoogleEarthView(this);
484
        addCentralWidget(gEarthWidget, tr("Google Earth"));
485
    }
486

487
#endif
488

pixhawk's avatar
pixhawk committed
489
    // Dock widgets
490

491
    if (!detectionDockWidget) {
492 493
        detectionDockWidget = new QDockWidget(tr("Object Recognition"), this);
        detectionDockWidget->setWidget( new ObjectDetectionView("images/patterns", this) );
pixhawk's avatar
pixhawk committed
494
        detectionDockWidget->setObjectName("OBJECT_DETECTION_DOCK_WIDGET");
495
        addTool(detectionDockWidget, tr("Object Recognition"), Qt::RightDockWidgetArea);
496
    }
497

498
    if (!parametersDockWidget) {
499
        parametersDockWidget = new QDockWidget(tr("Calibration and Onboard Parameters"), this);
500
        parametersDockWidget->setWidget( new ParameterInterface(this) );
pixhawk's avatar
pixhawk committed
501
        parametersDockWidget->setObjectName("PARAMETER_INTERFACE_DOCKWIDGET");
502
        addTool(parametersDockWidget, tr("Calibration and Parameters"), Qt::RightDockWidgetArea);
503
    }
504

505
    if (!watchdogControlDockWidget) {
506 507
        watchdogControlDockWidget = new QDockWidget(tr("Process Control"), this);
        watchdogControlDockWidget->setWidget( new WatchdogControl(this) );
pixhawk's avatar
pixhawk committed
508
        watchdogControlDockWidget->setObjectName("WATCHDOG_CONTROL_DOCKWIDGET");
509
        addTool(watchdogControlDockWidget, tr("Process Control"), Qt::BottomDockWidgetArea);
510
    }
511

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

519
    if (!headDown1DockWidget) {
520 521
        headDown1DockWidget = new QDockWidget(tr("Flight Display"), this);
        headDown1DockWidget->setWidget( new HDDisplay(acceptList, "Flight Display", this) );
522
        headDown1DockWidget->setObjectName("HEAD_DOWN_DISPLAY_1_DOCK_WIDGET");
523
        addTool(headDown1DockWidget, tr("Flight Display"), Qt::RightDockWidgetArea);
524
    }
525

526
    if (!headDown2DockWidget) {
527 528
        headDown2DockWidget = new QDockWidget(tr("Actuator Status"), this);
        headDown2DockWidget->setWidget( new HDDisplay(acceptList2, "Actuator Status", this) );
529
        headDown2DockWidget->setObjectName("HEAD_DOWN_DISPLAY_2_DOCK_WIDGET");
530
        addTool(headDown2DockWidget, tr("Actuator Status"), Qt::RightDockWidgetArea);
531
    }
532

533
    if (!rcViewDockWidget) {
534 535
        rcViewDockWidget = new QDockWidget(tr("Radio Control"), this);
        rcViewDockWidget->setWidget( new QGCRemoteControlView(this) );
536
        rcViewDockWidget->setObjectName("RADIO_CONTROL_CHANNELS_DOCK_WIDGET");
537
        addTool(rcViewDockWidget, tr("Radio Control"), Qt::BottomDockWidgetArea);
538
    }
539

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

547
    if (!video1DockWidget) {
pixhawk's avatar
pixhawk committed
548 549 550 551 552 553 554
        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");
555
        addTool(video1DockWidget, tr("Video Stream 1"), Qt::LeftDockWidgetArea);
pixhawk's avatar
pixhawk committed
556 557
    }

558
    if (!video2DockWidget) {
pixhawk's avatar
pixhawk committed
559 560 561 562 563 564 565
        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");
566
        addTool(video2DockWidget, tr("Video Stream 2"), Qt::LeftDockWidgetArea);
567
    }
568

LM's avatar
LM committed
569 570 571
    // Custom widgets, added last to all menus and layouts
    buildCustomWidget();

pixhawk's avatar
pixhawk committed
572 573
    // Dialogue widgets
    //FIXME: free memory in destructor
574

575
}
576

577
void MainWindow::addTool(QDockWidget* widget, const QString& title, Qt::DockWidgetArea area)
578
{
579
    QAction* tempAction = ui.menuTools->addAction(title);
580

581
    tempAction->setCheckable(true);
582 583 584 585
    QVariant var;
    var.setValue((QWidget*)widget);
    tempAction->setData(var);
    connect(tempAction,SIGNAL(triggered(bool)),this, SLOT(showTool(bool)));
LM's avatar
LM committed
586 587
    connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool)));
    tempAction->setChecked(widget->isVisible());
588
    addDockWidget(area, widget);
589 590 591
}


592
void MainWindow::showTool(bool show)
lm's avatar
lm committed
593
{
594 595 596
    QAction* act = qobject_cast<QAction *>(sender());
    QWidget* widget = qVariantValue<QWidget *>(act->data());
    widget->setVisible(show);
597
}
598

599
void MainWindow::addCentralWidget(QWidget* widget, const QString& title)
lm's avatar
lm committed
600
{
601 602 603 604
    // Check if this widget already has been added
    if (centerStack->indexOf(widget) == -1)
    {
        centerStack->addWidget(widget);
605

606
        QAction* tempAction = ui.menuMain->addAction(title);
607

608 609 610 611 612 613
        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
614 615
        connect(widget, SIGNAL(visibilityChanged(bool)), tempAction, SLOT(setChecked(bool)));
        tempAction->setChecked(widget->isVisible());
616 617 618 619
    }
}


620
void MainWindow::showCentralWidget()
621
{
622 623 624
    QAction* act = qobject_cast<QAction *>(sender());
    QWidget* widget = qVariantValue<QWidget *>(act->data());
    centerStack->setCurrentWidget(widget);
625 626
}

627 628
void MainWindow::closeEvent(QCloseEvent *event)
{
pixhawk's avatar
pixhawk committed
629
    if (isVisible()) storeViewState();
630
    storeSettings();
631
    aboutToCloseFlag = true;
632
    mavlink->storeSettings();
633
    UASManager::instance()->storeSettings();
634 635
    QMainWindow::closeEvent(event);
}
636 637 638 639

/**
 * Connect the signals and slots of the common window widgets
 */
640
void MainWindow::connectCommonWidgets()
641
{
642
    if (infoDockWidget && infoDockWidget->widget()) {
pixhawk's avatar
pixhawk committed
643 644 645
        connect(mavlink, SIGNAL(receiveLossChanged(int, float)),
                infoDockWidget->widget(), SLOT(updateSendLoss(int, float)));
    }
lm's avatar
lm committed
646 647 648 649 650
//    //TODO temporaly debug
//    if (slugsHilSimWidget && slugsHilSimWidget->widget()) {
//        connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)),
//                slugsHilSimWidget->widget(), SLOT(activeUasSet(UASInterface*)));
//    }
651
}
652

653 654
void MainWindow::createCustomWidget()
{
655 656
    QDockWidget* dock = new QDockWidget("Unnamed Tool", this);
    QGCToolWidget* tool = new QGCToolWidget("Unnamed Tool", dock);
lm's avatar
lm committed
657

658
    if (QGCToolWidget::instances()->size() < 2) {
lm's avatar
lm committed
659 660 661 662 663
        // This is the first widget
        ui.menuTools->addSeparator();
    }

    connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater()));
664
    dock->setWidget(tool);
665

666 667 668 669 670 671 672 673 674 675 676 677 678 679
    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));
680
    if (fileName != "") loadCustomWidget(fileName);
LM's avatar
LM committed
681
}
682

LM's avatar
LM committed
683 684 685 686
void MainWindow::loadCustomWidget(const QString& fileName, bool singleinstance)
{
    QGCToolWidget* tool = new QGCToolWidget("", this);
    if (tool->loadSettings(fileName, true) || !singleinstance)
687
    {
LM's avatar
LM committed
688 689 690 691
        // Add widget to UI
        QDockWidget* dock = new QDockWidget(tool->getTitle(), this);
        connect(tool, SIGNAL(destroyed()), dock, SLOT(deleteLater()));
        dock->setWidget(tool);
692
//        dock->setObjectName();
LM's avatar
LM committed
693 694
        tool->setParent(dock);

695
        QAction* showAction = new QAction(tool->getTitle(), this);
LM's avatar
LM committed
696 697 698 699 700 701 702 703 704 705 706
        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);
    }
    else
    {
        return;
707
    }
708 709
}

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

LM's avatar
LM committed
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
    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;
    }

    // Load all custom widgets found in the AP folder
    for(int i = 0; i < files.count(); ++i)
    {
        QString file = files[i];
        if (file.endsWith(".qgw"))
        {
            // Will only be loaded if not already a custom widget with
            // the same name is present
730 731
            qDebug() << "Tried to load file: " << file;
            loadCustomWidget(defaultsDir+"/"+file, true);
LM's avatar
LM committed
732 733
        }
    }
734 735
}

736 737 738 739 740 741
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();
742
    lowPowerMode = settings.value("LOW_POWER_MODE", lowPowerMode).toBool();
743 744 745 746 747 748 749 750 751 752
    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
753 754 755 756 757 758 759 760 761 762 763
    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
    }
764
    settings.setValue("LOW_POWER_MODE", lowPowerMode);
765 766 767
    settings.sync();
}

768 769
void MainWindow::configureWindowName()
{
pixhawk's avatar
pixhawk committed
770 771 772
    QList<QHostAddress> hostAddresses = QNetworkInterface::allAddresses();
    QString windowname = qApp->applicationName() + " " + qApp->applicationVersion();
    bool prevAddr = false;
773

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

776 777
    for (int i = 0; i < hostAddresses.size(); i++)
    {
pixhawk's avatar
pixhawk committed
778
        // Exclude loopback IPv4 and all IPv6 addresses
779 780
        if (hostAddresses.at(i) != QHostAddress("127.0.0.1") && !hostAddresses.at(i).toString().contains(":"))
        {
pixhawk's avatar
pixhawk committed
781 782 783 784 785
            if(prevAddr) windowname.append("/");
            windowname.append(hostAddresses.at(i).toString());
            prevAddr = true;
        }
    }
786

pixhawk's avatar
pixhawk committed
787
    windowname.append(")");
788

pixhawk's avatar
pixhawk committed
789
    setWindowTitle(windowname);
790 791

#ifndef Q_WS_MAC
pixhawk's avatar
pixhawk committed
792
    //qApp->setWindowIcon(QIcon(":/core/images/qtcreator_logo_128.png"));
793 794 795
#endif
}

pixhawk's avatar
pixhawk committed
796
void MainWindow::startVideoCapture()
pixhawk's avatar
pixhawk committed
797 798 799 800 801
{
    QString format = "bmp";
    QString initialPath = QDir::currentPath() + tr("/untitled.") + format;

    QString screenFileName = QFileDialog::getSaveFileName(this, tr("Save As"),
802 803 804 805
                             initialPath,
                             tr("%1 Files (*.%2);;All Files (*)")
                             .arg(format.toUpper())
                             .arg(format));
pixhawk's avatar
pixhawk committed
806 807
    delete videoTimer;
    videoTimer = new QTimer(this);
808 809 810
    //videoTimer->setInterval(40);
    //connect(videoTimer, SIGNAL(timeout()), this, SLOT(saveScreen()));
    //videoTimer->stop();
pixhawk's avatar
pixhawk committed
811 812
}

pixhawk's avatar
pixhawk committed
813
void MainWindow::stopVideoCapture()
pixhawk's avatar
pixhawk committed
814 815 816 817 818 819
{
    videoTimer->stop();

    // TODO Convert raw images to PNG
}

pixhawk's avatar
pixhawk committed
820
void MainWindow::saveScreen()
pixhawk's avatar
pixhawk committed
821 822 823 824
{
    QPixmap window = QPixmap::grabWindow(this->winId());
    QString format = "bmp";

825 826
    if (!screenFileName.isEmpty())
    {
pixhawk's avatar
pixhawk committed
827 828 829 830
        window.save(screenFileName, format.toAscii());
    }
}

831
void MainWindow::enableAutoReconnect(bool enabled)
pixhawk's avatar
pixhawk committed
832
{
833 834
    autoReconnect = enabled;
}
835

836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
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)
{
853 854 855 856 857 858 859 860 861
    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."));
862
        }
863 864
    }
    break;
865 866 867 868 869 870 871 872 873 874 875 876 877 878
    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;
}

879
void MainWindow::selectStylesheet()
pixhawk's avatar
pixhawk committed
880
{
881
    // Let user select style sheet
882
    styleFileName = QFileDialog::getOpenFileName(this, tr("Specify stylesheet"), styleFileName, tr("CSS Stylesheet (*.css);;"));
883

884
    if (!styleFileName.endsWith(".css")) {
885 886 887 888 889 890 891 892 893 894
        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
895
    // Load style sheet
896 897 898 899 900 901 902
    reloadStylesheet();
}

void MainWindow::reloadStylesheet()
{
    // Load style sheet
    QFile* styleSheet = new QFile(styleFileName);
903
    if (!styleSheet->exists()) {
904 905
        styleSheet = new QFile(":/images/style-mission.css");
    }
906
    if (styleSheet->open(QIODevice::ReadOnly | QIODevice::Text)) {
907 908
        QString style = QString(styleSheet->readAll());
        style.replace("ICONDIR", QCoreApplication::applicationDirPath()+ "/images/");
pixhawk's avatar
pixhawk committed
909
        qApp->setStyleSheet(style);
910
    } else {
911 912 913
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Information);
        msgBox.setText(tr("QGroundControl did lot load a new style"));
914
        msgBox.setInformativeText(tr("Stylesheet file %1 was not readable").arg(styleFileName));
915 916 917
        msgBox.setStandardButtons(QMessageBox::Ok);
        msgBox.setDefaultButton(QMessageBox::Ok);
        msgBox.exec();
pixhawk's avatar
pixhawk committed
918
    }
919
    delete styleSheet;
pixhawk's avatar
pixhawk committed
920 921
}

922 923 924 925 926 927
/**
 * 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
928
void MainWindow::showStatusMessage(const QString& status, int timeout)
pixhawk's avatar
pixhawk committed
929
{
lm's avatar
lm committed
930
    statusBar()->showMessage(status, timeout);
pixhawk's avatar
pixhawk committed
931 932
}

933 934 935 936 937 938
/**
 * 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
 */
939
void MainWindow::showStatusMessage(const QString& status)
pixhawk's avatar
pixhawk committed
940
{
lm's avatar
lm committed
941
    statusBar()->showMessage(status, 20000);
942 943 944 945
}

void MainWindow::showCriticalMessage(const QString& title, const QString& message)
{
946
    QMessageBox msgBox(this);
947 948 949 950 951 952
    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
953 954
}

lm's avatar
lm committed
955 956
void MainWindow::showInfoMessage(const QString& title, const QString& message)
{
957
    QMessageBox msgBox(this);
lm's avatar
lm committed
958 959 960 961 962 963 964 965
    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
966 967 968 969
/**
* @brief Create all actions associated to the main window
*
**/
970
void MainWindow::connectCommonActions()
pixhawk's avatar
pixhawk committed
971
{
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
    // 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
995 996 997 998 999
    // 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*)));
1000
    connect(UASManager::instance(), SIGNAL(activeUASSet(UASInterface*)), this, SLOT(setActiveUAS(UASInterface*)));
pixhawk's avatar
pixhawk committed
1001

1002
    // Unmanned System controls
pixhawk's avatar
pixhawk committed
1003 1004 1005 1006
    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()));
1007
    connect(ui.actionShutdownMAV, SIGNAL(triggered()), UASManager::instance(), SLOT(shutdownActiveUAS()));
pixhawk's avatar
pixhawk committed
1008 1009
    connect(ui.actionConfiguration, SIGNAL(triggered()), UASManager::instance(), SLOT(configureActiveUAS()));

1010
    // Views actions
1011
    connect(ui.actionPilotsView, SIGNAL(triggered()), this, SLOT(loadPilotView()));
1012
    connect(ui.actionEngineersView, SIGNAL(triggered()), this, SLOT(loadEngineerView()));
1013
    connect(ui.actionOperatorsView, SIGNAL(triggered()), this, SLOT(loadOperatorView()));
1014
    connect(ui.actionUnconnectedView, SIGNAL(triggered()), this, SLOT(loadUnconnectedView()));
1015

1016
    connect(ui.actionMavlinkView, SIGNAL(triggered()), this, SLOT(loadMAVLinkView()));
1017 1018
    connect(ui.actionReloadStylesheet, SIGNAL(triggered()), this, SLOT(reloadStylesheet()));
    connect(ui.actionSelectStylesheet, SIGNAL(triggered()), this, SLOT(selectStylesheet()));
1019

1020
    // Help Actions
1021
    connect(ui.actionOnline_Documentation, SIGNAL(triggered()), this, SLOT(showHelp()));
1022
    connect(ui.actionDeveloper_Credits, SIGNAL(triggered()), this, SLOT(showCredits()));
1023
    connect(ui.actionProject_Roadmap_2, SIGNAL(triggered()), this, SLOT(showRoadMap()));
1024 1025 1026

    // Custom widget actions
    connect(ui.actionNewCustomWidget, SIGNAL(triggered()), this, SLOT(createCustomWidget()));
1027
    connect(ui.actionLoadCustomWidgetFile, SIGNAL(triggered()), this, SLOT(loadCustomWidget()));
1028 1029 1030

    // Audio output
    ui.actionMuteAudioOutput->setChecked(GAudioOutput::instance()->isMuted());
1031
    connect(GAudioOutput::instance(), SIGNAL(mutedChanged(bool)), ui.actionMuteAudioOutput, SLOT(setChecked(bool)));
1032
    connect(ui.actionMuteAudioOutput, SIGNAL(triggered(bool)), GAudioOutput::instance(), SLOT(mute(bool)));
1033 1034 1035 1036 1037 1038 1039

    // 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);
1040 1041 1042

    // Configuration
    // Joystick
1043
    connect(ui.actionJoystickSettings, SIGNAL(triggered()), this, SLOT(configure()));
1044 1045
    // Application Settings
    connect(ui.actionSettings, SIGNAL(triggered()), this, SLOT(showSettings()));
1046 1047
}

1048 1049
void MainWindow::showHelp()
{
1050
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/users/start"))) {
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
        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()
{
1063
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/credits"))) {
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
        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()
{
1076
    if(!QDesktopServices::openUrl(QUrl("http://qgroundcontrol.org/dev/roadmap"))) {
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086
        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
1087
void MainWindow::configure()
pixhawk's avatar
pixhawk committed
1088
{
1089 1090
    if (!joystickWidget) {
        if (!joystick->isRunning()) {
1091 1092 1093 1094 1095
            joystick->start();
        }
        joystickWidget = new JoystickWidget(joystick);
    }
    joystickWidget->show();
pixhawk's avatar
pixhawk committed
1096 1097
}

1098 1099 1100 1101 1102 1103
void MainWindow::showSettings()
{
    QGCSettingsWidget* settings = new QGCSettingsWidget(this);
    settings->show();
}

pixhawk's avatar
pixhawk committed
1104
void MainWindow::addLink()
pixhawk's avatar
pixhawk committed
1105 1106 1107 1108
{
    SerialLink* link = new SerialLink();
    // TODO This should be only done in the dialog itself

1109
    LinkManager::instance()->add(link);
pixhawk's avatar
pixhawk committed
1110 1111
    LinkManager::instance()->addProtocol(link, mavlink);

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

1115 1116
    foreach (QAction* act, actions) {
        if (act->data().toInt() == LinkManager::instance()->getLinks().indexOf(link)) {
1117 1118 1119 1120
            act->trigger();
            break;
        }
    }
pixhawk's avatar
pixhawk committed
1121 1122
}

pixhawk's avatar
pixhawk committed
1123 1124
void MainWindow::addLink(LinkInterface *link)
{
1125 1126 1127 1128
    // 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)
1129
    LinkManager::instance()->add(link);
1130
    LinkManager::instance()->addProtocol(link, mavlink);
1131

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

1135
    bool found = false;
1136

1137 1138
    foreach (QAction* act, actions) {
        if (act->data().toInt() == LinkManager::instance()->getLinks().indexOf(link)) {
1139 1140 1141 1142 1143
            found = true;
        }
    }

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

1145
    if (!found || udp) {
1146 1147 1148 1149 1150 1151 1152 1153
        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);