Skip to content
Snippets Groups Projects
MainWindow.cc 68.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*=====================================================================
    
    QGroundControl Open Source Ground Control Station
    
    
    (c) 2009 - 2013 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/>.
    
    ======================================================================*/
    
    /**
     * @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>
    
    #include <QQuickView>
    
    #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"
    
    #include "QGCStatusBar.h"
    
    #include "QGCDataPlot2D.h"
    #include "Linecharts.h"
    
    #include "UASActionsWidget.h"
    #include "QGCTabbedInfoView.h"
    #include "UASRawStatusView.h"
    
    #include <apmtoolbar.h>
    #include <ApmHardwareConfig.h>
    #include <ApmSoftwareConfig.h>
    
    #include <QGCConfigView.h>
    
    #include "SerialSettingsDialog.h"
    #include "terminalconsole.h"
    
    #include "QGCUASFileViewMulti.h"
    
    #include <QDesktopWidget>
    
    
    #ifdef QGC_OSG_ENABLED
    #include "Q3DWidgetFactory.h"
    #endif
    
    // FIXME Move
    #include "PxQuadMAV.h"
    
    #include "LogCompressor.h"
    
    
    // Set up some constants
    const QString MainWindow::defaultDarkStyle = ":files/styles/style-dark.css";
    const QString MainWindow::defaultLightStyle = ":files/styles/style-light.css";
    
    
    MainWindow* MainWindow::instance_mode(QSplashScreen* screen, enum MainWindow::CUSTOM_MODE mode)
    
    {
        static MainWindow* _instance = 0;
    
        {
            _instance = new MainWindow();
    
            _instance->setCustomMode(mode);
    
            if (screen)
            {
                connect(_instance, SIGNAL(initStatusChanged(QString,int,QColor)),
                        screen, SLOT(showMessage(QString,int,QColor)));
            }
            _instance->init();
    
    MainWindow* MainWindow::instance(QSplashScreen* screen)
    {
    
        return instance_mode(screen, CUSTOM_MODE_UNCHANGED);
    
    /**
    * 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),
    
        currentView(VIEW_FLIGHT),
    
        currentStyle(QGC_MAINWINDOW_STYLE_DARK),
    
        aboutToCloseFlag(false),
        changingViewsFlag(false),
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink(new MAVLinkProtocol()),
    
        centerStackActionGroup(new QActionGroup(this)),
    
        darkStyleFileName(defaultDarkStyle),
        lightStyleFileName(defaultLightStyle),
    
        autoReconnect(false),
    
        simulationLink(NULL),
    
        customMode(CUSTOM_MODE_NONE),
        menuActionHelper(new MenuActionHelper())
    
        this->setAttribute(Qt::WA_DeleteOnClose);
    
        connect(menuActionHelper, SIGNAL(needToShowDockWidget(QString,bool)),SLOT(showDockWidget(QString,bool)));
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        //TODO:  move protocol outside UI
        connect(mavlink, SIGNAL(protocolStatusMessage(QString,QString)), this, SLOT(showCriticalMessage(QString,QString)), Qt::QueuedConnection);
    
        emit initStatusChanged(tr("Loading style"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
        if (currentStyle == QGC_MAINWINDOW_STYLE_LIGHT)
        {
            loadStyle(currentStyle, lightStyleFileName);
        }
        else
        {
            loadStyle(currentStyle, darkStyleFileName);
        }
    
            menuActionHelper->setAdvancedMode(settings.value("ADVANCED_MODE").toBool());
    
        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 &&
    
                    currentViewCandidate != VIEW_MISSION &&
                    currentViewCandidate != VIEW_FLIGHT &&
    
                    currentViewCandidate != VIEW_DEFAULT)
    
            {
                currentView = currentViewCandidate;
            }
        }
    
        settings.sync();
    
        emit initStatusChanged(tr("Setting up user interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
    
        // Setup user interface
        ui.setupUi(this);
    
        // Set dock options
        setDockOptions(AnimatedDocks | AllowTabbedDocks | AllowNestedDocks);
    
        configureWindowName();
    
        // Setup corners
    
        setCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
    
        // Qt 4 on Ubuntu does place the native menubar correctly so on Linux we revert back to in-window menu bar.
        // TODO: Check that this is still necessary on Qt5 on Ubuntu
    #ifdef Q_OS_LINUX
        menuBar()->setNativeMenuBar(false);
    #endif
    
    
        // Setup UI state machines
    
        centerStackActionGroup->setExclusive(true);
    
    
        centerStack = new QStackedWidget(this);
        setCentralWidget(centerStack);
    
    
        if (!(getCustomMode() == CUSTOM_MODE_APM)) {
    
            toolBar = new QGCToolBar(this);
            this->addToolBar(toolBar);
    
            // Add actions for average users (displayed next to each other)
            QList<QAction*> actions;
            actions << ui.actionMissionView;
    
            actions << ui.actionFlightView;
    
            toolBar->setPerspectiveChangeActions(actions);
    
            // Add actions for advanced users (displayed in dropdown under "advanced")
            QList<QAction*> advancedActions;
            advancedActions << ui.actionEngineersView;
    
            advancedActions << ui.actionGoogleEarthView;
            advancedActions << ui.actionLocal3DView;
            advancedActions << ui.actionSoftwareConfig;
            advancedActions << ui.actionTerminalView;
            advancedActions << ui.actionSimulationView;
    
    
            toolBar->setPerspectiveChangeAdvancedActions(advancedActions);
    
        } else {
            ui.actionHardwareConfig->setText(tr("Hardware"));
    
        customStatusBar = new QGCStatusBar(this);
    
        setStatusBar(customStatusBar);
        statusBar()->setSizeGripEnabled(true);
    
        emit initStatusChanged(tr("Building common widgets."), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
    
        buildCommonWidgets();
        connectCommonWidgets();
    
    
        emit initStatusChanged(tr("Building common actions"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
    
        // Create actions
        connectCommonActions();
    
        // Populate link menu
    
        emit initStatusChanged(tr("Populating link menu"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
        QList<LinkInterface*> links = LinkManager::instance()->getLinks();
        foreach(LinkInterface* link, links)
        {
            this->addLink(link);
        }
    
        connect(LinkManager::instance(), SIGNAL(newLink(LinkInterface*)), this, SLOT(addLink(LinkInterface*)));
    
    
        if (getCustomMode() == CUSTOM_MODE_APM) {
    
            APMToolBar *apmToolBar = new APMToolBar();
    
            apmToolBar->setFlightViewAction(ui.actionFlightView);
            apmToolBar->setFlightPlanViewAction(ui.actionMissionView);
            apmToolBar->setHardwareViewAction(ui.actionHardwareConfig);
            apmToolBar->setSoftwareViewAction(ui.actionSoftwareConfig);
    
            apmToolBar->setSimulationViewAction(ui.actionSimulationView);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
            apmToolBar->setTerminalViewAction(ui.actionTerminalView);
    
    
            QDockWidget *widget = new QDockWidget(tr("APM Tool Bar"),this);
    
            QWidget *toolbarWidget = QWidget::createWindowContainer(apmToolBar, this);
            widget->setWidget(toolbarWidget);
    
            widget->setMinimumHeight(72);
            widget->setMaximumHeight(72);
            widget->setMinimumWidth(1024);
            widget->setFeatures(QDockWidget::NoDockWidgetFeatures);
            widget->setTitleBarWidget(new QWidget(this)); // Disables the title bar
            this->addDockWidget(Qt::TopDockWidgetArea, widget);
        }
    
        // Connect user interface devices
    
        emit initStatusChanged(tr("Initializing joystick interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
        joystickWidget = NULL;
    
        joystick = new JoystickInput();
    
    
    #ifdef QGC_MOUSE_ENABLED_WIN
    
        emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
    
        mouseInput = new Mouse3DInput(this);
        mouse = new Mouse6dofInput(mouseInput);
    
    #endif //QGC_MOUSE_ENABLED_WIN
    
    #if QGC_MOUSE_ENABLED_LINUX
    
        emit initStatusChanged(tr("Initializing 3D mouse interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
    
        mouse = new Mouse6dofInput(this);
    
        connect(this, SIGNAL(x11EventOccured(XEvent*)), mouse, SLOT(handleX11Event(XEvent*)));
    
    #endif //QGC_MOUSE_ENABLED_LINUX
    
        // 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();
    
    
        emit initStatusChanged(tr("Restoring last view state"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
    
        // Restore the window setup
        loadViewState();
    
    
        emit initStatusChanged(tr("Restoring last window size"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
        // 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
            if (screenWidth < 1500)
    
                resize(screenWidth, screenHeight - 80);
                show();
    
            }
            else
            {
                resize(screenWidth*0.67f, qMin(screenHeight, (int)(screenWidth*0.67f*0.67f)));
                show();
            }
    
        }
    
    
        // Make sure the proper fullscreen/normal menu item is checked properly.
        if (isFullScreen())
        {
            ui.actionFullscreen->setChecked(true);
            ui.actionNormal->setChecked(false);
        }
        else
        {
            ui.actionFullscreen->setChecked(false);
            ui.actionNormal->setChecked(true);
        }
    
        // And that they will stay checked properly after user input
        QObject::connect(ui.actionFullscreen, SIGNAL(triggered()), this, SLOT(fullScreenActionItemCallback()));
        QObject::connect(ui.actionNormal, SIGNAL(triggered()), this,SLOT(normalActionItemCallback()));
    
    
    
        // Set OS dependent keyboard shortcuts for the main window, non OS dependent shortcuts are set in MainWindow.ui
    #ifdef Q_OS_MACX
    
        ui.actionMissionView->setShortcut(QApplication::translate("MainWindow", "Meta+1", 0));
        ui.actionFlightView->setShortcut(QApplication::translate("MainWindow", "Meta+2", 0));
    
        ui.actionHardwareConfig->setShortcut(QApplication::translate("MainWindow", "Meta+3", 0));
    
        ui.actionEngineersView->setShortcut(QApplication::translate("MainWindow", "Meta+4", 0));
        ui.actionGoogleEarthView->setShortcut(QApplication::translate("MainWindow", "Meta+5", 0));
        ui.actionLocal3DView->setShortcut(QApplication::translate("MainWindow", "Meta+6", 0));
        ui.actionTerminalView->setShortcut(QApplication::translate("MainWindow", "Meta+7", 0));
        ui.actionSimulationView->setShortcut(QApplication::translate("MainWindow", "Meta+8", 0));
        ui.actionFirmwareUpdateView->setShortcut(QApplication::translate("MainWindow", "Meta+9", 0));
    
        ui.actionFullscreen->setShortcut(QApplication::translate("MainWindow", "Meta+Return", 0));
    
        ui.actionMissionView->setShortcut(QApplication::translate("MainWindow", "Ctrl+1", 0));
        ui.actionFlightView->setShortcut(QApplication::translate("MainWindow", "Ctrl+2", 0));
    
        ui.actionHardwareConfig->setShortcut(QApplication::translate("MainWindow", "Ctrl+3", 0));
    
        ui.actionEngineersView->setShortcut(QApplication::translate("MainWindow", "Ctrl+4", 0));
        ui.actionGoogleEarthView->setShortcut(QApplication::translate("MainWindow", "Ctrl+5", 0));
        ui.actionLocal3DView->setShortcut(QApplication::translate("MainWindow", "Ctrl+6", 0));
        ui.actionTerminalView->setShortcut(QApplication::translate("MainWindow", "Ctrl+7", 0));
        ui.actionSimulationView->setShortcut(QApplication::translate("MainWindow", "Ctrl+8", 0));
        ui.actionFirmwareUpdateView->setShortcut(QApplication::translate("MainWindow", "Ctrl+9", 0));
    
        ui.actionFullscreen->setShortcut(QApplication::translate("MainWindow", "Ctrl+Return", 0));
    
        connect(&windowNameUpdateTimer, SIGNAL(timeout()), this, SLOT(configureWindowName()));
        windowNameUpdateTimer.start(15000);
    
        emit initStatusChanged(tr("Done"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
        show();
    }
    
    MainWindow::~MainWindow()
    {
        if (mavlink)
        {
            delete mavlink;
            mavlink = NULL;
        }
    
        if (simulationLink)
        {
            delete simulationLink;
            simulationLink = NULL;
        }
    
            joystick->shutdown();
            joystick->wait(5000);
    
            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
    
        for (int i=0;i<commsWidgetList.size();i++)
        {
            commsWidgetList[i]->deleteLater();
        }
    
    }
    
    void MainWindow::resizeEvent(QResizeEvent * event)
    {
        QMainWindow::resizeEvent(event);
    }
    
    QString MainWindow::getWindowStateKey()
    {
    
            return QString::number(currentView)+"_windowstate_" + QString::number(getCustomMode()) + "_" + UASManager::instance()->getActiveUAS()->getAutopilotTypeName();
    
            return QString::number(currentView)+"_windowstate_" + QString::number(getCustomMode());
    
    }
    
    QString MainWindow::getWindowGeometryKey()
    {
        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)
            {
    
                QSettings settings;
                settings.beginGroup("QGC_MAINWINDOW");
    
    
                // Load dock widget location (default is bottom)
    
                Qt::DockWidgetArea location = tool->getDockWidgetArea(currentView);
    
                int view = settings.value(QString("TOOL_PARENT_") + tool->objectName(),-1).toInt();
                settings.endGroup();
    
    
                QDockWidget* dock;
    
    
                    dock = createDockWidget(engineeringView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
    
                    dock = createDockWidget(pilotView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
    
                    dock = createDockWidget(simView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
    
                    dock = createDockWidget(plannerView,tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
                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;
    
                    dock = createDockWidget(centerStack->currentWidget(),tool,tool->getTitle(),tool->objectName(),(VIEW_SECTIONS)view,location);
    
                // XXX temporary "fix"
                dock->hide();
    
            }
        }
    }
    
    void MainWindow::buildCommonWidgets()
    {
        // Add generic MAVLink decoder
        mavlinkDecoder = new MAVLinkDecoder(mavlink, this);
    
    John Tapsell's avatar
    John Tapsell committed
        connect(mavlinkDecoder, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)),
                          this, SIGNAL(valueChanged(int,QString,QString,QVariant,quint64)));
    
        // Log player
        logPlayer = new QGCMAVLinkLogPlayer(mavlink, customStatusBar);
        customStatusBar->setLogPlayer(logPlayer);
    
    
        // Center widgets
        if (!plannerView)
        {
            plannerView = new SubMainWindow(this);
    
            plannerView->setObjectName("VIEW_MISSION");
    
            plannerView->setCentralWidget(new QGCMapTool(this));
    
            addToCentralStackedWidget(plannerView, VIEW_MISSION, "Maps");
    
        //pilotView (aka Flight or Mission View)
    
        if (!pilotView)
        {
            pilotView = new SubMainWindow(this);
    
            pilotView->setObjectName("VIEW_FLIGHT");
    
            pilotView->setCentralWidget(new PrimaryFlightDisplay(this));
    
            addToCentralStackedWidget(pilotView, VIEW_FLIGHT, "Pilot");
    
        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"));
        }
    
    
        if (getCustomMode() == CUSTOM_MODE_APM) {
    
            if (!configView)
            {
                configView = new SubMainWindow(this);
                configView->setObjectName("VIEW_HARDWARE_CONFIG");
                configView->setCentralWidget(new ApmHardwareConfig(this));
    
    Lorenz Meier's avatar
    Lorenz Meier committed
                addToCentralStackedWidget(configView, VIEW_HARDWARE_CONFIG, "Hardware");
    
            }
            if (!softwareConfigView)
            {
                softwareConfigView = new SubMainWindow(this);
                softwareConfigView->setObjectName("VIEW_SOFTWARE_CONFIG");
                softwareConfigView->setCentralWidget(new ApmSoftwareConfig(this));
    
    Lorenz Meier's avatar
    Lorenz Meier committed
                addToCentralStackedWidget(softwareConfigView, VIEW_SOFTWARE_CONFIG, "Software");
            }
    
        } else {
            if (!configView)
            {
                configView = new SubMainWindow(this);
                configView->setObjectName("VIEW_HARDWARE_CONFIG");
                configView->setCentralWidget(new QGCConfigView(this));
    
    Lorenz Meier's avatar
    Lorenz Meier committed
                addToCentralStackedWidget(configView, VIEW_HARDWARE_CONFIG, "Config");
    
        if (!engineeringView)
        {
            engineeringView = new SubMainWindow(this);
            engineeringView->setObjectName("VIEW_ENGINEER");
    
            engineeringView->setCentralWidget(new QGCDataPlot2D(this));
    
            addToCentralStackedWidget(engineeringView, VIEW_ENGINEER, tr("Logfile Plot"));
    
        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
    
        if (!local3DView)
    
        {
            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
    
    
        if (!simView)
        {
            simView = new SubMainWindow(this);
            simView->setObjectName("VIEW_SIMULATOR");
            simView->setCentralWidget(new QGCMapTool(this));
    
            addToCentralStackedWidget(simView, VIEW_SIMULATION, tr("Simulation View"));
    
        createDockWidget(simView,new UASControlWidget(this),tr("Control"),"UNMANNED_SYSTEM_CONTROL_DOCKWIDGET",VIEW_SIMULATION,Qt::LeftDockWidgetArea);
    
        createDockWidget(plannerView,new UASListWidget(this),tr("Unmanned Systems"),"UNMANNED_SYSTEM_LIST_DOCKWIDGET",VIEW_MISSION,Qt::LeftDockWidgetArea);
    
        createDockWidget(plannerView,new QGCWaypointListMulti(this),tr("Mission Plan"),"WAYPOINT_LIST_DOCKWIDGET",VIEW_MISSION,Qt::BottomDockWidgetArea);
    
        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);
    
        createDockWidget(engineeringView,new ParameterInterface(this),tr("Onboard Parameters"),"PARAMETER_INTERFACE_DOCKWIDGET",VIEW_ENGINEER,Qt::RightDockWidgetArea);
    
        createDockWidget(engineeringView,new QGCUASFileViewMulti(this),tr("Onboard Files"),"FILE_VIEW_DOCKWIDGET",VIEW_ENGINEER,Qt::RightDockWidgetArea);
    
        createDockWidget(simView,new ParameterInterface(this),tr("Onboard Parameters"),"PARAMETER_INTERFACE_DOCKWIDGET",VIEW_SIMULATION,Qt::RightDockWidgetArea);
    
        menuActionHelper->createToolAction(tr("Map View"), "MAP_VIEW_DOCKWIDGET");
    
        menuActionHelper->createToolAction(tr("Status Details"), "UAS_STATUS_DETAILS_DOCKWIDGET");
    
        createDockWidget(pilotView, new DebugConsole(this), tr("Communications Console"), "COMMUNICATION_CONSOLE_DOCKWIDGET", VIEW_FLIGHT, Qt::LeftDockWidgetArea);
    
        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"));
    
    treymarc's avatar
    treymarc committed
        createDockWidget(engineeringView,new HUD(320,240,this),tr("Video Downlink"),"HEAD_UP_DISPLAY_DOCKWIDGET",VIEW_FLIGHT,Qt::RightDockWidgetArea);
    
        createDockWidget(simView,new PrimaryFlightDisplay(this),tr("Primary Flight Display"),"PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET",VIEW_SIMULATION,Qt::RightDockWidgetArea);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        createDockWidget(plannerView,new PrimaryFlightDisplay(this),tr("Primary Flight Display"),"PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET",VIEW_FLIGHT,Qt::LeftDockWidgetArea);
    
        QGCTabbedInfoView *infoview = new QGCTabbedInfoView(this);
    
        infoview->addSource(mavlinkDecoder);
    
        createDockWidget(pilotView,infoview,tr("Info View"),"UAS_INFO_INFOVIEW_DOCKWIDGET",VIEW_FLIGHT,Qt::LeftDockWidgetArea);
    
    
        // Custom widgets, added last to all menus and layouts
        buildCustomWidget();
    }
    
    
    void MainWindow::addTool(SubMainWindow *parent,VIEW_SECTIONS view,QDockWidget* widget, const QString& title, Qt::DockWidgetArea area)
    
        menuActionHelper->createToolActionForCustomDockWidget(title, widget->objectName(), widget, view);
    
    QDockWidget* MainWindow::createDockWidget(QWidget *subMainWindowParent,QWidget *child,const QString& title,const QString& objectName,VIEW_SECTIONS view,Qt::DockWidgetArea area,const QSize& minSize)
    
        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
    
        if (minSize.height() >= 0)
            dockWidget->setMinimumHeight(minSize.height());
        if (minSize.width() >= 0)
            dockWidget->setMinimumWidth(minSize.width());
    
        addTool(parent,view,dockWidget,title,area);
        return dockWidget;
    
    void MainWindow::showDockWidget(const QString& name, bool show)
    {
        QDockWidget *dockWidget = menuActionHelper->getDockWidget(currentView, name);
        if(dockWidget)
            dockWidget->setVisible(show);
        else if (show)
            loadDockWidget(name);
    
    void MainWindow::fullScreenActionItemCallback()
    {
        ui.actionNormal->setChecked(false);
    }
    
    void MainWindow::normalActionItemCallback()
    {
        ui.actionFullscreen->setChecked(false);
    }
    
    
    void MainWindow::loadDockWidget(const QString& name)
    
        if(menuActionHelper->containsDockWidget(currentView, name))
    
    Lorenz Meier's avatar
    Lorenz Meier committed
    
    
        if (name.startsWith("HIL_CONFIG"))
        {
            //It's a HIL widget.
            showHILConfigurationWidget(UASManager::instance()->getActiveUAS());
        }
        else if (name == "UNMANNED_SYSTEM_CONTROL_DOCKWIDGET")
    
        {
            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);
        }
    
    Don Gagne's avatar
    Don Gagne committed
        else if (name == "FILE_VIEW_DOCKWIDGET")
        {
    
            createDockWidget(centerStack->currentWidget(),new QGCUASFileViewMulti(this),tr("Onboard Files"),"FILE_VIEW_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    
    Don Gagne's avatar
    Don Gagne committed
        }
    
        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 == "MAP_VIEW_DOCKWIDGET")
        {
            createDockWidget(centerStack->currentWidget(),new QGCMapTool(this),tr("Map view"),"MAP_VIEW_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
        }
    
        else if (name == "COMMUNICATION_DEBUG_CONSOLE_DOCKWIDGET")
        {
    
            //This is now a permanently detached window.
    
        }
        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")
        {
    
            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");
    
            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")
        {
    
            QStringList acceptList;
            acceptList.append("0,RAW_PRESSURE.pres_abs,hPa,65500");
            HDDisplay *hddisplay = new HDDisplay(acceptList,"Actuator Status",this);
    
            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";
        }
    
        else if (name == "PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET")
    
            createDockWidget(centerStack->currentWidget(),new PrimaryFlightDisplay(this),tr("Primary Flight Display"),"PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    
    Lorenz Meier's avatar
    Lorenz Meier committed
        }
        else if (name == "HEAD_UP_DISPLAY_DOCKWIDGET")
        {
    
    Lorenz Meier's avatar
    Lorenz Meier committed
            createDockWidget(centerStack->currentWidget(),new HUD(320,240,this),tr("Video Downlink"),"HEAD_UP_DISPLAY_DOCKWIDGET",currentView,Qt::RightDockWidgetArea);
    
        else if (name == "UAS_INFO_QUICKVIEW_DOCKWIDGET")
        {
            createDockWidget(centerStack->currentWidget(),new UASQuickView(this),tr("Quick View"),"UAS_INFO_QUICKVIEW_DOCKWIDGET",currentView,Qt::LeftDockWidgetArea);
        }
    
            if (customWidgetNameToFilenameMap.contains(name))
            {
                loadCustomWidget(customWidgetNameToFilenameMap[name],currentView);
            }
            else
            {
                qDebug() << "Error loading window:" << name;
            }
    
    void MainWindow::addToCentralStackedWidget(QWidget* widget, VIEW_SECTIONS viewSection, const QString& title)
    
        Q_UNUSED(title);
    
        Q_ASSERT(widget->objectName().length() != 0);
    
        // 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 = act->data().value<QWidget *>();
    
        centerStack->setCurrentWidget(widget);
    }
    
    void MainWindow::showHILConfigurationWidget(UASInterface* uas)
    {
        // Add simulation configuration widget
        UAS* mav = dynamic_cast<UAS*>(uas);
    
    
        if (mav && !hilDocks.contains(mav->getUASID()))
    
        {
            QGCHilConfiguration* hconf = new QGCHilConfiguration(mav, this);
    
            QString hilDockName = tr("HIL Config %1").arg(uas->getUASName());
    
            QString hilDockObjectName = QString("HIL_CONFIG_%1").arg(uas->getUASName().toUpper().replace(' ','_'));
            QDockWidget* hilDock = createDockWidget(simView, hconf,hilDockName, hilDockObjectName,VIEW_SIMULATION,Qt::LeftDockWidgetArea);
    
            hilDocks.insert(mav->getUASID(), hilDock);
    
        }
    }
    
    void MainWindow::closeEvent(QCloseEvent *event)
    {
        if (isVisible()) storeViewState();
        aboutToCloseFlag = true;
    
        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()
    {
    
        if (QGCToolWidget::instances()->isEmpty())
    
        {
            // This is the first widget
            ui.menuTools->addSeparator();
        }
    
        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 );
    
    
        QGCToolWidget* tool = new QGCToolWidget(objectName, title);
        createDockWidget(centerStack->currentWidget(),tool,title,objectName,currentView,Qt::BottomDockWidgetArea);
    
    
        QSettings settings;
        settings.beginGroup("QGC_MAINWINDOW");
        settings.setValue(QString("TOOL_PARENT_") + tool->objectName(),currentView);
        settings.endGroup();
    
    }
    
    void MainWindow::loadCustomWidget()
    {
        QString widgetFileExtension(".qgw");
    
        QString fileName = QFileDialog::getOpenFileName(this, tr("Specify Widget File Name"), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), tr("QGroundControl Widget (*%1);;").arg(widgetFileExtension));
    
        if (fileName != "") loadCustomWidget(fileName);
    }
    
    void MainWindow::loadCustomWidget(const QString& fileName, int view)
    {
    
        QGCToolWidget* tool = new QGCToolWidget("", "", this);
    
        if (tool->loadSettings(fileName, true))
        {
            qDebug() << "Loading custom tool:" << tool->getTitle() << tool->objectName();
            switch ((VIEW_SECTIONS)view)
            {
            case VIEW_ENGINEER:
    
                createDockWidget(engineeringView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
    
                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:
    
                //Delete tool, create menu item to tie it to.
                customWidgetNameToFilenameMap[tool->objectName()+"DOCK"] = fileName;
    
                menuActionHelper->createToolAction(tool->getTitle(), tool->objectName()+"DOCK");
    
    
    void MainWindow::loadCustomWidget(const QString& fileName, bool singleinstance)
    {
    
        QGCToolWidget* tool = new QGCToolWidget("", "", this);
    
        if (tool->loadSettings(fileName, true) || !singleinstance)
        {
    
            qDebug() << "Loading custom tool:" << tool->getTitle() << tool->objectName();
            QSettings settings;
            settings.beginGroup("QGC_MAINWINDOW");
    
            int view = settings.value(QString("TOOL_PARENT_") + tool->objectName(),-1).toInt();
            switch (view)
            {
            case VIEW_ENGINEER:
    
                createDockWidget(engineeringView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
    
                createDockWidget(pilotView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
    
                createDockWidget(simView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
    
                createDockWidget(plannerView,tool,tool->getTitle(),tool->objectName()+"DOCK",(VIEW_SECTIONS)view,Qt::LeftDockWidgetArea);
    
                //Delete tool, create menu item to tie it to.
                customWidgetNameToFilenameMap[tool->objectName()+"DOCK"] = fileName;
    
                QAction *action = menuActionHelper->createToolAction(tool->getTitle(), tool->objectName()+"DOCK");
                ui.menuTools->addAction(action);
    
        }
        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);
        files.append(platformWidgets.entryList());
    
        if (files.count() == 0)
        {
            qDebug() << "No default custom widgets for system " << systemType << "autopilot" << autopilotType << " found";
            qDebug() << "Tried with path: " << defaultsDir;
            showStatusMessage(tr("Did not find any custom widgets in %1").arg(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
                loadCustomWidget(defaultsDir+"/"+file, true);
                showStatusMessage(tr("Loaded custom widget %1").arg(defaultsDir+"/"+file));