MainWindow.cc 74.9 KB
Newer Older
1
2
3
4
/*=====================================================================

QGroundControl Open Source Ground Control Station

Lorenz Meier's avatar
Lorenz Meier committed
5
(c) 2009 - 2013 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Implementation of class MainWindow
 *   @author Lorenz Meier <mail@qgroundcontrol.org>
 */

#include <QSettings>
#include <QDockWidget>
#include <QNetworkInterface>
#include <QMessageBox>
#include <QDebug>
#include <QTimer>
#include <QHostInfo>
#include <QSplashScreen>
#include <QGCHilLink.h>
#include <QGCHilConfiguration.h>
#include <QGCHilFlightGearConfiguration.h>
41
#include <QDeclarativeView>
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include "QGC.h"
#include "MAVLinkSimulationLink.h"
#include "SerialLink.h"
#include "UDPLink.h"
#include "MAVLinkProtocol.h"
#include "CommConfigurationWindow.h"
#include "QGCWaypointListMulti.h"
#include "MainWindow.h"
#include "JoystickWidget.h"
#include "GAudioOutput.h"
#include "QGCToolWidget.h"
#include "QGCMAVLinkLogPlayer.h"
#include "QGCSettingsWidget.h"
#include "QGCMapTool.h"
#include "MAVLinkDecoder.h"
#include "QGCMAVLinkMessageSender.h"
#include "QGCRGBDView.h"
59
#include "QGCStatusBar.h"
60
#include "UASQuickView.h"
61
62
#include "QGCDataPlot2D.h"
#include "Linecharts.h"
63
64
65
#include "UASActionsWidget.h"
#include "QGCTabbedInfoView.h"
#include "UASRawStatusView.h"
66
#include "PrimaryFlightDisplay.h"
67
68
69
#include <apmtoolbar.h>
#include <ApmHardwareConfig.h>
#include <ApmSoftwareConfig.h>
70
#include <QGCConfigView.h>
71
72
#include "SerialSettingsDialog.h"
#include "terminalconsole.h"
73
#include "menuactionhelper.h"
74

75
76
77
78
79
// Add support for the MAVLink generator UI if it's been requested.
#ifdef QGC_MAVGEN_ENABLED
#include "XMLCommProtocolWidget.h"
#endif

80
81
82
83
84
85
86
87
88
89
#ifdef QGC_OSG_ENABLED
#include "Q3DWidgetFactory.h"
#endif

// FIXME Move
#include "PxQuadMAV.h"
#include "SlugsMAV.h"

#include "LogCompressor.h"

90
91
92
93
// Set up some constants
const QString MainWindow::defaultDarkStyle = ":files/styles/style-dark.css";
const QString MainWindow::defaultLightStyle = ":files/styles/style-light.css";

94
MainWindow* MainWindow::instance_mode(QSplashScreen* screen, enum MainWindow::CUSTOM_MODE mode)
95
96
{
    static MainWindow* _instance = 0;
97
    if (_instance == 0)
98
99
    {
        _instance = new MainWindow();
100
        _instance->setCustomMode(mode);
101
102
103
104
105
106
        if (screen)
        {
            connect(_instance, SIGNAL(initStatusChanged(QString,int,QColor)),
                    screen, SLOT(showMessage(QString,int,QColor)));
        }
        _instance->init();
107
108
109
110
    }
    return _instance;
}

111
112
MainWindow* MainWindow::instance(QSplashScreen* screen)
{
113
    return instance_mode(screen, CUSTOM_MODE_UNCHANGED);
114
115
}

116
117
118
119
120
121
122
123
124
/**
* 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()
**/
MainWindow::MainWindow(QWidget *parent):
    QMainWindow(parent),
125
    currentView(VIEW_FLIGHT),
126
    currentStyle(QGC_MAINWINDOW_STYLE_DARK),
127
128
    aboutToCloseFlag(false),
    changingViewsFlag(false),
Don Gagne's avatar
Don Gagne committed
129
    mavlink(new MAVLinkProtocol()),
130
    centerStackActionGroup(new QActionGroup(this)),
131
132
    darkStyleFileName(defaultDarkStyle),
    lightStyleFileName(defaultLightStyle),
133
    autoReconnect(false),
134
    simulationLink(NULL),
135
    lowPowerMode(false),
136
137
    customMode(CUSTOM_MODE_NONE),
    menuActionHelper(new MenuActionHelper())
138
{
139
    this->setAttribute(Qt::WA_DeleteOnClose);
140
    connect(menuActionHelper, SIGNAL(needToShowDockWidget(QString,bool)),SLOT(showDockWidget(QString,bool)));
Lorenz Meier's avatar
Lorenz Meier committed
141
142
    //TODO:  move protocol outside UI
    connect(mavlink, SIGNAL(protocolStatusMessage(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection);
143
    loadSettings();
144
145
146
147
}

void MainWindow::init()
{
Lorenz Meier's avatar
Lorenz Meier committed
148

149
    emit initStatusChanged(tr("Loading style"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
150
    qApp->setStyle("plastique");
151
152
153
154
155
156
157
158
    if (currentStyle == QGC_MAINWINDOW_STYLE_LIGHT)
    {
        loadStyle(currentStyle, lightStyleFileName);
    }
    else
    {
        loadStyle(currentStyle, darkStyleFileName);
    }
Lorenz Meier's avatar
Lorenz Meier committed
159

160
161
    if (settings.contains("ADVANCED_MODE"))
    {
162
        menuActionHelper->setAdvancedMode(settings.value("ADVANCED_MODE").toBool());
163
164
    }

165
166
167
168
169
170
171
172
173
174
    if (!settings.contains("CURRENT_VIEW"))
    {
        // Set this view as default view
        settings.setValue("CURRENT_VIEW", currentView);
    }
    else
    {
        // LOAD THE LAST VIEW
        VIEW_SECTIONS currentViewCandidate = (VIEW_SECTIONS) settings.value("CURRENT_VIEW", currentView).toInt();
        if (currentViewCandidate != VIEW_ENGINEER &&
175
176
                currentViewCandidate != VIEW_MISSION &&
                currentViewCandidate != VIEW_FLIGHT &&
177
178
179
180
181
182
183
                currentViewCandidate != VIEW_FULL)
        {
            currentView = currentViewCandidate;
        }
    }

    settings.sync();
184
    emit initStatusChanged(tr("Setting up user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
185
186
187
188

    // Setup user interface
    ui.setupUi(this);
    hide();
189
    menuActionHelper->setMenu(ui.menuTools);
190

Lorenz Meier's avatar
Lorenz Meier committed
191
    // We only need this menu if we have more than one system
192
    //    ui.menuConnected_Systems->setEnabled(false);
Lorenz Meier's avatar
Lorenz Meier committed
193

194
195
196
197
198
199
    // Set dock options
    setDockOptions(AnimatedDocks | AllowTabbedDocks | AllowNestedDocks);

    configureWindowName();

    // Setup corners
200
    setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
201
202

    // Setup UI state machines
203
    centerStackActionGroup->setExclusive(true);
204
205
206
207

    centerStack = new QStackedWidget(this);
    setCentralWidget(centerStack);

208

209
    // Load Toolbar
210
    if (!(getCustomMode() == CUSTOM_MODE_APM)) {
211
212
213
        toolBar = new QGCToolBar(this);
        this->addToolBar(toolBar);

214
215
        ui.actionHardwareConfig->setText(tr("Config"));

216
217
218
219
220
221
222
223
224
225
226
227
228
229
        // Add actions for average users (displayed next to each other)
        QList<QAction*> actions;
        actions << ui.actionFlightView;
        actions << ui.actionMissionView;
        actions << ui.actionHardwareConfig;
        toolBar->setPerspectiveChangeActions(actions);

        // Add actions for advanced users (displayed in dropdown under "advanced")
        QList<QAction*> advancedActions;
        advancedActions << ui.actionSimulation_View;
        advancedActions << ui.actionEngineersView;

        toolBar->setPerspectiveChangeAdvancedActions(advancedActions);
    }
230

231
    customStatusBar = new QGCStatusBar(this);
232
233
    setStatusBar(customStatusBar);
    statusBar()->setSizeGripEnabled(true);
234

235
    emit initStatusChanged(tr("Building common widgets."), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
236
237
238
239

    buildCommonWidgets();
    connectCommonWidgets();

240
    emit initStatusChanged(tr("Building common actions"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
241
242
243
244
245

    // Create actions
    connectCommonActions();

    // Populate link menu
246
    emit initStatusChanged(tr("Populating link menu"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
247
248
249
250
251
252
253
254
    QList<LinkInterface*> links = LinkManager::instance()->getLinks();
    foreach(LinkInterface* link, links)
    {
        this->addLink(link);
    }

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

255
    if (getCustomMode() == CUSTOM_MODE_APM) {
256
257
258
259
260
261
262
263
        // Add the APM 'toolbar'

        APMToolBar *apmToolBar = new APMToolBar(this);
        apmToolBar->setFlightViewAction(ui.actionFlightView);
        apmToolBar->setFlightPlanViewAction(ui.actionMissionView);
        apmToolBar->setHardwareViewAction(ui.actionHardwareConfig);
        apmToolBar->setSoftwareViewAction(ui.actionSoftwareConfig);
        apmToolBar->setSimulationViewAction(ui.actionSimulation_View);
Lorenz Meier's avatar
Lorenz Meier committed
264
        apmToolBar->setTerminalViewAction(ui.actionTerminalView);
265
266
267
268
269
270
271
272
273
274
275

        QDockWidget *widget = new QDockWidget(tr("APM Tool Bar"),this);
        widget->setWidget(apmToolBar);
        widget->setMinimumHeight(72);
        widget->setMaximumHeight(72);
        widget->setMinimumWidth(1024);
        widget->setFeatures(QDockWidget::NoDockWidgetFeatures);
        widget->setTitleBarWidget(new QWidget(this)); // Disables the title bar
    //    /*widget*/->setStyleSheet("QDockWidget { border: 0px solid #FFFFFF; border-radius: 0px; border-bottom: 0px;}");
        this->addDockWidget(Qt::TopDockWidgetArea, widget);
    }
276

277
    // Connect user interface devices
278
    emit initStatusChanged(tr("Initializing joystick interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
279
280
281
    joystickWidget = 0;
    joystick = new JoystickInput();

282
#ifdef QGC_MOUSE_ENABLED_WIN
283
    emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
284
285
286

    mouseInput = new Mouse3DInput(this);
    mouse = new Mouse6dofInput(mouseInput);
287
#endif //QGC_MOUSE_ENABLED_WIN
288

289
#if QGC_MOUSE_ENABLED_LINUX
290
    emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
291
292

    mouse = new Mouse6dofInput(this);
293
    connect(this, SIGNAL(x11EventOccured(XEvent*)), mouse, SLOT(handleX11Event(XEvent*)));
294
#endif //QGC_MOUSE_ENABLED_LINUX
295

296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
    // Connect link
    if (autoReconnect)
    {
        SerialLink* link = new SerialLink();
        // Add to registry
        LinkManager::instance()->add(link);
        LinkManager::instance()->addProtocol(link, mavlink);
        link->connect();
    }

    // Set low power mode
    enableLowPowerMode(lowPowerMode);

    // Initialize window state
    windowStateVal = windowState();

312
    emit initStatusChanged(tr("Restoring last view state"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
313
314
315
316

    // Restore the window setup
    loadViewState();

317
    emit initStatusChanged(tr("Restoring last window size"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
318
319
320
321
322
323
324
325
326
327
328
329
330
    // Restore the window position and size
    if (settings.contains(getWindowGeometryKey()))
    {
        // Restore the window geometry
        restoreGeometry(settings.value(getWindowGeometryKey()).toByteArray());
        show();
    }
    else
    {
        // Adjust the size
        const int screenWidth = QApplication::desktop()->width();
        const int screenHeight = QApplication::desktop()->height();

Lorenz Meier's avatar
Lorenz Meier committed
331
        if (screenWidth < 1500)
332
        {
333
334
            resize(screenWidth, screenHeight - 80);
            show();
335
336
337
338
339
340
341
342
343
344
345
        }
        else
        {
            resize(screenWidth*0.67f, qMin(screenHeight, (int)(screenWidth*0.67f*0.67f)));
            show();
        }

    }

    connect(&windowNameUpdateTimer, SIGNAL(timeout()), this, SLOT(configureWindowName()));
    windowNameUpdateTimer.start(15000);
346
    emit initStatusChanged(tr("Done"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
347
348
349
350
351
352
353
354
355
356
    show();
}

MainWindow::~MainWindow()
{
    if (mavlink)
    {
        delete mavlink;
        mavlink = NULL;
    }
357
358
359
360
361
    if (simulationLink)
    {
        delete simulationLink;
        simulationLink = NULL;
    }
362
363
    if (joystick)
    {
364
365
        joystick->shutdown();
        joystick->wait(5000);
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
        delete joystick;
        joystick = NULL;
    }

    // Get and delete all dockwidgets and contained
    // widgets
    QObjectList childList(this->children());

    QObjectList::iterator i;
    QDockWidget* dockWidget;
    for (i = childList.begin(); i != childList.end(); ++i)
    {
        dockWidget = dynamic_cast<QDockWidget*>(*i);
        if (dockWidget)
        {
            // Remove dock widget from main window
            // removeDockWidget(dockWidget);
            // delete dockWidget->widget();
            delete dockWidget;
            dockWidget = NULL;
        }
        else if (dynamic_cast<QWidget*>(*i))
        {
            delete dynamic_cast<QWidget*>(*i);
            *i = NULL;
        }
    }
    // Delete all UAS objects
394
395
    delete debugConsole;
    delete menuActionHelper;
396
397
398
399
    for (int i=0;i<commsWidgetList.size();i++)
    {
        commsWidgetList[i]->deleteLater();
    }
400
401
402
403
404
405
406
407
408
}

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

QString MainWindow::getWindowStateKey()
{
409
410
411
412
413
    if (UASManager::instance()->getActiveUAS())
    {
        return QString::number(currentView)+"_windowstate_" + UASManager::instance()->getActiveUAS()->getAutopilotTypeName();
    }
    else
414
        return QString::number(currentView)+"_windowstate";
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
}

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

void MainWindow::buildCustomWidget()
{
    // Create custom widgets
    QList<QGCToolWidget*> widgets = QGCToolWidget::createWidgetsFromSettings(this);

    if (widgets.size() > 0)
    {
        ui.menuTools->addSeparator();
    }

    for(int i = 0; i < widgets.size(); ++i)
    {
        // Check if this widget already has a parent, do not create it in this case
        QGCToolWidget* tool = widgets.at(i);
        QDockWidget* dock = dynamic_cast<QDockWidget*>(tool->parentWidget());
        if (!dock)
        {
440
441
442
            QSettings settings;
            settings.beginGroup("QGC_MAINWINDOW");

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

446
447
448
449
450
            //addDockWidget(location, dock);
            //dock->hide();
            int view = settings.value(QString("TOOL_PARENT_") + tool->objectName(),-1).toInt();
            //settings.setValue(QString("TOOL_PARENT_") + "UNNAMED_TOOL_" + QString::number(ui.menuTools->actions().size()),currentView);
            settings.endGroup();
Lorenz Meier's avatar
Lorenz Meier committed
451
452
453

            QDockWidget* dock;

454
455
456
            switch (view)
            {
            case VIEW_ENGINEER:
457
                dock = createDockWidget(engineeringView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
458
459
                break;
            case VIEW_FLIGHT:
Lorenz Meier's avatar
Lorenz Meier committed
460
                dock = createDockWidget(pilotView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
461
462
                break;
            case VIEW_SIMULATION:
Lorenz Meier's avatar
Lorenz Meier committed
463
                dock = createDockWidget(simView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
464
465
                break;
            case VIEW_MISSION:
Lorenz Meier's avatar
Lorenz Meier committed
466
                dock = createDockWidget(plannerView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
467
                break;
468
469
470
            case VIEW_MAVLINK:
                dock = createDockWidget(mavlinkView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
                break;
Lorenz Meier's avatar
Lorenz Meier committed
471
472
473
474
475
476
            case VIEW_GOOGLEEARTH:
                dock = createDockWidget(googleEarthView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
                break;
            case VIEW_LOCAL3D:
                dock = createDockWidget(local3DView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
                break;
477
            default:
Lorenz Meier's avatar
Lorenz Meier committed
478
                dock = createDockWidget(centerStack->currentWidget(),tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
479
480
481
                break;
            }

Lorenz Meier's avatar
Lorenz Meier committed
482
483
484
            // XXX temporary "fix"
            dock->hide();

485
            //createDockWidget(0,tool,tool->getTitle(),tool->objectName(),view,location);
486
487
488
489
490
491
492
493
        }
    }
}

void MainWindow::buildCommonWidgets()
{
    // Add generic MAVLink decoder
    mavlinkDecoder = new MAVLinkDecoder(mavlink, this);
John Tapsell's avatar
John Tapsell committed
494
495
    connect(mavlinkDecoder, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)),
                      this, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)));
496

497
498
499
500
    // Log player
    logPlayer = new QGCMAVLinkLogPlayer(mavlink, customStatusBar);
    customStatusBar->setLogPlayer(logPlayer);

501
502
503
504
    // Center widgets
    if (!plannerView)
    {
        plannerView = new SubMainWindow(this);
505
        plannerView->setObjectName("VIEW_MISSION");
506
        plannerView->setCentralWidget(new QGCMapTool(this));
507
        addToCentralStackedWidget(plannerView, VIEW_MISSION, "Maps");
508
509
    }

510
    //pilotView (aka Flight or Mission View)
511
512
513
    if (!pilotView)
    {
        pilotView = new SubMainWindow(this);
514
        pilotView->setObjectName("VIEW_FLIGHT");
515
        pilotView->setCentralWidget(new QGCMapTool(this));
516
        addToCentralStackedWidget(pilotView, VIEW_FLIGHT, "Pilot");
517
    }
518

519
    if (getCustomMode() == CUSTOM_MODE_APM) {
520
521
522
523
524
        if (!configView)
        {
            configView = new SubMainWindow(this);
            configView->setObjectName("VIEW_HARDWARE_CONFIG");
            configView->setCentralWidget(new ApmHardwareConfig(this));
Lorenz Meier's avatar
Lorenz Meier committed
525
            addToCentralStackedWidget(configView, VIEW_HARDWARE_CONFIG, "Hardware");
526

527
528
529
530
531
532
        }
        if (!softwareConfigView)
        {
            softwareConfigView = new SubMainWindow(this);
            softwareConfigView->setObjectName("VIEW_SOFTWARE_CONFIG");
            softwareConfigView->setCentralWidget(new ApmSoftwareConfig(this));
Lorenz Meier's avatar
Lorenz Meier committed
533
534
535
536
537
538
539
540
541
            addToCentralStackedWidget(softwareConfigView, VIEW_SOFTWARE_CONFIG, "Software");
        }
        if (!terminalView)
        {
            terminalView = new SubMainWindow(this);
            terminalView->setObjectName("VIEW_TERMINAL");
            TerminalConsole *terminalConsole = new TerminalConsole(this);
            terminalView->setCentralWidget(terminalConsole);
            addToCentralStackedWidget(terminalView, VIEW_TERMINAL, tr("Terminal View"));
542
543
544
545
546
547
548
        }
    } else {
        if (!configView)
        {
            configView = new SubMainWindow(this);
            configView->setObjectName("VIEW_HARDWARE_CONFIG");
            configView->setCentralWidget(new QGCConfigView(this));
Lorenz Meier's avatar
Lorenz Meier committed
549
            addToCentralStackedWidget(configView, VIEW_HARDWARE_CONFIG, "Config");
550
        }
551
    }
552

553
554
555
556
    if (!engineeringView)
    {
        engineeringView = new SubMainWindow(this);
        engineeringView->setObjectName("VIEW_ENGINEER");
557
        engineeringView->setCentralWidget(new QGCDataPlot2D(this));
558
        addToCentralStackedWidget(engineeringView, VIEW_ENGINEER, tr("Logfile Plot"));
559
    }
560

561
562
// Add the MAVLink generator UI if it's been requested.
#ifdef QGC_MAVGEN_ENABLED
563
    if (!mavlinkView)
564
    {
565
566
567
        mavlinkView = new SubMainWindow(this);
        mavlinkView->setObjectName("VIEW_MAVLINK");
        mavlinkView->setCentralWidget(new XMLCommProtocolWidget(this));
568
        addToCentralStackedWidget(mavlinkView, VIEW_MAVLINK, tr("Mavlink Generator"));
569
    }
570
#endif
571

572
#if QGC_GOOGLE_EARTH_ENABLED
573
574
575
576
577
578
579
580
581
582
    if (!googleEarthView)
    {
        googleEarthView = new SubMainWindow(this);
        googleEarthView->setObjectName("VIEW_GOOGLEEARTH");
        googleEarthView->setCentralWidget(new QGCGoogleEarthView(this));
        addToCentralStackedWidget(googleEarthView, VIEW_GOOGLEEARTH, tr("Google Earth View"));
    }
#endif

#ifdef QGC_OSG_ENABLED
583
    if (!local3DView)
584
585
586
587
588
589
590
591
592
593
594
    {
        q3DWidget = Q3DWidgetFactory::get("PIXHAWK", this);
        q3DWidget->setObjectName("VIEW_3DWIDGET");

        local3DView = new SubMainWindow(this);
        local3DView->setObjectName("VIEW_LOCAL3D");
        local3DView->setCentralWidget(q3DWidget);
        addToCentralStackedWidget(local3DView, VIEW_LOCAL3D, tr("Local 3D View"));
    }
#endif

595
596
597
598
599
    if (!simView)
    {
        simView = new SubMainWindow(this);
        simView->setObjectName("VIEW_SIMULATOR");
        simView->setCentralWidget(new QGCMapTool(this));
600
        addToCentralStackedWidget(simView, VIEW_SIMULATION, tr("Simulation View"));
601
602
    }

603
    // Dock widgets
604
    createDockWidget(simView,new UASControlWidget(this),tr("Control"),"UNMANNED_SYSTEM_CONTROL_DOCKWIDGET",VIEW_SIMULATION,Qt::LeftDockWidgetArea);
605

606
    createDockWidget(plannerView,new UASListWidget(this),tr("Unmanned Systems"),"UNMANNED_SYSTEM_LIST_DOCKWIDGET",VIEW_MISSION,Qt::LeftDockWidgetArea);
607
    createDockWidget(plannerView,new QGCWaypointListMulti(this),tr("Mission Plan"),"WAYPOINT_LIST_DOCKWIDGET",VIEW_MISSION,Qt::BottomDockWidgetArea);
608

609
610
    createDockWidget(simView,new QGCWaypointListMulti(this),tr("Mission Plan"),"WAYPOINT_LIST_DOCKWIDGET",VIEW_SIMULATION,Qt::BottomDockWidgetArea);
    createDockWidget(engineeringView,new QGCMAVLinkInspector(mavlink,this),tr("MAVLink Inspector"),"MAVLINK_INSPECTOR_DOCKWIDGET",VIEW_ENGINEER,Qt::RightDockWidgetArea);
611

612
613
    createDockWidget(engineeringView,new ParameterInterface(this),tr("Onboard Parameters"),"PARAMETER_INTERFACE_DOCKWIDGET",VIEW_ENGINEER,Qt::RightDockWidgetArea);
    createDockWidget(simView,new ParameterInterface(this),tr("Onboard Parameters"),"PARAMETER_INTERFACE_DOCKWIDGET",VIEW_SIMULATION,Qt::RightDockWidgetArea);
614

615
    menuActionHelper->createToolAction(tr("Status Details"), "UAS_STATUS_DETAILS_DOCKWIDGET");
616

617
    {
618
619
620
621
        if (!debugConsole)
        {
            debugConsole = new DebugConsole();
            debugConsole->setWindowTitle("Communications Console");
622
            debugConsole->hide();
623
624
625
626
            QAction* tempAction = ui.menuTools->addAction(tr("Communication Console"));
            tempAction->setCheckable(true);
            connect(tempAction,SIGNAL(triggered(bool)),debugConsole,SLOT(setShown(bool)));
        }
627
628
    }
    createDockWidget(simView,new HSIDisplay(this),tr("Horizontal Situation"),"HORIZONTAL_SITUATION_INDICATOR_DOCKWIDGET",VIEW_SIMULATION,Qt::BottomDockWidgetArea);
629

630
631
632
    menuActionHelper->createToolAction(tr("Flight Display"), "HEAD_DOWN_DISPLAY_1_DOCKWIDGET");
    menuActionHelper->createToolAction(tr("Actuator Status"), "HEAD_DOWN_DISPLAY_2_DOCKWIDGET");
    menuActionHelper->createToolAction(tr("Radio Control"));
633

634
    createDockWidget(engineeringView,new HUD(320,240,this),tr("Video Downlink"),"HEAD_UP_DISPLAY_DOCKWIDGET",VIEW_ENGINEER,Qt::RightDockWidgetArea,QSize(this->width()/1.5,0));
635

636
637
    createDockWidget(simView,new PrimaryFlightDisplay(320,240,this),tr("Primary Flight Display"),"PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET",VIEW_SIMULATION,Qt::RightDockWidgetArea,QSize(this->width()/1.5,0));
    createDockWidget(pilotView,new PrimaryFlightDisplay(320,240,this),tr("Primary Flight Display"),"PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET",VIEW_FLIGHT,Qt::LeftDockWidgetArea,QSize(this->width()/1.8,0));
638

639
    QGCTabbedInfoView *infoview = new QGCTabbedInfoView(this);
640
    infoview->addSource(mavlinkDecoder);
641
642
643
    createDockWidget(pilotView,infoview,tr("Info View"),"UAS_INFO_INFOVIEW_DOCKWIDGET",VIEW_FLIGHT,Qt::LeftDockWidgetArea);


644
645
    //createDockWidget(pilotView,new HUD(320,240,this),tr("Head Up Display"),"HEAD_UP_DISPLAY_DOCKWIDGET",VIEW_FLIGHT,Qt::LeftDockWidgetArea,this->width()/1.8);

646
647
648
649
650
//    createDockWidget(pilotView,new UASQuickView(this),tr("Quick View"),"UAS_INFO_QUICKVIEW_DOCKWIDGET",VIEW_FLIGHT,Qt::LeftDockWidgetArea);
//    createDockWidget(pilotView,new HSIDisplay(this),tr("Horizontal Situation"),"HORIZONTAL_SITUATION_INDICATOR_DOCKWIDGET",VIEW_FLIGHT,Qt::LeftDockWidgetArea);
//    pilotView->setTabPosition(Qt::LeftDockWidgetArea,QTabWidget::North);
//    pilotView->tabifyDockWidget((QDockWidget*)centralWidgetToDockWidgetsMap[VIEW_FLIGHT]["HORIZONTAL_SITUATION_INDICATOR_DOCKWIDGET"],(QDockWidget*)centralWidgetToDockWidgetsMap[VIEW_FLIGHT]["UAS_INFO_QUICKVIEW_DOCKWIDGET"]);

651
652
653
    //UASRawStatusView *view = new UASRawStatusView();
    //view->setDecoder(mavlinkDecoder);
    //view->show();
654
    //hddisplay->addSource(mavlinkDecoder);
655
656
657
658
    //createDockWidget(pilotView,new HSIDisplay(this),tr("Horizontal Situation"),"HORIZONTAL_SITUATION_INDICATOR_DOCKWIDGET",VIEW_FLIGHT,Qt::LeftDockWidgetArea);
    //pilotView->setTabPosition(Qt::LeftDockWidgetArea,QTabWidget::North);
    //pilotView->tabifyDockWidget((QDockWidget*)centralWidgetToDockWidgetsMap[VIEW_FLIGHT]["HORIZONTAL_SITUATION_INDICATOR_DOCKWIDGET"],(QDockWidget*)centralWidgetToDockWidgetsMap[VIEW_FLIGHT]["UAS_INFO_QUICKVIEW_DOCKWIDGET"]);

659

660
    //createDockWidget(pilotView,new UASActionsWidget(this),tr("Actions"),"UNMANNED_SYSTEM_ACTION_DOCKWIDGET",VIEW_FLIGHT,Qt::RightDockWidgetArea);
661
662
663
664
665

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


666
667

    /*if (!protocolWidget)
668
669
670
    {
        protocolWidget    = new XMLCommProtocolWidget(this);
        addCentralWidget(protocolWidget, "Mavlink Generator");
671
672
    }*/

673

674
675
676
677
678
    //    if (!firmwareUpdateWidget)
    //    {
    //        firmwareUpdateWidget    = new QGCFirmwareUpdate(this);
    //        addCentralWidget(firmwareUpdateWidget, "Firmware Update");
    //    }
679

680
    /*if (!hudWidget)
681
682
683
    {
        hudWidget         = new HUD(320, 240, this);
        addCentralWidget(hudWidget, tr("Head Up Display"));
684
    }*/
685

686
    /*if (!configWidget)
687
688
689
    {
        configWidget = new QGCVehicleConfig(this);
        addCentralWidget(configWidget, tr("Vehicle Configuration"));
690
691
    }*/

692

693
    /*if (!dataplotWidget)
694
695
696
    {
        dataplotWidget    = new QGCDataPlot2D(this);
        addCentralWidget(dataplotWidget, tr("Logfile Plot"));
697
    }*/
698
699
}

700
void MainWindow::addTool(SubMainWindow *parent,VIEW_SECTIONS view,QDockWidget* widget, const QString& title, Qt::DockWidgetArea area)
701
{
702
    menuActionHelper->createToolActionForCustomDockWidget(title, widget->objectName(), widget, view);
703
    parent->addDockWidget(area,widget);
704
705
}

706
QDockWidget* MainWindow::createDockWidget(QWidget *subMainWindowParent,QWidget *child,const QString& title,const QString& objectName,VIEW_SECTIONS view,Qt::DockWidgetArea area,const QSize& minSize)
707
{
708
709
710
711
712
713
    SubMainWindow *parent = qobject_cast<SubMainWindow*>(subMainWindowParent);
    Q_ASSERT(parent);
    QDockWidget* dockWidget = menuActionHelper->createDockWidget(title, objectName);
    child->setObjectName(objectName);
    dockWidget->setWidget(child); //Set child objectName before setting dockwidget, since the dock widget might react to object name changes
    connect(child, SIGNAL(destroyed()), dockWidget, SLOT(deleteLater()));  //Our dockwidget only has only child widget, so kill the dock widget if the child is deleted
Lorenz Meier's avatar
Lorenz Meier committed
714

715
716
717
718
    if (minSize.height() >= 0)
        dockWidget->setMinimumHeight(minSize.height());
    if (minSize.width() >= 0)
        dockWidget->setMinimumWidth(minSize.width());
719
720
    addTool(parent,view,dockWidget,title,area);
    return dockWidget;
721
}
722

723
724
725
726
727
728
729
void MainWindow::showDockWidget(const QString& name, bool show)
{
    QDockWidget *dockWidget = menuActionHelper->getDockWidget(currentView, name);
    if(dockWidget)
        dockWidget->setVisible(show);
    else if (show)
        loadDockWidget(name);
730
731
}

732
void MainWindow::loadDockWidget(const QString& name)
733
{
734
    if(menuActionHelper->containsDockWidget(currentView, name))
735
        return;
736
737
738
739
740
741
    if (name.startsWith("HIL_CONFIG"))
    {
        //It's a HIL widget.
        showHILConfigurationWidget(UASManager::instance()->getActiveUAS());
    }
    else if (name == "UNMANNED_SYSTEM_CONTROL_DOCKWIDGET")
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
    {
        createDockWidget(centerStack->currentWidget(),new UASControlWidget(this),tr("Control"),"UNMANNED_SYSTEM_CONTROL_DOCKWIDGET",currentView,Qt::LeftDockWidgetArea);
    }
    else if (name == "UNMANNED_SYSTEM_LIST_DOCKWIDGET")
    {
        createDockWidget(centerStack->currentWidget(),new UASListWidget(this),tr("Unmanned Systems"),"UNMANNED_SYSTEM_LIST_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    }
    else if (name == "WAYPOINT_LIST_DOCKWIDGET")
    {
        createDockWidget(centerStack->currentWidget(),new QGCWaypointListMulti(this),tr("Mission Plan"),"WAYPOINT_LIST_DOCKWIDGET",currentView,Qt::BottomDockWidgetArea);
    }
    else if (name == "MAVLINK_INSPECTOR_DOCKWIDGET")
    {
        createDockWidget(centerStack->currentWidget(),new QGCMAVLinkInspector(mavlink,this),tr("MAVLink Inspector"),"MAVLINK_INSPECTOR_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    }
    else if (name == "PARAMETER_INTERFACE_DOCKWIDGET")
    {
        createDockWidget(centerStack->currentWidget(),new ParameterInterface(this),tr("Onboard Parameters"),"PARAMETER_INTERFACE_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    }
    else if (name == "UAS_STATUS_DETAILS_DOCKWIDGET")
    {
        createDockWidget(centerStack->currentWidget(),new UASInfoWidget(this),tr("Status Details"),"UAS_STATUS_DETAILS_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    }
    else if (name == "COMMUNICATION_DEBUG_CONSOLE_DOCKWIDGET")
    {
767
        //This is now a permanently detached window.
768
769
770
771
772
773
774
    }
    else if (name == "HORIZONTAL_SITUATION_INDICATOR_DOCKWIDGET")
    {
        createDockWidget(centerStack->currentWidget(),new HSIDisplay(this),tr("Horizontal Situation"),"HORIZONTAL_SITUATION_INDICATOR_DOCKWIDGET",currentView,Qt::BottomDockWidgetArea);
    }
    else if (name == "HEAD_DOWN_DISPLAY_1_DOCKWIDGET")
    {
775
776
777
778
        QStringList acceptList;
        acceptList.append("-3.3,ATTITUDE.roll,rad,+3.3,s");
        acceptList.append("-3.3,ATTITUDE.pitch,deg,+3.3,s");
        acceptList.append("-3.3,ATTITUDE.yaw,deg,+3.3,s");
779
780
781
782
783
784
        HDDisplay *hddisplay = new HDDisplay(acceptList,"Flight Display",this);
        hddisplay->addSource(mavlinkDecoder);
        createDockWidget(centerStack->currentWidget(),hddisplay,tr("Flight Display"),"HEAD_DOWN_DISPLAY_1_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    }
    else if (name == "HEAD_DOWN_DISPLAY_2_DOCKWIDGET")
    {
785
786
787
        QStringList acceptList;
        acceptList.append("0,RAW_PRESSURE.pres_abs,hPa,65500");
        HDDisplay *hddisplay = new HDDisplay(acceptList,"Actuator Status",this);
788
789
790
791
792
793
794
795
        hddisplay->addSource(mavlinkDecoder);
        createDockWidget(centerStack->currentWidget(),hddisplay,tr("Actuator Status"),"HEAD_DOWN_DISPLAY_2_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    }
    else if (name == "Radio Control")
    {
        qDebug() << "Error loading window:" << name << "Unknown window type";
        //createDockWidget(centerStack->currentWidget(),hddisplay,tr("Actuator Status"),"HEADS_DOWN_DISPLAY_2_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    }
796
    else if (name == "PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET")
797
    {
Lorenz Meier's avatar
Lorenz Meier committed
798
799
800
801
802
        createDockWidget(centerStack->currentWidget(),new PrimaryFlightDisplay(320,240,this),tr("Primary Flight Display"),"PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    }
    else if (name == "HEAD_UP_DISPLAY_DOCKWIDGET")
    {
        createDockWidget(centerStack->currentWidget(),new HUD(320,240,this),tr("Head Up Display"),"HEAD_UP_DISPLAY_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
803
    }
804
805
806
807
    else if (name == "UAS_INFO_QUICKVIEW_DOCKWIDGET")
    {
        createDockWidget(centerStack->currentWidget(),new UASQuickView(this),tr("Quick View"),"UAS_INFO_QUICKVIEW_DOCKWIDGET",currentView,Qt::LeftDockWidgetArea);
    }
808
809
    else
    {
810
811
812
813
814
815
816
817
818
        if (customWidgetNameToFilenameMap.contains(name))
        {
            loadCustomWidget(customWidgetNameToFilenameMap[name],currentView);
            //customWidgetNameToFilenameMap.remove(name);
        }
        else
        {
            qDebug() << "Error loading window:" << name;
        }
819
820
    }
}
821

822
void MainWindow::addToCentralStackedWidget(QWidget* widget, VIEW_SECTIONS viewSection, const QString& title)
823
{
824
    Q_UNUSED(viewSection);
Bryant's avatar
Bryant committed
825
    Q_UNUSED(title);
826
    Q_ASSERT(widget->objectName().length() != 0);
827

828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
    // Check if this widget already has been added
    if (centerStack->indexOf(widget) == -1)
    {
        centerStack->addWidget(widget);
    }
}


void MainWindow::showCentralWidget()
{
    QAction* act = qobject_cast<QAction *>(sender());
    QWidget* widget = qVariantValue<QWidget *>(act->data());
    centerStack->setCurrentWidget(widget);
}

void MainWindow::showHILConfigurationWidget(UASInterface* uas)
{
    // Add simulation configuration widget
    UAS* mav = dynamic_cast<UAS*>(uas);

848
    if (mav && !hilDocks.contains(mav->getUASID()))
849
850
    {
        QGCHilConfiguration* hconf = new QGCHilConfiguration(mav, this);
851
        QString hilDockName = tr("HIL Config %1").arg(uas->getUASName());
852
853
        QString hilDockObjectName = QString("HIL_CONFIG_%1").arg(uas->getUASName().toUpper().replace(' ','_'));
        QDockWidget* hilDock = createDockWidget(simView, hconf,hilDockName, hilDockObjectName,VIEW_SIMULATION,Qt::LeftDockWidgetArea);
854
        hilDocks.insert(mav->getUASID(), hilDock);
855
856
857
858
859
860
861
    }
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    if (isVisible()) storeViewState();
    aboutToCloseFlag = true;
862
    storeSettings();
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
    mavlink->storeSettings();
    UASManager::instance()->storeSettings();
    QMainWindow::closeEvent(event);
}

/**
 * Connect the signals and slots of the common window widgets
 */
void MainWindow::connectCommonWidgets()
{
    if (infoDockWidget && infoDockWidget->widget())
    {
        connect(mavlink, SIGNAL(receiveLossChanged(int, float)),
                infoDockWidget->widget(), SLOT(updateSendLoss(int, float)));
    }
}

void MainWindow::createCustomWidget()
{
882
    if (QGCToolWidget::instances()->isEmpty())
883
884
885
886
    {
        // This is the first widget
        ui.menuTools->addSeparator();
    }
887
888
889
890
891
892
893
894
895
896
    QString objectName;
    int customToolIndex = 0;
    //Find the next unique object name that we can use
    do {
        ++customToolIndex;
        objectName = QString("CUSTOM_TOOL_%1").arg(customToolIndex) + "DOCK";
    } while(QGCToolWidget::instances()->contains(objectName));

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

897
898
899
    QGCToolWidget* tool = new QGCToolWidget(objectName, title);
    createDockWidget(centerStack->currentWidget(),tool,title,objectName,currentView,Qt::BottomDockWidgetArea);

900
901
902
903
    QSettings settings;
    settings.beginGroup("QGC_MAINWINDOW");
    settings.setValue(QString("TOOL_PARENT_") + tool->objectName(),currentView);
    settings.endGroup();
904
905
906
907
908
909
910
911
}

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));
    if (fileName != "") loadCustomWidget(fileName);
}
912
913
void MainWindow::loadCustomWidget(const QString& fileName, int view)
{
914
    QGCToolWidget* tool = new QGCToolWidget("", "", this);
915
916
917
918
919
920
    if (tool->loadSettings(fileName, true))
    {
        qDebug() << "Loading custom tool:" << tool->getTitle() << tool->objectName();
        switch ((VIEW_SECTIONS)view)
        {
        case VIEW_ENGINEER:
921
            createDockWidget(engineeringView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
922
923
924
925
926
927
928
929
930
931
932
            break;
        case VIEW_FLIGHT:
            createDockWidget(pilotView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
            break;
        case VIEW_SIMULATION:
            createDockWidget(simView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
            break;
        case VIEW_MISSION:
            createDockWidget(plannerView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
            break;
        default:
933
            {
934
935
            //Delete tool, create menu item to tie it to.
            customWidgetNameToFilenameMap[tool->objectName()+"DOCK"] = fileName;
936
            menuActionHelper->createToolAction(tool->getTitle(), tool->objectName()+"DOCK");
937
            tool->deleteLater();
938
            }
939
940
941
942
943
944
945
946
            break;
        }
    }
    else
    {
        return;
    }
}
947
948
949

void MainWindow::loadCustomWidget(const QString& fileName, bool singleinstance)
{
950
    QGCToolWidget* tool = new QGCToolWidget("", "", this);
951
952
    if (tool->loadSettings(fileName, true) || !singleinstance)
    {
953
954
955
956
957
958
959
960
961
        qDebug() << "Loading custom tool:" << tool->getTitle() << tool->objectName();
        QSettings settings;
        settings.beginGroup("QGC_MAINWINDOW");
        //settings.setValue(QString("TOOL_PARENT_") + "UNNAMED_TOOL_" + QString::number(ui.menuTools->actions().size()),currentView);

        int view = settings.value(QString("TOOL_PARENT_") + tool->objectName(),-1).toInt();
        switch (view)
        {
        case VIEW_ENGINEER:
962
            createDockWidget(engineeringView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
963
964
            break;
        case VIEW_FLIGHT:
965
            createDockWidget(pilotView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
966
967
            break;
        case VIEW_SIMULATION:
968
            createDockWidget(simView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
969
970
            break;
        case VIEW_MISSION:
971
            createDockWidget(plannerView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
972
973
            break;
        default:
974
            {
975
976
            //Delete tool, create menu item to tie it to.
            customWidgetNameToFilenameMap[tool->objectName()+"DOCK"] = fileName;
977
978
            QAction *action = menuActionHelper->createToolAction(tool->getTitle(), tool->objectName()+"DOCK");
            ui.menuTools->addAction(action);
979
            tool->deleteLater();
980
            }
981
982
983
984
985
            break;
        }


        settings.endGroup();
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
    }
    else
    {
        return;
    }
}

void MainWindow::loadCustomWidgetsFromDefaults(const QString& systemType, const QString& autopilotType)
{
    QString defaultsDir = qApp->applicationDirPath() + "/files/" + autopilotType.toLower() + "/widgets/";
    QString platformDir = qApp->applicationDirPath() + "/files/" + autopilotType.toLower() + "/" + systemType.toLower() + "/widgets/";

    QDir widgets(defaultsDir);
    QStringList files = widgets.entryList();
    QDir platformWidgets(platformDir);
For faster browsing, not all history is shown. View entire blame