/*===================================================================== QGroundControl Open Source Ground Control Station (c) 2009, 2015 QGROUNDCONTROL PROJECT 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 . ======================================================================*/ import QtQuick 2.5 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 import QtPositioning 5.2 import QGroundControl 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 import QGroundControl.FlightDisplay 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.MultiVehicleManager 1.0 /// Inner common QML for mainWindow Item { id: mainWindow signal reallyClose readonly property string _planViewSource: "MissionEditor.qml" readonly property string _setupViewSource: "SetupView.qml" QGCPalette { id: __qgcPal; colorGroupEnabled: true } property real tbHeight: ScreenTools.isMobile ? (ScreenTools.isTinyScreen ? (mainWindow.width * 0.0666) : (mainWindow.width * 0.05)) : ScreenTools.defaultFontPixelSize * 4 property int tbCellHeight: tbHeight * 0.75 property real tbSpacing: ScreenTools.isMobile ? width * 0.00824 : 9.54 property real tbButtonWidth: tbCellHeight * 1.35 property real availableHeight: height - tbHeight property real menuButtonWidth: (tbButtonWidth * 2) + (tbSpacing * 4) + 1 property var gcsPosition: QtPositioning.coordinate() // Starts as invalid coordinate property var currentPopUp: null property real currentCenterX: 0 property var activeVehicle: multiVehicleManager.activeVehicle property string formatedMessage: activeVehicle ? activeVehicle.formatedMessage : "" function showFlyView() { if(currentPopUp) { currentPopUp.close() } flightView.visible = true setupViewLoader.visible = false planViewLoader.visible = false toolBar.checkFlyButton() } function showPlanView() { if(currentPopUp) { currentPopUp.close() } if (planViewLoader.source != _planViewSource) { planViewLoader.source = _planViewSource } flightView.visible = false setupViewLoader.visible = false planViewLoader.visible = true toolBar.checkPlanButton() } function showSetupView() { if(currentPopUp) { currentPopUp.close() } if (setupViewLoader.source != _setupViewSource) { setupViewLoader.source = _setupViewSource } flightView.visible = false setupViewLoader.visible = true planViewLoader.visible = false toolBar.checkSetupButton() } // The following are use for unit testing only function showSetupFirmware() { setupViewLoader.item.showFirmwarePanel() } function showSetupParameters() { setupViewLoader.item.showParametersPanel() } function showSetupSummary() { setupViewLoader.item.showSummaryPanel() } function showSetupVehicleComponent(vehicleComponent) { setupViewLoader.item.showVehicleComponentPanel(vehicleComponent) } /// Start the process of closing QGroundControl. Prompts the user are needed. function attemptWindowClose() { unsavedMissionCloseDialog.check() } function finishCloseProcess() { QGroundControl.linkManager.shutdown() // The above shutdown causes a flurry of activity as the vehicle components are removed. This in turn // causes the Windows Version of Qt to crash if you allow the close event to be accepted. In order to prevent // the crash, we ignore the close event and setup a delayed timer to close the window after things settle down. delayedWindowCloseTimer.start() } MessageDialog { id: unsavedMissionCloseDialog title: "QGroundControl close" text: "You have a mission edit in progress which has not been saved/sent. If you close you will lose changes. Are you sure you want to close?" standardButtons: StandardButton.Yes | StandardButton.No modality: Qt.ApplicationModal visible: false onYes: activeConnectionsCloseDialog.check() function check() { if (planViewLoader.item && planViewLoader.item.syncNeeded) { unsavedMissionCloseDialog.open() } else { activeConnectionsCloseDialog.check() } } } MessageDialog { id: activeConnectionsCloseDialog title: "QGroundControl close" text: "There are still active connections to vehicles. Do you want to disconnect these before closing?" standardButtons: StandardButton.Yes | StandardButton.Cancel modality: Qt.ApplicationModal visible: false onYes: finishCloseProcess() function check() { if (QGroundControl.multiVehicleManager.activeVehicle) { activeConnectionsCloseDialog.open() } else { finishCloseProcess() } } } Timer { id: delayedWindowCloseTimer interval: 1500 running: false repeat: false onTriggered: { mainWindow.reallyClose() } } //-- Detect tablet position PositionSource { id: positionSource updateInterval: 1000 active: true onPositionChanged: { if(positionSource.valid) { if(positionSource.position.coordinate.latitude) { if(Math.abs(positionSource.position.coordinate.latitude) > 0.001) { if(positionSource.position.coordinate.longitude) { if(Math.abs(positionSource.position.coordinate.longitude) > 0.001) { gcsPosition = positionSource.position.coordinate } } } } } } } property var messageQueue: [] function showMessage(message) { if(criticalMmessageArea.visible) { messageQueue.push(message) } else { criticalMessageText.text = message criticalMmessageArea.visible = true } } function showLeftMenu() { if(!leftPanel.visible && !leftPanel.item.animateShowDialog.running) { leftPanel.visible = true leftPanel.item.animateShowDialog.start() } else if(leftPanel.visible && !leftPanel.item.animateShowDialog.running) { //-- If open, toggle it closed hideLeftMenu() } } function hideLeftMenu() { if(leftPanel.visible && !leftPanel.item.animateHideDialog.running) { leftPanel.item.animateHideDialog.start() } } onFormatedMessageChanged: { if(messageArea.visible) { messageText.append(formatedMessage) //-- Hack to scroll down messageFlick.flick(0,-500) } } function showMessageArea() { if(currentPopUp) { currentPopUp.close() } if(multiVehicleManager.activeVehicleAvailable) { messageText.text = activeVehicle.formatedMessages //-- Hack to scroll to last message for (var i = 0; i < activeVehicle.messageCount; i++) messageFlick.flick(0,-5000) activeVehicle.resetMessages() } else { messageText.text = "No Messages" } currentPopUp = messageArea messageArea.visible = true } function showPopUp(dropItem, centerX) { if(currentPopUp) { currentPopUp.close() } indicatorDropdown.centerX = centerX indicatorDropdown.sourceComponent = dropItem indicatorDropdown.visible = true currentPopUp = indicatorDropdown } //-- Left Settings Menu Loader { id: leftPanel anchors.fill: parent visible: false z: QGroundControl.zOrderTopMost + 100 } //-- Main UI MainToolBar { id: toolBar height: tbHeight anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top mainWindow: mainWindow opaqueBackground: leftPanel.visible isBackgroundDark: flightView.isBackgroundDark z: QGroundControl.zOrderTopMost Component.onCompleted: { leftPanel.source = "MainWindowLeftPanel.qml" } onShowSetupView: mainWindow.showSetupView() onShowPlanView: mainWindow.showPlanView() onShowFlyView: mainWindow.showFlyView() } FlightDisplayView { id: flightView anchors.fill: parent availableHeight: mainWindow.availableHeight visible: true } Loader { id: planViewLoader anchors.fill: parent visible: false } Loader { id: setupViewLoader anchors.fill: parent visible: false } //------------------------------------------------------------------------- //-- Dismiss Pop Up Messages MouseArea { visible: currentPopUp != null enabled: currentPopUp != null anchors.fill: parent onClicked: { currentPopUp.close() } } //------------------------------------------------------------------------- //-- Indicator Drop Down Info Loader { id: indicatorDropdown visible: false property real centerX: 0 function close() { sourceComponent = null mainWindow.currentPopUp = null } } //------------------------------------------------------------------------- //-- System Message Area Rectangle { id: messageArea function close() { currentPopUp = null messageText.text = "" messageArea.visible = false } width: mainWindow.width * 0.5 height: mainWindow.height * 0.5 color: Qt.rgba(0,0,0,0.8) visible: false radius: ScreenTools.defaultFontPixelHeight * 0.5 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: tbHeight + ScreenTools.defaultFontPixelHeight QGCFlickable { id: messageFlick anchors.margins: ScreenTools.defaultFontPixelHeight anchors.fill: parent contentHeight: messageText.height contentWidth: messageText.width pixelAligned: true clip: true TextEdit { id: messageText readOnly: true textFormat: TextEdit.RichText color: "white" } } //-- Dismiss System Message Image { anchors.margins: ScreenTools.defaultFontPixelHeight anchors.top: parent.top anchors.right: parent.right width: ScreenTools.defaultFontPixelHeight * 1.5 height: ScreenTools.defaultFontPixelHeight * 1.5 source: "/res/XDelete.svg" fillMode: Image.PreserveAspectFit mipmap: true smooth: true MouseArea { anchors.fill: parent onClicked: { messageArea.close() } } } } //------------------------------------------------------------------------- //-- Critical Message Area Rectangle { id: criticalMmessageArea function close() { //-- Are there messages in the waiting queue? if(mainWindow.messageQueue.length) { criticalMessageText.text = "" //-- Show all messages in queue for (var i = 0; i < mainWindow.messageQueue.length; i++) { criticalMessageText.append(mainWindow.messageQueue[i]) } //-- Clear it mainWindow.messageQueue = [] } else { criticalMessageText.text = "" criticalMmessageArea.visible = false } } width: mainWindow.width * 0.55 height: ScreenTools.defaultFontPixelHeight * ScreenTools.fontHRatio * 6 color: Qt.rgba(0,0,0,0.8) visible: false radius: ScreenTools.defaultFontPixelHeight * 0.5 anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom anchors.bottomMargin: ScreenTools.defaultFontPixelHeight MouseArea { // This MouseArea prevents the Map below it from getting Mouse events. Without this // things like mousewheel will scroll the Flickable and then scroll the map as well. anchors.fill: parent preventStealing: true onWheel: wheel.accepted = true } Flickable { id: criticalMessageFlick anchors.margins: ScreenTools.defaultFontPixelHeight anchors.fill: parent contentHeight: criticalMessageText.height contentWidth: criticalMessageText.width boundsBehavior: Flickable.StopAtBounds pixelAligned: true clip: true TextEdit { id: criticalMessageText width: criticalMmessageArea.width - criticalClose.width - (ScreenTools.defaultFontPixelHeight * 2) anchors.left: parent.left readOnly: true textFormat: TextEdit.RichText font.weight: Font.DemiBold wrapMode: TextEdit.WordWrap color: "#fdfd3b" } } //-- Dismiss Critical Message Image { id: criticalClose anchors.margins: ScreenTools.defaultFontPixelHeight anchors.top: parent.top anchors.right: parent.right width: ScreenTools.defaultFontPixelHeight * 1.5 height: ScreenTools.defaultFontPixelHeight * 1.5 source: "/res/XDelete.svg" fillMode: Image.PreserveAspectFit mipmap: true smooth: true MouseArea { anchors.fill: parent onClicked: { criticalMmessageArea.close() } } } //-- More text below indicator Image { anchors.margins: ScreenTools.defaultFontPixelHeight anchors.bottom: parent.bottom anchors.right: parent.right width: ScreenTools.defaultFontPixelHeight * 1.5 height: ScreenTools.defaultFontPixelHeight * 1.5 source: "/res/ArrowDown.svg" fillMode: Image.PreserveAspectFit mipmap: true smooth: true visible: criticalMessageText.lineCount > 5 MouseArea { anchors.fill: parent onClicked: { criticalMessageFlick.flick(0,-500) } } } } }