Unverified Commit 543ba3f3 authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #9003 from DonLakeFlyer/PhotoVideoControl

Unified photo/video control which works with mavlink cameras and and video stream
parents 19310246 d6cd32a5
......@@ -36,7 +36,6 @@
<file alias="AnalyzeView.qml">src/AnalyzeView/AnalyzeView.qml</file>
<file alias="AppSettings.qml">src/ui/AppSettings.qml</file>
<file alias="BluetoothSettings.qml">src/ui/preferences/BluetoothSettings.qml</file>
<file alias="CameraPageWidget.qml">src/FlightMap/Widgets/CameraPageWidget.qml</file>
<file alias="CorridorScanEditor.qml">src/PlanView/CorridorScanEditor.qml</file>
<file alias="DebugWindow.qml">src/ui/preferences/DebugWindow.qml</file>
<file alias="ESP8266Component.qml">src/AutoPilotPlugins/Common/ESP8266Component.qml</file>
......@@ -117,7 +116,6 @@
<file alias="QGroundControl/Controls/ModeSwitchDisplay.qml">src/QmlControls/ModeSwitchDisplay.qml</file>
<file alias="QGroundControl/Controls/MultiRotorMotorDisplay.qml">src/QmlControls/MultiRotorMotorDisplay.qml</file>
<file alias="QGroundControl/Controls/OfflineMapButton.qml">src/QmlControls/OfflineMapButton.qml</file>
<file alias="QGroundControl/Controls/PageView.qml">src/QmlControls/PageView.qml</file>
<file alias="QGroundControl/Controls/ParameterEditor.qml">src/QmlControls/ParameterEditor.qml</file>
<file alias="QGroundControl/Controls/ParameterEditorDialog.qml">src/QmlControls/ParameterEditorDialog.qml</file>
<file alias="QGroundControl/Controls/PIDTuning.qml">src/QmlControls/PIDTuning.qml</file>
......@@ -222,7 +220,6 @@
<file alias="QGroundControl/FlightDisplay/GuidedAltitudeSlider.qml">src/FlightDisplay/GuidedAltitudeSlider.qml</file>
<file alias="QGroundControl/FlightDisplay/GuidedToolStripAction.qml">src/FlightDisplay/GuidedToolStripAction.qml</file>
<file alias="QGroundControl/FlightDisplay/MultiVehicleList.qml">src/FlightDisplay/MultiVehicleList.qml</file>
<file alias="QGroundControl/FlightDisplay/MultiVehiclePanel.qml">src/FlightDisplay/MultiVehiclePanel.qml</file>
<file alias="QGroundControl/FlightDisplay/PreFlightBatteryCheck.qml">src/FlightDisplay/PreFlightBatteryCheck.qml</file>
<file alias="QGroundControl/FlightDisplay/PreFlightGPSCheck.qml">src/FlightDisplay/PreFlightGPSCheck.qml</file>
<file alias="QGroundControl/FlightDisplay/PreFlightRCCheck.qml">src/FlightDisplay/PreFlightRCCheck.qml</file>
......@@ -246,6 +243,7 @@
<file alias="QGroundControl/FlightMap/MissionItemIndicatorDrag.qml">src/FlightMap/MapItems/MissionItemIndicatorDrag.qml</file>
<file alias="QGroundControl/FlightMap/MissionItemView.qml">src/FlightMap/MapItems/MissionItemView.qml</file>
<file alias="QGroundControl/FlightMap/MissionLineView.qml">src/FlightMap/MapItems/MissionLineView.qml</file>
<file alias="QGroundControl/FlightMap/PhotoVideoControl.qml">src/FlightMap/Widgets/PhotoVideoControl.qml</file>
<file alias="QGroundControl/FlightMap/PlanMapItems.qml">src/FlightMap/MapItems/PlanMapItems.qml</file>
<file alias="QGroundControl/FlightMap/PolygonEditor.qml">src/FlightMap/MapItems/PolygonEditor.qml</file>
<file alias="QGroundControl/FlightMap/QGCArtificialHorizon.qml">src/FlightMap/Widgets/QGCArtificialHorizon.qml</file>
......@@ -271,10 +269,8 @@
<file alias="TcpSettings.qml">src/ui/preferences/TcpSettings.qml</file>
<file alias="test.qml">src/test.qml</file>
<file alias="UdpSettings.qml">src/ui/preferences/UdpSettings.qml</file>
<file alias="ValuePageWidget.qml">src/FlightMap/Widgets/ValuePageWidget.qml</file>
<file alias="VehicleSummary.qml">src/VehicleSetup/VehicleSummary.qml</file>
<file alias="VibrationPage.qml">src/AnalyzeView/VibrationPage.qml</file>
<file alias="VideoPageWidget.qml">src/FlightMap/Widgets/VideoPageWidget.qml</file>
<file alias="VirtualJoystick.qml">src/FlightDisplay/VirtualJoystick.qml</file>
<file alias="VTOLLandingPatternEditor.qml">src/PlanView/VTOLLandingPatternEditor.qml</file>
</qresource>
......
......@@ -300,17 +300,6 @@ SetupPage {
}
}
} // QGCGroupBox - Hardware Settings
QGCGroupBox {
title: qsTr("Camera Test")
Layout.fillWidth: true
QGCButton {
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Trigger Camera")
onClicked: controller.vehicle.triggerCamera()
}
}
}
}
}
......
......@@ -1590,7 +1590,7 @@ QGCCameraControl::handleVideoInfo(const mavlink_video_stream_information_t* vi)
qCDebug(CameraControlLog) << "All stream handlers done";
_streamInfoTimer.stop();
emit autoStreamChanged();
emit _vehicle->dynamicCameras()->streamChanged();
emit _vehicle->cameraManager()->streamChanged();
}
}
......@@ -1636,7 +1636,7 @@ QGCCameraControl::setCurrentStream(int stream)
_requestStreamStatus(static_cast<uint8_t>(pInfo->streamID()));
}
emit currentStreamChanged();
emit _vehicle->dynamicCameras()->streamChanged();
emit _vehicle->cameraManager()->streamChanged();
}
}
}
......@@ -1786,7 +1786,7 @@ QGCCameraControl::_streamTimeout()
//-- If we have at least one stream, work with what we have.
if(_streams.count()) {
emit autoStreamChanged();
emit _vehicle->dynamicCameras()->streamChanged();
emit _vehicle->cameraManager()->streamChanged();
}
return;
}
......@@ -2140,7 +2140,7 @@ QGCCameraControl::wb()
Fact*
QGCCameraControl::mode()
{
return _paramComplete ? getFact(kCAM_MODE) : nullptr;
return _paramComplete && factExists(kCAM_MODE) ? getFact(kCAM_MODE) : nullptr;
}
//-----------------------------------------------------------------------------
......
......@@ -53,6 +53,29 @@ void FactGroup::_setupTimer()
}
}
bool FactGroup::factExists(const QString& name)
{
if (name.contains(".")) {
QStringList parts = name.split(".");
if (parts.count() != 2) {
qWarning() << "Only single level of hierarchy supported";
return false;
}
FactGroup * factGroup = getFactGroup(parts[0]);
if (!factGroup) {
qWarning() << "Unknown FactGroup" << parts[0];
return false;
}
return factGroup->factExists(parts[1]);
}
QString camelCaseName = _ignoreCamelCase ? name : _camelCase(name);
return _nameToFactMap.contains(camelCaseName);
}
Fact* FactGroup::getFact(const QString& name)
{
if (name.contains(".")) {
......
......@@ -32,10 +32,15 @@ public:
Q_PROPERTY(QStringList factNames READ factNames CONSTANT)
Q_PROPERTY(QStringList factGroupNames READ factGroupNames CONSTANT)
/// @ return true: if the fact exists in the group
Q_INVOKABLE bool factExists(const QString& name);
/// @return Fact for specified name, NULL if not found
/// Note: Requesting a fact which doesn't exists is considered an internal error and will spit out a qWarning
Q_INVOKABLE Fact* getFact(const QString& name);
/// @return FactGroup for specified name, NULL if not found
/// Note: Requesting a fact group which doesn't exists is considered an internal error and will spit out a qWarning
Q_INVOKABLE FactGroup* getFactGroup(const QString& name);
/// Turning on live updates will allow value changes to flow through as they are received.
......
......@@ -28,7 +28,7 @@ Item {
property double _ar: QGroundControl.videoManager.aspectRatio
property bool _showGrid: QGroundControl.settingsManager.videoSettings.gridLines.rawValue > 0
property var _dynamicCameras: activeVehicle ? activeVehicle.dynamicCameras : null
property var _dynamicCameras: activeVehicle ? activeVehicle.cameraManager : null
property bool _connected: activeVehicle ? !activeVehicle.connectionLost : false
property int _curCameraIndex: _dynamicCameras ? _dynamicCameras.currentCamera : 0
property bool _isCamera: _dynamicCameras ? _dynamicCameras.cameras.count > 0 : false
......
......@@ -26,15 +26,11 @@ Column {
id: _root
spacing: ScreenTools.defaultFontPixelHeight * 0.25
property var missionController
property real availableHeight
property var missionController
Loader {
width: parent.width
width: parent.width
source: QGroundControl.settingsManager.flyViewSettings.alternateInstrumentPanel.rawValue ?
"qrc:/qml/QGCInstrumentWidgetAlternate.qml" : "qrc:/qml/QGCInstrumentWidget.qml"
property real maxHeight: availableHeight - y
property bool showValues: !QGroundControl.airspaceManager.airspaceVisible
}
}
......@@ -23,12 +23,6 @@ Column {
property real availableHeight
MultiVehiclePanel {
id: multiVehiclePanel
width: parent.width
availableHeight: parent.height - y
}
AirspaceControl {
id: airspaceControl
width: parent.width
......@@ -40,7 +34,5 @@ Column {
id: flightDisplayViewWidgets
width: parent.width
missionController: _missionController
availableHeight: _root.availableHeight - y
visible: multiVehiclePanel.singleVehiclePanel
}
}
......@@ -71,20 +71,61 @@ Item {
rallyPointController: _rallyPointController
}
Row {
id: multiVehiclePanelSelector
anchors.margins: _toolsMargin
anchors.top: parent.top
anchors.right: parent.right
width: _rightPanelWidth
spacing: ScreenTools.defaultFontPixelWidth
visible: QGroundControl.multiVehicleManager.vehicles.count > 1 && QGroundControl.corePlugin.options.flyView.showMultiVehicleList
property bool showSingleVehiclePanel: !visible || singleVehicleRadio.checked
QGCMapPalette { id: mapPal; lightColors: true }
QGCRadioButton {
id: singleVehicleRadio
text: qsTr("Single")
checked: true
textColor: mapPal.text
}
QGCRadioButton {
text: qsTr("Multi-Vehicle")
textColor: mapPal.text
}
}
MultiVehicleList {
anchors.margins: _toolsMargin
anchors.top: multiVehiclePanelSelector.bottom
anchors.right: parent.right
width: _rightPanelWidth
height: parent.height - y - _toolsMargin
visible: !multiVehiclePanelSelector.showSingleVehiclePanel
}
FlyViewInstrumentPanel {
id: instrumentPanel
anchors.margins: _toolsMargin
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.top: multiVehiclePanelSelector.visible ? multiVehiclePanelSelector.bottom : parent.top
anchors.right: parent.right
width: _rightPanelWidth
spacing: _toolsMargin
visible: QGroundControl.corePlugin.options.flyView.showInstrumentPanel
visible: QGroundControl.corePlugin.options.flyView.showInstrumentPanel && multiVehiclePanelSelector.showSingleVehiclePanel
availableHeight: parent.height - y - _toolsMargin
property real rightInset: visible ? parent.width - x : 0
}
PhotoVideoControl {
anchors.margins: _toolsMargin
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
width: _rightPanelWidth
}
TelemetryValuesBar {
id: telemetryPanel
x: recalcXPosition()
......
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
import QtQuick 2.12
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.12
import QGroundControl 1.0
import QGroundControl.Controls 1.0
import QGroundControl.FlightDisplay 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Palette 1.0
/// Multi vehicle panel for Fly View
Item {
id: _root
height: singleVehiclePanel ? selectorRow.height : availableHeight
visible: QGroundControl.multiVehicleManager.vehicles.count > 1 && QGroundControl.corePlugin.options.flyView.showMultiVehicleList
property alias singleVehiclePanel: singleVehicleView.checked
property real availableHeight
QGCMapPalette { id: mapPal; lightColors: true }
Row {
id: selectorRow
spacing: ScreenTools.defaultFontPixelWidth
QGCRadioButton {
id: singleVehicleView
text: qsTr("Single")
checked: true
textColor: mapPal.text
}
QGCRadioButton {
text: qsTr("Multi-Vehicle")
textColor: mapPal.text
}
}
MultiVehicleList {
anchors.topMargin: ScreenTools.defaultFontPixelHeight / 2
anchors.top: selectorRow.bottom
anchors.bottom: parent.bottom
width: parent.width
visible: !singleVehiclePanel && !QGroundControl.videoManager.fullScreen && QGroundControl.corePlugin.options.showMultiVehicleList
}
}
This diff is collapsed.
This diff is collapsed.
......@@ -22,15 +22,10 @@ ColumnLayout {
id: root
spacing: ScreenTools.defaultFontPixelHeight / 4
// These properties are expected to be in the Loader
// property real maxHeight
// property bool showValues - true: show value pages
property real _innerRadius: (width - (_topBottomMargin * 3)) / 4
property real _outerRadius: _innerRadius + _topBottomMargin
property real _spacing: ScreenTools.defaultFontPixelHeight * 0.33
property real _topBottomMargin: (width * 0.05) / 2
property real _availableValueHeight: maxHeight - _valuesItem.y
QGCPalette { id: qgcPal }
......@@ -65,26 +60,4 @@ ColumnLayout {
TerrainProgress {
Layout.fillWidth: true
}
Item {
id: _valuesItem
Layout.fillWidth: true
height: _valuesWidget.height
visible: showValues
DeadMouseArea { anchors.fill: parent }
Rectangle {
anchors.fill: _valuesWidget
color: qgcPal.window
}
PageView {
id: _valuesWidget
anchors.margins: 1
anchors.left: parent.left
anchors.right: parent.right
maxHeight: _availableValueHeight
}
}
}
......@@ -27,11 +27,9 @@ Rectangle {
// property real maxHeight
// property bool showValues - true: show value pages
property real _innerRadius: (width - (_topBottomMargin * 2)) / 2
property real _outerRadius: _innerRadius + _topBottomMargin * 2
property real _spacing: ScreenTools.defaultFontPixelHeight * 0.33
property real _topBottomMargin: (width * 0.05) / 2
property real _availableValueHeight: maxHeight - (attitude.height + compass.height)
property real _innerRadius: (width - (_topBottomMargin * 2)) / 2
property real _outerRadius: _innerRadius + _topBottomMargin * 2
property real _margins: (width * 0.05) / 2
// Prevent all clicks from going through to lower layers
DeadMouseArea {
......@@ -41,51 +39,20 @@ Rectangle {
QGCPalette { id: qgcPal }
QGCAttitudeWidget {
id: attitude
anchors.topMargin : _topBottomMargin
anchors.bottomMargin: _topBottomMargin
anchors.top: parent.top
size: _innerRadius * 2
vehicle: activeVehicle
anchors.horizontalCenter: parent.horizontalCenter
}
Item {
id: _valuesItem
anchors.topMargin: ScreenTools.defaultFontPixelHeight / 4
anchors.bottomMargin: ScreenTools.defaultFontPixelHeight / 4
anchors.top: attitude.bottom
anchors.bottom: compass.top
width: parent.width
height: _valuesWidget.height
visible: showValues
// Prevent all clicks from going through to lower layers
DeadMouseArea {
anchors.fill: parent
}
Rectangle {
anchors.fill: _valuesWidget
color: qgcPal.window
}
PageView {
id: _valuesWidget
anchors.margins: 1
anchors.left: parent.left
anchors.right: parent.right
maxHeight: _availableValueHeight
}
id: attitude
anchors.horizontalCenter: parent.horizontalCenter
anchors.margins : _margins
anchors.top: parent.top
size: _innerRadius * 2
vehicle: activeVehicle
}
QGCCompassWidget {
id: compass
anchors.bottom : parent.bottom
anchors.bottomMargin: _topBottomMargin
anchors.topMargin: _topBottomMargin
anchors.horizontalCenter: parent.horizontalCenter
anchors.margins: _margins
anchors.top: attitude.bottom
size: _innerRadius * 2
vehicle: activeVehicle
anchors.horizontalCenter: parent.horizontalCenter
}
}
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
import QtQuick 2.12
import QtQuick.Dialogs 1.3
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.5
import QtQml 2.12
import QGroundControl.Controls 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.FactSystem 1.0
import QGroundControl.FactControls 1.0
import QGroundControl.Controllers 1.0
import QGroundControl.Palette 1.0
import QGroundControl.FlightMap 1.0
import QGroundControl 1.0
/// Value page for InstrumentPanel PageView
VerticalFactValueGrid {
id: _root
width: pageWidth
userSettingsGroup: valuePageUserSettingsGroup
defaultSettingsGroup: valuePageDefaultSettingsGroup
property bool showSettingsIcon: true
property bool showLockIcon: true
function showSettings(settingsUnlocked) {
_root.settingsUnlocked = settingsUnlocked
}
}
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
import QtQuick 2.11
import QtPositioning 5.2
import QtQuick.Layouts 1.2
import QtQuick.Controls 2.4
import QtQuick.Dialogs 1.2
import QtGraphicalEffects 1.0
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Controls 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Vehicle 1.0
import QGroundControl.Controllers 1.0
import QGroundControl.FactSystem 1.0
import QGroundControl.FactControls 1.0
/// Video streaming page for Instrument Panel PageView
Item {
width: pageWidth
height: videoGrid.y + videoGrid.height + _margins
anchors.margins: ScreenTools.defaultFontPixelWidth * 2
anchors.centerIn: parent
property bool _communicationLost: activeVehicle ? activeVehicle.connectionLost : false
property bool _recordingVideo: QGroundControl.videoManager.recording
property bool _decodingVideo: QGroundControl.videoManager.decoding
property bool _streamingEnabled: QGroundControl.settingsManager.videoSettings.streamConfigured
property var _dynamicCameras: activeVehicle ? activeVehicle.dynamicCameras : null
property int _curCameraIndex: _dynamicCameras ? _dynamicCameras.currentCamera : 0
property bool _isCamera: _dynamicCameras ? _dynamicCameras.cameras.count > 0 : false
property var _camera: _isCamera ? (_dynamicCameras.cameras.get(_curCameraIndex) && _dynamicCameras.cameras.get(_curCameraIndex).paramComplete ? _dynamicCameras.cameras.get(_curCameraIndex) : null) : null
property real _margins: ScreenTools.defaultFontPixelWidth / 2
QGCPalette { id:qgcPal; colorGroupEnabled: true }
GridLayout {
id: videoGrid
anchors.margins: _margins
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
columns: 2
columnSpacing: _margins
rowSpacing: ScreenTools.defaultFontPixelHeight
Connections {
// For some reason, the normal signal is not reflected in the control below
target: QGroundControl.settingsManager.videoSettings.streamEnabled
onRawValueChanged: {
enableSwitch.checked = QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue
}
}
// Enable/Disable Video Streaming
QGCLabel {
text: qsTr("Enable")
font.pointSize: ScreenTools.smallFontPointSize
visible: !_camera || !_camera.autoStream
}
QGCSwitch {
id: enableSwitch
visible: !_camera || !_camera.autoStream
enabled: _streamingEnabled
checked: QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue
Layout.alignment: Qt.AlignHCenter
onClicked: {
if(checked) {
QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue = 1
QGroundControl.videoManager.startVideo()
} else {
QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue = 0
QGroundControl.videoManager.stopVideo()
}
}
}
// Grid Lines
QGCLabel {
text: qsTr("Grid Lines")
font.pointSize: ScreenTools.smallFontPointSize
visible: QGroundControl.videoManager.isGStreamer && QGroundControl.settingsManager.videoSettings.gridLines.visible
}
QGCSwitch {
enabled: _streamingEnabled && activeVehicle
checked: QGroundControl.settingsManager.videoSettings.gridLines.rawValue
visible: QGroundControl.videoManager.isGStreamer && QGroundControl.settingsManager.videoSettings.gridLines.visible
Layout.alignment: Qt.AlignHCenter
onClicked: {
if(checked) {
QGroundControl.settingsManager.videoSettings.gridLines.rawValue = 1
} else {
QGroundControl.settingsManager.videoSettings.gridLines.rawValue = 0
}
}
}
//-- Video Fit
QGCLabel {
text: qsTr("Video Fit")
visible: QGroundControl.videoManager.isGStreamer
font.pointSize: ScreenTools.smallFontPointSize
}
FactComboBox {
fact: QGroundControl.settingsManager.videoSettings.videoFit
visible: QGroundControl.videoManager.isGStreamer
indexModel: false
Layout.alignment: Qt.AlignHCenter
}
QGCLabel {
text: qsTr("File Name");
font.pointSize: ScreenTools.smallFontPointSize
visible: QGroundControl.videoManager.isGStreamer
}
QGCTextField {
id: videoFileName
Layout.fillWidth: true
visible: QGroundControl.videoManager.isGStreamer
}
//-- Video Recording
QGCLabel {
text: _recordingVideo ? qsTr("Stop Recording") : qsTr("Record Stream")
font.pointSize: ScreenTools.smallFontPointSize
visible: QGroundControl.videoManager.isGStreamer
}
// Button to start/stop video recording
Item {
anchors.margins: ScreenTools.defaultFontPixelHeight / 2
height: ScreenTools.defaultFontPixelHeight * 2
width: height
Layout.alignment: Qt.AlignHCenter
visible: QGroundControl.videoManager.isGStreamer
Rectangle {
id: recordBtnBackground
anchors.top: parent.top
anchors.bottom: parent.bottom
width: height
radius: _recordingVideo ? 0 : height
color: (_decodingVideo && _streamingEnabled) ? "red" : "gray"
SequentialAnimation on opacity {
running: _recordingVideo
loops: Animation.Infinite
PropertyAnimation { to: 0.5; duration: 500 }
PropertyAnimation { to: 1.0; duration: 500 }
}
}
QGCColoredImage {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: height * 0.625
sourceSize.width: width
source: "/qmlimages/CameraIcon.svg"
visible: recordBtnBackground.visible
fillMode: Image.PreserveAspectFit
color: "white"
}
MouseArea {
anchors.fill: parent
enabled: _decodingVideo && _streamingEnabled
onClicked: {
if (_recordingVideo) {
QGroundControl.videoManager.stopRecording()
// reset blinking animation
recordBtnBackground.opacity = 1
} else {
QGroundControl.videoManager.startRecording(videoFileName.text)
}
}
}
}
QGCLabel {
text: qsTr("Video Streaming Not Configured")
font.pointSize: ScreenTools.smallFontPointSize
visible: !_streamingEnabled
Layout.columnSpan: 2
}
}
}
......@@ -7,12 +7,7 @@
*
****************************************************************************/
/// @file
/// @author Don Gagne <don@thegagnes.com>
#ifndef QGC_LOGGING_CATEGORY_H
#define QGC_LOGGING_CATEGORY_H
#pragma once
#include <QLoggingCategory>
#include <QStringList>
......@@ -73,5 +68,3 @@ class QGCLoggingCategory
public:
QGCLoggingCategory(const char* category) { QGCLoggingCategoryRegister::instance()->registerCategory(category); }
};
#endif
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.2
import QGroundControl 1.0
import QGroundControl.Palette 1.0
import QGroundControl.ScreenTools 1.0
Rectangle {
id: _root
height: pageFlickable.y + pageFlickable.height + _margins
color: qgcPal.window
radius: ScreenTools.defaultFontPixelWidth * 0.5
property real maxHeight ///< Maximum height that should be taken, smaller than this is ok
property real _margins: ScreenTools.defaultFontPixelWidth / 2
property real _pageWidth: _root.width
property var _instrumentPages: QGroundControl.corePlugin.instrumentPages
property bool _settingsUnlocked: false
QGCPalette { id:qgcPal; colorGroupEnabled: parent.enabled }
QGCComboBox {
id: pageCombo
anchors.left: parent.left
anchors.right: parent.right
model: _instrumentPages
textRole: "title"
centeredLabel: true
font.pointSize: ScreenTools.smallFontPointSize
onCurrentIndexChanged: _settingsUnlocked = false
QGCColoredImage {
anchors.leftMargin: _margins
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
source: pageWidgetLoader.item.showLockIcon ? (_settingsUnlocked ? "/res/LockOpen.svg" : "/res/LockClosed.svg") : "/res/gear-black.svg"
mipmap: true
height: parent.height * 0.7
width: height
sourceSize.height: height
color: qgcPal.text
fillMode: Image.PreserveAspectFit
visible: pageWidgetLoader.item ? (pageWidgetLoader.item.showSettingsIcon ? pageWidgetLoader.item.showSettingsIcon : false) : false
QGCMouseArea {
fillItem: parent
onClicked: {
if (pageWidgetLoader.item.showLockIcon) {
_settingsUnlocked = !_settingsUnlocked
pageWidgetLoader.item.showSettings(_settingsUnlocked)
} else {
pageWidgetLoader.item.showSettings()
}
}
}
}
}
QGCFlickable {
id: pageFlickable
anchors.margins: _margins
anchors.top: pageCombo.bottom
anchors.left: parent.left
anchors.right: parent.right
height: Math.min(_maxHeight, pageWidgetLoader.height)
contentHeight: pageWidgetLoader.height
flickableDirection: Flickable.VerticalFlick
clip: true
property real _maxHeight: maxHeight - y - _margins
Loader {
id: pageWidgetLoader
source: _instrumentPages[pageCombo.currentIndex].url
property real pageWidth: parent.width
}
}
}
......@@ -11,7 +11,7 @@ import QtQuick 2.12
Rectangle {
id: _root
border.width: borderColor === undefined ? 0 : 1
border.width: 1
border.color: borderColor
color: "transparent"
......
......@@ -179,7 +179,7 @@ Popup {
Layout.leftMargin: ScreenTools.defaultFontPixelWidth
Layout.fillWidth: true
text: _dialogTitle
height: parent.height
font.pointSize: ScreenTools.mediumFontPointSize
verticalAlignment: Text.AlignVCenter
}
......
......@@ -26,8 +26,8 @@ Slider {
style: SliderStyle {
groove: Item {
anchors.verticalCenter: parent.verticalCenter
implicitWidth: Math.round(ScreenTools.defaultFontPixelHeight * 4.5)
implicitHeight: Math.round(ScreenTools.defaultFontPixelHeight * 0.3)
implicitWidth: Math.round(ScreenTools.defaultFontPixelHeight * 4.5)
implicitHeight: Math.round(ScreenTools.defaultFontPixelHeight * 0.3)
Rectangle {
radius: height / 2
......@@ -66,7 +66,9 @@ Slider {
implicitWidth: _radius * 2
implicitHeight: _radius * 2
radius: _radius
property real _radius: Math.round(ScreenTools.defaultFontPixelHeight * 0.75)
property real _radius: Math.round(_root.implicitHeight / 2)
Label {
text: _root.value.toFixed( _root.maximumValue <= 1 ? 1 : 0)
visible: _root.displayValue
......
......@@ -43,7 +43,6 @@ MissionItemStatus 1.0 MissionItemStatus.qml
ModeSwitchDisplay 1.0 ModeSwitchDisplay.qml
MultiRotorMotorDisplay 1.0 MultiRotorMotorDisplay.qml
OfflineMapButton 1.0 OfflineMapButton.qml
PageView 1.0 PageView.qml
ParameterEditor 1.0 ParameterEditor.qml
ParameterEditorDialog 1.0 ParameterEditorDialog.qml
PIDTuning 1.0 PIDTuning.qml
......
......@@ -23,7 +23,6 @@ GuidedAltitudeSlider 1.0 GuidedAltitudeSlider.qml
GuidedActionTakeoff 1.0 GuidedActionTakeoff.qml
GuidedToolStripAction 1.0 GuidedToolStripAction.qml
MultiVehicleList 1.0 MultiVehicleList.qml
MultiVehiclePanel 1.0 MultiVehiclePanel.qml
PreFlightBatteryCheck 1.0 PreFlightBatteryCheck.qml
PreFlightGPSCheck 1.0 PreFlightGPSCheck.qml
PreFlightRCCheck 1.0 PreFlightRCCheck.qml
......
......@@ -12,6 +12,7 @@ InstrumentSwipeView 1.0 InstrumentSwipeView.qml
MapFitFunctions 1.0 MapFitFunctions.qml
MapLineArrow 1.0 MapLineArrow.qml
MapScale 1.0 MapScale.qml
PhotoVideoControl 1.0 PhotoVideoControl.qml
QGCArtificialHorizon 1.0 QGCArtificialHorizon.qml
QGCAttitudeHUD 1.0 QGCAttitudeHUD.qml
QGCAttitudeWidget 1.0 QGCAttitudeWidget.qml
......
......@@ -149,7 +149,6 @@ Vehicle::Vehicle(LinkInterface* link,
, _telemetryRNoise(0)
, _highLatencyLink(false)
, _receivingAttitudeQuaternion(false)
, _cameras(nullptr)
, _connectionLost(false)
, _connectionLostEnabled(true)
, _initialPlanRequestComplete(false)
......@@ -282,8 +281,8 @@ Vehicle::Vehicle(LinkInterface* link,
connect(&_orbitTelemetryTimer, &QTimer::timeout, this, &Vehicle::_orbitTelemetryTimeout);
// Create camera manager instance
_cameras = _firmwarePlugin->createCameraManager(this);
emit dynamicCamerasChanged();
_cameraManager = _firmwarePlugin->createCameraManager(this);
emit cameraManagerChanged();
// Start csv logger
connect(&_csvLogTimer, &QTimer::timeout, this, &Vehicle::_writeCsvLine);
......@@ -338,7 +337,7 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType,
, _capabilityBits(MAV_PROTOCOL_CAPABILITY_MISSION_FENCE | MAV_PROTOCOL_CAPABILITY_MISSION_RALLY)
, _highLatencyLink(false)
, _receivingAttitudeQuaternion(false)
, _cameras(nullptr)
, _cameraManager(nullptr)
, _connectionLost(false)
, _connectionLostEnabled(true)
, _initialPlanRequestComplete(false)
......@@ -573,13 +572,13 @@ Vehicle::~Vehicle()
void Vehicle::prepareDelete()
{
if(_cameras) {
// because of _cameras QML bindings check for nullptr won't work in the binding pipeline
if(_cameraManager) {
// because of _cameraManager QML bindings check for nullptr won't work in the binding pipeline
// the dangling pointer access will cause a runtime fault
auto tmpCameras = _cameras;
_cameras = nullptr;
auto tmpCameras = _cameraManager;
_cameraManager = nullptr;
delete tmpCameras;
emit dynamicCamerasChanged();
emit cameraManagerChanged();
qApp->processEvents();
}
}
......@@ -3616,17 +3615,6 @@ void Vehicle::setOfflineEditingDefaultComponentId(int defaultComponentId)
}
}
void Vehicle::triggerCamera()
{
sendMavCommand(_defaultComponentId,
MAV_CMD_DO_DIGICAM_CONTROL,
true, // show errors
0.0, 0.0, 0.0, 0.0, // param 1-4 unused
1.0, // trigger camera
0.0, // param 6 unused
1.0); // test shot flag
}
void Vehicle::setVtolInFwdFlight(bool vtolInFwdFlight)
{
if (_vtolInFwdFlight != vtolInFwdFlight) {
......
......@@ -625,7 +625,7 @@ public:
Q_PROPERTY(QVariantList modeIndicators READ modeIndicators NOTIFY modeIndicatorsChanged)
Q_PROPERTY(bool initialPlanRequestComplete READ initialPlanRequestComplete NOTIFY initialPlanRequestCompleteChanged)
Q_PROPERTY(QVariantList staticCameraList READ staticCameraList CONSTANT)
Q_PROPERTY(QGCCameraManager* dynamicCameras READ dynamicCameras NOTIFY dynamicCamerasChanged)
Q_PROPERTY(QGCCameraManager* cameraManager READ cameraManager NOTIFY cameraManagerChanged)
Q_PROPERTY(QString hobbsMeter READ hobbsMeter NOTIFY hobbsMeterChanged)
Q_PROPERTY(bool vtolInFwdFlight READ vtolInFwdFlight WRITE setVtolInFwdFlight NOTIFY vtolInFwdFlightChanged)
Q_PROPERTY(bool highLatencyLink READ highLatencyLink NOTIFY highLatencyLinkChanged)
......@@ -771,7 +771,6 @@ public:
/// Clear Messages
Q_INVOKABLE void clearMessages();
Q_INVOKABLE void triggerCamera();
Q_INVOKABLE void sendPlan(QString planFile);
/// Used to check if running current version is equal or higher than the one being compared.
......@@ -1130,7 +1129,7 @@ public:
bool capabilitiesKnown () const { return _capabilityBitsKnown; }
uint64_t capabilityBits () const { return _capabilityBits; } // Change signalled by capabilityBitsChanged
QGCCameraManager* dynamicCameras () { return _cameras; }
QGCCameraManager* cameraManager () { return _cameraManager; }
QString hobbsMeter ();
/// The vehicle is responsible for making the initial request for the Plan.
......@@ -1190,7 +1189,7 @@ signals:
void defaultHoverSpeedChanged (double hoverSpeed);
void firmwareTypeChanged ();
void vehicleTypeChanged ();
void dynamicCamerasChanged ();
void cameraManagerChanged ();
void hobbsMeterChanged ();
void capabilitiesKnownChanged (bool capabilitiesKnown);
void initialPlanRequestCompleteChanged(bool initialPlanRequestComplete);
......@@ -1457,7 +1456,7 @@ private:
SysStatusSensorInfo _sysStatusSensorInfo;
QGCCameraManager* _cameras;
QGCCameraManager* _cameraManager = nullptr;
QString _prearmError;
QTimer _prearmErrorTimer;
......
......@@ -363,8 +363,8 @@ VideoManager::grabImage(const QString& imageFile)
//-----------------------------------------------------------------------------
double VideoManager::aspectRatio()
{
if(_activeVehicle && _activeVehicle->dynamicCameras()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance();
if(_activeVehicle && _activeVehicle->cameraManager()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->cameraManager()->currentStreamInstance();
if(pInfo) {
qCDebug(VideoManagerLog) << "Primary AR: " << pInfo->aspectRatio();
return pInfo->aspectRatio();
......@@ -377,8 +377,8 @@ double VideoManager::aspectRatio()
//-----------------------------------------------------------------------------
double VideoManager::thermalAspectRatio()
{
if(_activeVehicle && _activeVehicle->dynamicCameras()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->thermalStreamInstance();
if(_activeVehicle && _activeVehicle->cameraManager()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->cameraManager()->thermalStreamInstance();
if(pInfo) {
qCDebug(VideoManagerLog) << "Thermal AR: " << pInfo->aspectRatio();
return pInfo->aspectRatio();
......@@ -390,8 +390,8 @@ double VideoManager::thermalAspectRatio()
//-----------------------------------------------------------------------------
double VideoManager::hfov()
{
if(_activeVehicle && _activeVehicle->dynamicCameras()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance();
if(_activeVehicle && _activeVehicle->cameraManager()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->cameraManager()->currentStreamInstance();
if(pInfo) {
return pInfo->hfov();
}
......@@ -402,8 +402,8 @@ double VideoManager::hfov()
//-----------------------------------------------------------------------------
double VideoManager::thermalHfov()
{
if(_activeVehicle && _activeVehicle->dynamicCameras()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->thermalStreamInstance();
if(_activeVehicle && _activeVehicle->cameraManager()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->cameraManager()->thermalStreamInstance();
if(pInfo) {
return pInfo->aspectRatio();
}
......@@ -415,8 +415,8 @@ double VideoManager::thermalHfov()
bool
VideoManager::hasThermal()
{
if(_activeVehicle && _activeVehicle->dynamicCameras()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->thermalStreamInstance();
if(_activeVehicle && _activeVehicle->cameraManager()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->cameraManager()->thermalStreamInstance();
if(pInfo) {
return true;
}
......@@ -436,8 +436,8 @@ bool
VideoManager::autoStreamConfigured()
{
#if defined(QGC_GST_STREAMING)
if(_activeVehicle && _activeVehicle->dynamicCameras()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance();
if(_activeVehicle && _activeVehicle->cameraManager()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->cameraManager()->currentStreamInstance();
if(pInfo) {
return !pInfo->uri().isEmpty();
}
......@@ -614,8 +614,8 @@ VideoManager::_updateSettings(unsigned id)
//-- Auto discovery
if(_activeVehicle && _activeVehicle->dynamicCameras()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->dynamicCameras()->currentStreamInstance();
if(_activeVehicle && _activeVehicle->cameraManager()) {
QGCVideoStreamInfo* pInfo = _activeVehicle->cameraManager()->currentStreamInstance();
if(pInfo) {
if (id == 0) {
qCDebug(VideoManagerLog) << "Configure primary stream:" << pInfo->uri();
......@@ -646,7 +646,7 @@ VideoManager::_updateSettings(unsigned id)
}
}
else if (id == 1) { //-- Thermal stream (if any)
QGCVideoStreamInfo* pTinfo = _activeVehicle->dynamicCameras()->thermalStreamInstance();
QGCVideoStreamInfo* pTinfo = _activeVehicle->cameraManager()->thermalStreamInstance();
if (pTinfo) {
qCDebug(VideoManagerLog) << "Configure secondary stream:" << pTinfo->uri();
switch(pTinfo->type()) {
......@@ -788,20 +788,20 @@ VideoManager::_setActiveVehicle(Vehicle* vehicle)
{
if(_activeVehicle) {
disconnect(_activeVehicle, &Vehicle::connectionLostChanged, this, &VideoManager::_connectionLostChanged);
if(_activeVehicle->dynamicCameras()) {
QGCCameraControl* pCamera = _activeVehicle->dynamicCameras()->currentCameraInstance();
if(_activeVehicle->cameraManager()) {
QGCCameraControl* pCamera = _activeVehicle->cameraManager()->currentCameraInstance();
if(pCamera) {
pCamera->stopStream();
}
disconnect(_activeVehicle->dynamicCameras(), &QGCCameraManager::streamChanged, this, &VideoManager::_restartAllVideos);
disconnect(_activeVehicle->cameraManager(), &QGCCameraManager::streamChanged, this, &VideoManager::_restartAllVideos);
}
}
_activeVehicle = vehicle;
if(_activeVehicle) {
connect(_activeVehicle, &Vehicle::connectionLostChanged, this, &VideoManager::_connectionLostChanged);
if(_activeVehicle->dynamicCameras()) {
connect(_activeVehicle->dynamicCameras(), &QGCCameraManager::streamChanged, this, &VideoManager::_restartAllVideos);
QGCCameraControl* pCamera = _activeVehicle->dynamicCameras()->currentCameraInstance();
if(_activeVehicle->cameraManager()) {
connect(_activeVehicle->cameraManager(), &QGCCameraManager::streamChanged, this, &VideoManager::_restartAllVideos);
QGCCameraControl* pCamera = _activeVehicle->cameraManager()->currentCameraInstance();
if(pCamera) {
pCamera->resumeStream();
}
......
......@@ -97,14 +97,9 @@ public:
QmlComponentInfo* pQmlTest = nullptr;
#endif
QmlComponentInfo* cameraPageWidgetInfo = nullptr;
QmlComponentInfo* videoPageWidgetInfo = nullptr;
QmlComponentInfo* vibrationPageWidgetInfo = nullptr;
QGCOptions* defaultOptions = nullptr;
QVariantList settingsList;
QVariantList analyzeList;
QVariantList instrumentPageWidgetList;
QmlObjectListModel _emptyCustomMapItems;
};
......@@ -132,79 +127,6 @@ void QGCCorePlugin::setToolbox(QGCToolbox *toolbox)
qmlRegisterUncreatableType<QGCCorePlugin> ("QGroundControl", 1, 0, "QGCCorePlugin", "Reference only");
qmlRegisterUncreatableType<QGCOptions> ("QGroundControl", 1, 0, "QGCOptions", "Reference only");
qmlRegisterUncreatableType<QGCFlyViewOptions> ("QGroundControl", 1, 0, "QGCFlyViewOptions", "Reference only");
//-- Handle Camera and Video Changes
connect(toolbox->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &QGCCorePlugin::_activeVehicleChanged);
}
void QGCCorePlugin::_activeVehicleChanged(Vehicle* activeVehicle)
{
if(activeVehicle != _activeVehicle) {
if(_activeVehicle) {
disconnect(_activeVehicle, &Vehicle::dynamicCamerasChanged, this, &QGCCorePlugin::_dynamicCamerasChanged);
}
if(_dynamicCameras) {
disconnect(_dynamicCameras, &QGCCameraManager::currentCameraChanged, this, &QGCCorePlugin::_currentCameraChanged);
_dynamicCameras = nullptr;
}
_activeVehicle = activeVehicle;
if(_activeVehicle) {
connect(_activeVehicle, &Vehicle::dynamicCamerasChanged, this, &QGCCorePlugin::_dynamicCamerasChanged);
}
}
}
void QGCCorePlugin::_dynamicCamerasChanged()
{
if(_currentCamera) {
disconnect(_currentCamera, &QGCCameraControl::autoStreamChanged, this, &QGCCorePlugin::_autoStreamChanged);
_currentCamera = nullptr;
}
if(_activeVehicle) {
_dynamicCameras = _activeVehicle->dynamicCameras();
if(_dynamicCameras) {
connect(_dynamicCameras, &QGCCameraManager::currentCameraChanged, this, &QGCCorePlugin::_currentCameraChanged);
}
}
}
void QGCCorePlugin::_currentCameraChanged()
{
if(_dynamicCameras) {
QGCCameraControl* cp = _dynamicCameras->currentCameraInstance();
if(_currentCamera) {
disconnect(_currentCamera, &QGCCameraControl::autoStreamChanged, this, &QGCCorePlugin::_autoStreamChanged);
}
if(_currentCamera != cp) {
_currentCamera = cp;
connect(_currentCamera, &QGCCameraControl::autoStreamChanged, this, &QGCCorePlugin::_autoStreamChanged);
}
}
}
void QGCCorePlugin::_autoStreamChanged()
{
_resetInstrumentPages();
emit instrumentPagesChanged();
}
void QGCCorePlugin::_resetInstrumentPages()
{
if(_p->cameraPageWidgetInfo) {
_p->cameraPageWidgetInfo->deleteLater();
_p->cameraPageWidgetInfo = nullptr;
}
#if defined(QGC_GST_STREAMING)
if(_p->videoPageWidgetInfo) {
_p->videoPageWidgetInfo->deleteLater();
_p->videoPageWidgetInfo = nullptr;
}
#endif
if(_p->vibrationPageWidgetInfo) {
_p->vibrationPageWidgetInfo->deleteLater();
_p->vibrationPageWidgetInfo = nullptr;
}
_p->instrumentPageWidgetList.clear();
}
QVariantList &QGCCorePlugin::settingsPages()
......@@ -266,26 +188,6 @@ QVariantList &QGCCorePlugin::settingsPages()
return _p->settingsList;
}
QVariantList& QGCCorePlugin::instrumentPages()
{
if (!_p->cameraPageWidgetInfo) {
_p->cameraPageWidgetInfo = new QmlComponentInfo(tr("Camera"), QUrl::fromUserInput("qrc:/qml/CameraPageWidget.qml"));
#if defined(QGC_GST_STREAMING)
if(!_currentCamera || !_currentCamera->autoStream()) {
//-- Video Page Widget only available if using manual video streaming
_p->videoPageWidgetInfo = new QmlComponentInfo(tr("Video Stream"), QUrl::fromUserInput("qrc:/qml/VideoPageWidget.qml"));
}
#endif
_p->instrumentPageWidgetList.append(QVariant::fromValue(_p->cameraPageWidgetInfo));
#if defined(QGC_GST_STREAMING)
_p->instrumentPageWidgetList.append(QVariant::fromValue(_p->videoPageWidgetInfo));
#endif
_p->instrumentPageWidgetList.append(QVariant::fromValue(_p->vibrationPageWidgetInfo));
}
return _p->instrumentPageWidgetList;
}
QVariantList& QGCCorePlugin::analyzePages()
{
if (!_p->analyzeList.count()) {
......
......@@ -48,7 +48,6 @@ public:
Q_PROPERTY(QVariantList settingsPages READ settingsPages NOTIFY settingsPagesChanged)
Q_PROPERTY(QVariantList analyzePages READ analyzePages NOTIFY analyzePagesChanged)
Q_PROPERTY(QVariantList instrumentPages READ instrumentPages NOTIFY instrumentPagesChanged)
Q_PROPERTY(int defaultSettings READ defaultSettings CONSTANT)
Q_PROPERTY(QGCOptions* options READ options CONSTANT)
Q_PROPERTY(bool showTouchAreas READ showTouchAreas WRITE setShowTouchAreas NOTIFY showTouchAreasChanged)
......@@ -71,10 +70,6 @@ public:
/// @return A list of QmlPageInfo
virtual QVariantList& analyzePages();
/// The list of PageWidget pages shown in the instrument panel
/// @return A list of QmlPageInfo
virtual QVariantList& instrumentPages();
/// The default settings panel to show
/// @return The settings index
virtual int defaultSettings();
......@@ -205,25 +200,15 @@ public:
signals:
void settingsPagesChanged ();
void analyzePagesChanged ();
void instrumentPagesChanged ();
void showTouchAreasChanged (bool showTouchAreas);
void showAdvancedUIChanged (bool showAdvancedUI);
void toolBarIndicatorsChanged ();
protected slots:
void _activeVehicleChanged (Vehicle* activeVehicle);
void _dynamicCamerasChanged ();
void _currentCameraChanged ();
void _autoStreamChanged ();
protected:
void _resetInstrumentPages ();
protected:
bool _showTouchAreas;
bool _showAdvancedUI;
Vehicle* _activeVehicle = nullptr;
QGCCameraManager* _dynamicCameras = nullptr;
QGCCameraManager* _cameraManager = nullptr;
QGCCameraControl* _currentCamera = nullptr;
QVariantList _toolBarIndicatorList;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment