Skip to content
Snippets Groups Projects
MainWindow.cc 63.5 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 <QDebug>
    #include <QTimer>
    #include <QHostInfo>
    #include <QSplashScreen>
    #include <QGCHilLink.h>
    #include <QGCHilConfiguration.h>
    #include <QGCHilFlightGearConfiguration.h>
    
    #include <QQuickView>
    
    #include <QDesktopWidget>
    
    
    #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"
    
    Don Gagne's avatar
    Don Gagne committed
    #include "SettingsDialog.h"
    
    #include "QGCMapTool.h"
    #include "MAVLinkDecoder.h"
    #include "QGCMAVLinkMessageSender.h"
    #include "QGCRGBDView.h"
    
    #include "QGCStatusBar.h"
    
    #include "QGCDataPlot2D.h"
    #include "Linecharts.h"
    
    #include "QGCTabbedInfoView.h"
    #include "UASRawStatusView.h"
    
    #include "SetupView.h"
    
    #include "SerialSettingsDialog.h"
    #include "terminalconsole.h"
    
    #include "QGCUASFileViewMulti.h"
    
    Don Gagne's avatar
    Don Gagne committed
    #include "QGCApplication.h"
    
    #include "QGCFileDialog.h"
    
    Don Gagne's avatar
    Don Gagne committed
    #include "QGCMessageBox.h"
    
    
    #ifdef QGC_OSG_ENABLED
    #include "Q3DWidgetFactory.h"
    #endif
    
    #include "LogCompressor.h"
    
    
    Don Gagne's avatar
    Don Gagne committed
    static MainWindow* _instance = NULL;   ///< @brief MainWindow singleton
    
    
    // Set up some constants
    const QString MainWindow::defaultDarkStyle = ":files/styles/style-dark.css";
    const QString MainWindow::defaultLightStyle = ":files/styles/style-light.css";
    
    
    Don Gagne's avatar
    Don Gagne committed
    MainWindow* MainWindow::_create(QSplashScreen* splashScreen, enum MainWindow::CUSTOM_MODE mode)
    
    Don Gagne's avatar
    Don Gagne committed
        Q_ASSERT(_instance == NULL);
        
        new MainWindow(splashScreen, mode);
        
        // _instance is set in constructor
        Q_ASSERT(_instance);
    
    
    Don Gagne's avatar
    Don Gagne committed
    MainWindow* MainWindow::instance(void)
    
    Don Gagne's avatar
    Don Gagne committed
        return _instance;
    
    void MainWindow::deleteInstance(void)
    {
    
    Don Gagne's avatar
    Don Gagne committed
        delete this;
    
    Don Gagne's avatar
    Don Gagne committed
    /// @brief Private constructor for MainWindow. MainWindow singleton is only ever created
    ///         by MainWindow::_create method. Hence no other code should have access to
    ///         constructor.
    MainWindow::MainWindow(QSplashScreen* splashScreen, enum MainWindow::CUSTOM_MODE mode) :
    
        currentView(VIEW_FLIGHT),
    
        currentStyle(QGC_MAINWINDOW_STYLE_DARK),
    
    Don Gagne's avatar
    Don Gagne committed
        mavlink(new MAVLinkProtocol()),
    
        centerStackActionGroup(new QActionGroup(this)),
        autoReconnect(false),
    
        simulationLink(NULL),
    
    Don Gagne's avatar
    Don Gagne committed
        customMode(mode),
        menuActionHelper(new MenuActionHelper()),
        _splashScreen(splashScreen)
    
    Don Gagne's avatar
    Don Gagne committed
        Q_ASSERT(_instance == NULL);
        _instance = this;
        
    
        if (splashScreen) {
            connect(this, &MainWindow::initStatusChanged, splashScreen, &QSplashScreen::showMessage);
        }
    
        this->setAttribute(Qt::WA_DeleteOnClose);
    
        connect(menuActionHelper, SIGNAL(needToShowDockWidget(QString,bool)),SLOT(showDockWidget(QString,bool)));
    
    Don Gagne's avatar
    Don Gagne committed
        connect(mavlink, SIGNAL(protocolStatusMessage(const QString&, const QString&)), this, SLOT(showCriticalMessage(const QString&, const QString&)));
    
        connect(mavlink, SIGNAL(saveTempFlightDataLog(QString)), this, SLOT(_saveTempFlightDataLog(QString)));
    
        emit initStatusChanged(tr("Loading style"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
        loadStyle(currentStyle);
    
            menuActionHelper->setAdvancedMode(settings.value("ADVANCED_MODE").toBool());
    
        // Select the proper view. Default to the flight view or load the last one used if it's supported.
    
        if (!settings.contains("CURRENT_VIEW"))
        {
            settings.setValue("CURRENT_VIEW", currentView);
        }
        else
        {
            VIEW_SECTIONS currentViewCandidate = (VIEW_SECTIONS) settings.value("CURRENT_VIEW", currentView).toInt();
    
            switch (currentViewCandidate) {
                case VIEW_ENGINEER:
                case VIEW_MISSION:
                case VIEW_FLIGHT:
                case VIEW_SIMULATION:
                case VIEW_SETUP:
                case VIEW_TERMINAL:
    
    // And only re-load views if they're supported with the current QGC build
    #ifdef QGC_OSG_ENABLED
                case VIEW_LOCAL3D:
    #endif
    #ifdef QGC_GOOGLE_EARTH_ENABLED
                case VIEW_GOOGLEEARTH:
    #endif
    
                    currentView = currentViewCandidate;
                default:
                    // If an invalid view candidate was found in the settings file, just use the default view and re-save.
                    settings.setValue("CURRENT_VIEW", currentView);
                    break;
    
        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);
    
    
    Don Gagne's avatar
    Don Gagne committed
        toolBar = new QGCToolBar(this);
        this->addToolBar(toolBar);
    
    Bryant's avatar
    Bryant committed
        // Add the perspectives to the toolbar
    
    Don Gagne's avatar
    Don Gagne committed
        QList<QAction*> actions;
    
        actions << ui.actionSetup;
    
    Don Gagne's avatar
    Don Gagne committed
        actions << ui.actionMissionView;
        actions << ui.actionFlightView;
    
        actions << ui.actionEngineersView;
    
    Don Gagne's avatar
    Don Gagne committed
        toolBar->setPerspectiveChangeActions(actions);
    
    Don Gagne's avatar
    Don Gagne committed
        // Add actions for advanced users (displayed in dropdown under "advanced")
        QList<QAction*> advancedActions;
        advancedActions << ui.actionGoogleEarthView;
        advancedActions << ui.actionLocal3DView;
        advancedActions << ui.actionTerminalView;
        advancedActions << ui.actionSimulationView;
        toolBar->setPerspectiveChangeAdvancedActions(advancedActions);
    
        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*)));
    
        // Connect user interface devices
    
        emit initStatusChanged(tr("Initializing joystick interface"), Qt::AlignLeft | Qt::AlignBottom, QColor(62, 93, 141));
    
        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)
        {
    
    Don Gagne's avatar
    Don Gagne committed
            LinkManager* linkMgr = LinkManager::instance();
            Q_ASSERT(linkMgr);
            
    
            SerialLink* link = new SerialLink();
    
    Don Gagne's avatar
    Don Gagne committed
            linkMgr->add(link);
            linkMgr->addProtocol(link, mavlink);
            linkMgr->connectLink(link);
    
        }
    
        // 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.actionSetup->setShortcut(QApplication::translate("MainWindow", "Meta+1", 0));
        ui.actionMissionView->setShortcut(QApplication::translate("MainWindow", "Meta+2", 0));
        ui.actionFlightView->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.actionSetup->setShortcut(QApplication::translate("MainWindow", "Ctrl+1", 0));
        ui.actionMissionView->setShortcut(QApplication::translate("MainWindow", "Ctrl+2", 0));
        ui.actionFlightView->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();
        }
    
    Don Gagne's avatar
    Don Gagne committed
        
        _instance = NULL;
    
    }
    
    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);
    
                default: // Flight view is the default.
    
                    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;
    
                // 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);
    
    
        // Initialize all of the views, if they haven't been already, and add their central widgets
    
        if (!plannerView)
        {
            plannerView = new SubMainWindow(this);
    
            plannerView->setObjectName("VIEW_MISSION");
    
            plannerView->setCentralWidget(new QGCMapTool(this));
    
            addToCentralStackedWidget(plannerView, VIEW_MISSION, "Maps");
    
        }
        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"));
        }
    
            setupView = new SubMainWindow(this);
            setupView->setObjectName("VIEW_SETUP");
            setupView->setCentralWidget(new SetupView(this));
            addToCentralStackedWidget(setupView, VIEW_SETUP, "Setup");
    
        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"));
    
        // Add dock widgets for the planner view
        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);
    
        // Add dock widgets for the pilot view
        createDockWidget(pilotView, new DebugConsole(this), tr("Communications Console"), "COMMUNICATION_CONSOLE_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);
    
        // Add dock widgets for the simulation view
        createDockWidget(simView,new UASControlWidget(this),tr("Control"),"UNMANNED_SYSTEM_CONTROL_DOCKWIDGET",VIEW_SIMULATION,Qt::LeftDockWidgetArea);
    
        createDockWidget(simView,new QGCWaypointListMulti(this),tr("Mission Plan"),"WAYPOINT_LIST_DOCKWIDGET",VIEW_SIMULATION,Qt::BottomDockWidgetArea);
    
        createDockWidget(simView, new ParameterInterface(this), tr("Onboard Parameters"), "PARAMETER_INTERFACE_DOCKWIDGET", VIEW_SIMULATION, Qt::RightDockWidgetArea);
        createDockWidget(simView, new PrimaryFlightDisplay(this), tr("Primary Flight Display"), "PRIMARY_FLIGHT_DISPLAY_DOCKWIDGET", VIEW_SIMULATION, Qt::RightDockWidgetArea);
    
        // Add dock widgets for the engineering view
        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(engineeringView, new HUD(320, 240, this), tr("Video Downlink"), "HEAD_UP_DISPLAY_DOCKWIDGET", VIEW_ENGINEER, Qt::RightDockWidgetArea);
    
        // Add some extra widgets to the Tool Widgets menu
    
        menuActionHelper->createToolAction(tr("Map View"), "MAP_VIEW_DOCKWIDGET");
    
        menuActionHelper->createToolAction(tr("Status Details"), "UAS_STATUS_DETAILS_DOCKWIDGET");
        menuActionHelper->createToolAction(tr("Flight Display"), "HEAD_DOWN_DISPLAY_1_DOCKWIDGET");
        menuActionHelper->createToolAction(tr("Actuator Status"), "HEAD_DOWN_DISPLAY_2_DOCKWIDGET");
    
        // Add any custom widgets last to all menus and layouts
    
    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 == "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)
    {
    
        storeViewState();
    
        mavlink->storeSettings();
        UASManager::instance()->storeSettings();
    
        event->accept();
    
    }
    
    /**
     * 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 = QGCFileDialog::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);
    
            default: // Flight view is the default.
    
            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;
            }
        }
        else
        {
            return;
        }
    }
    
    
    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);
    
            default: // Flight view is the default.
    
                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);
    
        }
        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));
            }
        }
    }
    
    void MainWindow::loadSettings()
    {
        QSettings settings;
    
        customMode = static_cast<enum MainWindow::CUSTOM_MODE>(settings.value("QGC_CUSTOM_MODE", (unsigned int)MainWindow::CUSTOM_MODE_NONE).toInt());
    
        settings.beginGroup("QGC_MAINWINDOW");
        autoReconnect = settings.value("AUTO_RECONNECT", autoReconnect).toBool();
        currentStyle = (QGC_MAINWINDOW_STYLE)settings.value("CURRENT_STYLE", currentStyle).toInt();
        lowPowerMode = settings.value("LOW_POWER_MODE", lowPowerMode).toBool();
    
        bool dockWidgetTitleBarEnabled = settings.value("DOCK_WIDGET_TITLEBARS",menuActionHelper->dockWidgetTitleBarsEnabled()).toBool();
    
        enableDockWidgetTitleBars(dockWidgetTitleBarEnabled);
    
    }
    
    void MainWindow::storeSettings()
    {
        QSettings settings;
    
    
        settings.setValue("QGC_CUSTOM_MODE", (int)customMode);
    
    
        settings.beginGroup("QGC_MAINWINDOW");
        settings.setValue("AUTO_RECONNECT", autoReconnect);
        settings.setValue("CURRENT_STYLE", currentStyle);
        settings.setValue("LOW_POWER_MODE", lowPowerMode);
    
        settings.endGroup();
    
        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());
    
        // Save the current UAS view if a UAS is connected
        if (UASManager::instance()->getUASList().length() > 0) settings.setValue("CURRENT_VIEW_WITH_UAS_CONNECTED", currentView);
    
        // And save any custom weidgets
    
        QGCToolWidget::storeWidgetsToSettings(settings);
    
    }
    
    void MainWindow::configureWindowName()
    {
        QList<QHostAddress> hostAddresses = QNetworkInterface::allAddresses();
        QString windowname = qApp->applicationName() + " " + qApp->applicationVersion();
        bool prevAddr = false;
    
        windowname.append(" (" + QHostInfo::localHostName() + ": ");
    
        for (int i = 0; i < hostAddresses.size(); i++)
        {
            // Exclude loopback IPv4 and all IPv6 addresses
            if (hostAddresses.at(i) != QHostAddress("127.0.0.1") && !hostAddresses.at(i).toString().contains(":"))
            {
                if(prevAddr) windowname.append("/");
                windowname.append(hostAddresses.at(i).toString());
                prevAddr = true;
            }
        }
    
        windowname.append(")");
    
        setWindowTitle(windowname);
    }
    
    void MainWindow::startVideoCapture()
    {
        QString format = "bmp";
        QString initialPath = QDir::currentPath() + tr("/untitled.") + format;